From b044c134d3b3f3b220465923fe184a7c90ef9ffc Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Sat, 7 Apr 2012 11:33:35 +0000 Subject: [PATCH] - fixed ammo usage issues with Dehacked modified weapons that switch attack code pointers. Unless Dehacked specifies an 'ammo use' value for a weapon any Dehacked modified weapon determines ammo use by attack function, like Doom did originally, and not by the weapon's AmmoUse property. This also addresses that the Cells/BFG shot value always modified the BFG itself. - fixed: A_Saw depleted ammo after the attack so it still went through with it, even though it was out of ammo. SVN r3522 (trunk) --- src/d_dehacked.cpp | 91 ++++++++++++++++++++++++++++++++++++-- src/doomstat.h | 1 + src/g_doom/a_doomweaps.cpp | 28 ++++++------ src/g_shared/a_pickups.h | 6 ++- src/g_shared/a_weapons.cpp | 27 ++++++++--- src/thingdef/thingdef.h | 1 + 6 files changed, 129 insertions(+), 25 deletions(-) diff --git a/src/d_dehacked.cpp b/src/d_dehacked.cpp index da0b901a27..0cfe20f1cb 100644 --- a/src/d_dehacked.cpp +++ b/src/d_dehacked.cpp @@ -162,6 +162,41 @@ struct CodePointerAlias }; static TArray MBFCodePointers; +struct AmmoPerAttack +{ + actionf_p func; + int ammocount; +}; + +DECLARE_ACTION(A_Punch) +DECLARE_ACTION(A_FirePistol) +DECLARE_ACTION(A_FireShotgun) +DECLARE_ACTION(A_FireShotgun2) +DECLARE_ACTION(A_FireCGun) +DECLARE_ACTION(A_FireMissile) +DECLARE_ACTION_PARAMS(A_Saw) +DECLARE_ACTION(A_FirePlasma) +DECLARE_ACTION(A_FireBFG) +DECLARE_ACTION(A_FireOldBFG) +DECLARE_ACTION(A_FireRailgun) + +// Default ammo use of the various weapon attacks +static AmmoPerAttack AmmoPerAttacks[] = { + { AF_A_Punch, 0}, + { AF_A_FirePistol, 1}, + { AF_A_FireShotgun, 1}, + { AF_A_FireShotgun2, 2}, + { AF_A_FireCGun, 1}, + { AF_A_FireMissile, 1}, + { AFP_A_Saw, 0}, + { AF_A_FirePlasma, 1}, + { AF_A_FireBFG, -1}, // uses deh.BFGCells + { AF_A_FireOldBFG, 1}, + { AF_A_FireRailgun, 1}, + { NULL, 0} +}; + + // Miscellaneous info that used to be constant DehInfo deh = { @@ -183,6 +218,7 @@ DehInfo deh = 255, // Rocket explosion style, 255=use cvar FRACUNIT*2/3, // Rocket explosion alpha false, // .NoAutofreeze + 40, // BFG cells per shot }; // Doom identified pickup items by their sprites. ZDoom prefers to use their @@ -1589,6 +1625,7 @@ static int PatchWeapon (int weapNum) else if (stricmp (Line1, "Ammo use") == 0 || stricmp (Line1, "Ammo per shot") == 0) { info->AmmoUse1 = val; + info->flags6 |= MF6_INTRYMOVE; // flag the weapon for postprocessing (reuse a flag that can't be set by external means) } else if (stricmp (Line1, "Min ammo") == 0) { @@ -1742,7 +1779,7 @@ static int PatchMisc (int dummy) { if (stricmp (Line1, "BFG Cells/Shot") == 0) { - ((AWeapon*)GetDefaultByName ("BFG9000"))->AmmoUse1 = atoi (Line2); + deh.BFGCells = atoi (Line2); } else if (stricmp (Line1, "Rocket Explosion Style") == 0) { @@ -2500,8 +2537,6 @@ static void UnloadDehSupp () BitNames.ShrinkToFit(); StyleNames.Clear(); StyleNames.ShrinkToFit(); - WeaponNames.Clear(); - WeaponNames.ShrinkToFit(); AmmoNames.Clear(); AmmoNames.ShrinkToFit(); @@ -2794,6 +2829,7 @@ static bool LoadDehSupp () } else if (sc.Compare("WeaponNames")) { + WeaponNames.Clear(); // This won't be cleared by UnloadDEHSupp so we need to do it here explicitly sc.MustGetStringName("{"); while (!sc.CheckString("}")) { @@ -2900,6 +2936,55 @@ void FinishDehPatch () StateMap.ShrinkToFit(); TouchedActors.Clear(); TouchedActors.ShrinkToFit(); + + // Now it gets nasty: We have to fiddle around with the weapons' ammo use info to make Doom's original + // ammo consumption work as intended. + + for(unsigned i = 0; i < WeaponNames.Size(); i++) + { + AWeapon *weap = (AWeapon*)GetDefaultByType(WeaponNames[i]); + bool found = false; + if (weap->flags6 & MF6_INTRYMOVE) + { + // Weapon sets an explicit amount of ammo to use so we won't need any special processing here + weap->flags6 &= ~MF6_INTRYMOVE; + } + else + { + weap->WeaponFlags |= WIF_DEHAMMO; + weap->AmmoUse1 = 0; + // to allow proper checks in CheckAmmo we have to find the first attack pointer in the Fire sequence + // and set its default ammo use as the weapon's AmmoUse1. + + TMap StateVisited; + + FState *state = WeaponNames[i]->ActorInfo->FindState(NAME_Fire); + while (state != NULL) + { + bool *check = StateVisited.CheckKey(state); + if (check != NULL && *check) + { + break; // State has already been checked so we reached a loop + } + StateVisited[state] = true; + for(unsigned j = 0; AmmoPerAttacks[j].func != NULL; j++) + { + if (state->ActionFunc == AmmoPerAttacks[j].func) + { + found = true; + int use = AmmoPerAttacks[j].ammocount; + if (use < 0) use = deh.BFGCells; + weap->AmmoUse1 = use; + break; + } + } + if (found) break; + state = state->GetNextState(); + } + } + } + WeaponNames.Clear(); + WeaponNames.ShrinkToFit(); } void ModifyDropAmount(AInventory *inv, int dropamount); diff --git a/src/doomstat.h b/src/doomstat.h index 0daa8d6b98..2819f1bf0d 100644 --- a/src/doomstat.h +++ b/src/doomstat.h @@ -230,6 +230,7 @@ struct DehInfo BYTE ExplosionStyle; fixed_t ExplosionAlpha; int NoAutofreeze; + int BFGCells; }; extern DehInfo deh; EXTERN_CVAR (Int, infighting) diff --git a/src/g_doom/a_doomweaps.cpp b/src/g_doom/a_doomweaps.cpp index 23496b99b0..05a3bb2327 100644 --- a/src/g_doom/a_doomweaps.cpp +++ b/src/g_doom/a_doomweaps.cpp @@ -36,7 +36,7 @@ DEFINE_ACTION_FUNCTION(AActor, A_Punch) if (self->player != NULL) { AWeapon *weapon = self->player->ReadyWeapon; - if (weapon != NULL) + if (weapon != NULL && !(weapon->WeaponFlags & WIF_DEHAMMO)) { if (!weapon->DepleteAmmo (weapon->bAltFire)) return; @@ -78,7 +78,7 @@ DEFINE_ACTION_FUNCTION(AActor, A_FirePistol) AWeapon *weapon = self->player->ReadyWeapon; if (weapon != NULL) { - if (!weapon->DepleteAmmo (weapon->bAltFire)) + if (!weapon->DepleteAmmo (weapon->bAltFire, true, 1)) return; P_SetPsprite (self->player, ps_flash, weapon->FindState(NAME_Flash)); @@ -144,17 +144,15 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_Saw) angle = self->angle + (pr_saw.Random2() * (Spread_XY / 255)); slope = P_AimLineAttack (self, angle, Range, &linetarget) + (pr_saw.Random2() * (Spread_Z / 255)); - P_LineAttack (self, angle, Range, - slope, damage, - NAME_None, pufftype); - AWeapon *weapon = self->player->ReadyWeapon; - if ((weapon != NULL) && !(Flags & SF_NOUSEAMMO) && !(!linetarget && (Flags & SF_NOUSEAMMOMISS))) + if ((weapon != NULL) && !(Flags & SF_NOUSEAMMO) && !(!linetarget && (Flags & SF_NOUSEAMMOMISS)) && !(weapon->WeaponFlags & WIF_DEHAMMO)) { if (!weapon->DepleteAmmo (weapon->bAltFire)) return; } + P_LineAttack (self, angle, Range, slope, damage, NAME_None, pufftype); + if (!linetarget) { if ((Flags & SF_RANDOMLIGHTMISS) && (pr_saw() > 64)) @@ -224,7 +222,7 @@ DEFINE_ACTION_FUNCTION(AActor, A_FireShotgun) AWeapon *weapon = self->player->ReadyWeapon; if (weapon != NULL) { - if (!weapon->DepleteAmmo (weapon->bAltFire)) + if (!weapon->DepleteAmmo (weapon->bAltFire, true, 1)) return; P_SetPsprite (player, ps_flash, weapon->FindState(NAME_Flash)); } @@ -255,7 +253,7 @@ DEFINE_ACTION_FUNCTION(AActor, A_FireShotgun2) AWeapon *weapon = self->player->ReadyWeapon; if (weapon != NULL) { - if (!weapon->DepleteAmmo (weapon->bAltFire)) + if (!weapon->DepleteAmmo (weapon->bAltFire, true, 2)) return; P_SetPsprite (player, ps_flash, weapon->FindState(NAME_Flash)); } @@ -360,7 +358,7 @@ DEFINE_ACTION_FUNCTION(AActor, A_FireCGun) AWeapon *weapon = player->ReadyWeapon; if (weapon != NULL) { - if (!weapon->DepleteAmmo (weapon->bAltFire)) + if (!weapon->DepleteAmmo (weapon->bAltFire, true, 1)) return; S_Sound (self, CHAN_WEAPON, "weapons/chngun", 1, ATTN_NORM); @@ -401,7 +399,7 @@ DEFINE_ACTION_FUNCTION(AActor, A_FireMissile) AWeapon *weapon = self->player->ReadyWeapon; if (weapon != NULL) { - if (!weapon->DepleteAmmo (weapon->bAltFire)) + if (!weapon->DepleteAmmo (weapon->bAltFire, true, 1)) return; } P_SpawnPlayerMissile (self, PClass::FindClass("Rocket")); @@ -449,7 +447,7 @@ DEFINE_ACTION_FUNCTION(AActor, A_FirePlasma) AWeapon *weapon = self->player->ReadyWeapon; if (weapon != NULL) { - if (!weapon->DepleteAmmo (weapon->bAltFire)) + if (!weapon->DepleteAmmo (weapon->bAltFire, true, 1)) return; FState *flash = weapon->FindState(NAME_Flash); @@ -478,7 +476,7 @@ static void FireRailgun(AActor *self, int RailOffset) AWeapon *weapon = self->player->ReadyWeapon; if (weapon != NULL) { - if (!weapon->DepleteAmmo (weapon->bAltFire)) + if (!weapon->DepleteAmmo (weapon->bAltFire, true, 1)) return; FState *flash = weapon->FindState(NAME_Flash); @@ -530,7 +528,7 @@ DEFINE_ACTION_FUNCTION(AActor, A_FireBFG) AWeapon *weapon = self->player->ReadyWeapon; if (weapon != NULL) { - if (!weapon->DepleteAmmo (weapon->bAltFire)) + if (!weapon->DepleteAmmo (weapon->bAltFire, true, deh.BFGCells)) return; } @@ -623,7 +621,7 @@ DEFINE_ACTION_FUNCTION(AActor, A_FireOldBFG) AWeapon *weapon = self->player->ReadyWeapon; if (weapon != NULL) { - if (!weapon->DepleteAmmo (weapon->bAltFire)) + if (!weapon->DepleteAmmo (weapon->bAltFire, true, 1)) return; } self->player->extralight = 2; diff --git a/src/g_shared/a_pickups.h b/src/g_shared/a_pickups.h index 3d9a853890..176c0a7b5f 100644 --- a/src/g_shared/a_pickups.h +++ b/src/g_shared/a_pickups.h @@ -299,8 +299,8 @@ public: AltFire, EitherFire }; - bool CheckAmmo (int fireMode, bool autoSwitch, bool requireAmmo=false); - bool DepleteAmmo (bool altFire, bool checkEnough=true); + bool CheckAmmo (int fireMode, bool autoSwitch, bool requireAmmo=false, int ammocount = -1); + bool DepleteAmmo (bool altFire, bool checkEnough=true, int ammouse = -1); protected: AAmmo *AddAmmo (AActor *other, const PClass *ammotype, int amount); @@ -326,6 +326,8 @@ enum WIF_STAFF2_KICKBACK = 0x00002000, // the powered-up Heretic staff has special kickback WIF_NOAUTOAIM = 0x00004000, // this weapon never uses autoaim (useful for ballistic projectiles) WIF_MELEEWEAPON = 0x00008000, // melee weapon. Used by bots and monster AI. + WIF_DEHAMMO = 0x00010000, // Uses Doom's original amount of ammo for the respective attack functions so that old DEHACKED patches work as intended. + // AmmoUse1 will be set to the first attack's ammo use so that checking for empty weapons still works WIF_CHEATNOTWEAPON = 0x08000000, // Give cheat considers this not a weapon (used by Sigil) diff --git a/src/g_shared/a_weapons.cpp b/src/g_shared/a_weapons.cpp index f382073319..5673c9f0ee 100644 --- a/src/g_shared/a_weapons.cpp +++ b/src/g_shared/a_weapons.cpp @@ -430,11 +430,12 @@ bool AWeapon::ShouldStay () // //=========================================================================== -bool AWeapon::CheckAmmo (int fireMode, bool autoSwitch, bool requireAmmo) +bool AWeapon::CheckAmmo (int fireMode, bool autoSwitch, bool requireAmmo, int ammocount) { int altFire; int count1, count2; int enough, enoughmask; + int lAmmoUse1; if ((dmflags & DF_INFINITE_AMMO) || (Owner->player->cheats & CF_INFINITEAMMO)) { @@ -457,7 +458,16 @@ bool AWeapon::CheckAmmo (int fireMode, bool autoSwitch, bool requireAmmo) count1 = (Ammo1 != NULL) ? Ammo1->Amount : 0; count2 = (Ammo2 != NULL) ? Ammo2->Amount : 0; - enough = (count1 >= AmmoUse1) | ((count2 >= AmmoUse2) << 1); + if (ammocount >= 0 && (WeaponFlags & WIF_DEHAMMO)) + { + lAmmoUse1 = ammocount; + } + else + { + lAmmoUse1 = AmmoUse1; + } + + enough = (count1 >= lAmmoUse1) | ((count2 >= AmmoUse2) << 1); if (WeaponFlags & (WIF_PRIMARY_USES_BOTH << altFire)) { enoughmask = 3; @@ -492,11 +502,11 @@ bool AWeapon::CheckAmmo (int fireMode, bool autoSwitch, bool requireAmmo) // //=========================================================================== -bool AWeapon::DepleteAmmo (bool altFire, bool checkEnough) +bool AWeapon::DepleteAmmo (bool altFire, bool checkEnough, int ammouse) { if (!((dmflags & DF_INFINITE_AMMO) || (Owner->player->cheats & CF_INFINITEAMMO))) { - if (checkEnough && !CheckAmmo (altFire ? AltFire : PrimaryFire, false)) + if (checkEnough && !CheckAmmo (altFire ? AltFire : PrimaryFire, false, false, ammouse)) { return false; } @@ -504,7 +514,14 @@ bool AWeapon::DepleteAmmo (bool altFire, bool checkEnough) { if (Ammo1 != NULL) { - Ammo1->Amount -= AmmoUse1; + if (ammouse >= 0 && (WeaponFlags & WIF_DEHAMMO)) + { + Ammo1->Amount -= ammouse; + } + else + { + Ammo1->Amount -= AmmoUse1; + } } if ((WeaponFlags & WIF_PRIMARY_USES_BOTH) && Ammo2 != NULL) { diff --git a/src/thingdef/thingdef.h b/src/thingdef/thingdef.h index 2043350edc..2f0656735f 100644 --- a/src/thingdef/thingdef.h +++ b/src/thingdef/thingdef.h @@ -368,6 +368,7 @@ struct StateCallData // Macros to handle action functions. These are here so that I don't have to // change every single use in case the parameters change. #define DECLARE_ACTION(name) void AF_##name(AActor *self, AActor *stateowner, FState *, int, StateCallData *); +#define DECLARE_ACTION_PARAMS(name) void AFP_##name(AActor *self, AActor *stateowner, FState *, int, StateCallData *); // This distinction is here so that CALL_ACTION produces errors when trying to // access a function that requires parameters.