- 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)
This commit is contained in:
Christoph Oelckers 2012-04-07 11:33:35 +00:00
parent 35f0b32a7f
commit b044c134d3
6 changed files with 129 additions and 25 deletions

View file

@ -162,6 +162,41 @@ struct CodePointerAlias
}; };
static TArray<CodePointerAlias> MBFCodePointers; static TArray<CodePointerAlias> 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 // Miscellaneous info that used to be constant
DehInfo deh = DehInfo deh =
{ {
@ -183,6 +218,7 @@ DehInfo deh =
255, // Rocket explosion style, 255=use cvar 255, // Rocket explosion style, 255=use cvar
FRACUNIT*2/3, // Rocket explosion alpha FRACUNIT*2/3, // Rocket explosion alpha
false, // .NoAutofreeze false, // .NoAutofreeze
40, // BFG cells per shot
}; };
// Doom identified pickup items by their sprites. ZDoom prefers to use their // 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) else if (stricmp (Line1, "Ammo use") == 0 || stricmp (Line1, "Ammo per shot") == 0)
{ {
info->AmmoUse1 = val; 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) else if (stricmp (Line1, "Min ammo") == 0)
{ {
@ -1742,7 +1779,7 @@ static int PatchMisc (int dummy)
{ {
if (stricmp (Line1, "BFG Cells/Shot") == 0) 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) else if (stricmp (Line1, "Rocket Explosion Style") == 0)
{ {
@ -2500,8 +2537,6 @@ static void UnloadDehSupp ()
BitNames.ShrinkToFit(); BitNames.ShrinkToFit();
StyleNames.Clear(); StyleNames.Clear();
StyleNames.ShrinkToFit(); StyleNames.ShrinkToFit();
WeaponNames.Clear();
WeaponNames.ShrinkToFit();
AmmoNames.Clear(); AmmoNames.Clear();
AmmoNames.ShrinkToFit(); AmmoNames.ShrinkToFit();
@ -2794,6 +2829,7 @@ static bool LoadDehSupp ()
} }
else if (sc.Compare("WeaponNames")) else if (sc.Compare("WeaponNames"))
{ {
WeaponNames.Clear(); // This won't be cleared by UnloadDEHSupp so we need to do it here explicitly
sc.MustGetStringName("{"); sc.MustGetStringName("{");
while (!sc.CheckString("}")) while (!sc.CheckString("}"))
{ {
@ -2900,6 +2936,55 @@ void FinishDehPatch ()
StateMap.ShrinkToFit(); StateMap.ShrinkToFit();
TouchedActors.Clear(); TouchedActors.Clear();
TouchedActors.ShrinkToFit(); 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<FState*, bool> 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); void ModifyDropAmount(AInventory *inv, int dropamount);

View file

@ -230,6 +230,7 @@ struct DehInfo
BYTE ExplosionStyle; BYTE ExplosionStyle;
fixed_t ExplosionAlpha; fixed_t ExplosionAlpha;
int NoAutofreeze; int NoAutofreeze;
int BFGCells;
}; };
extern DehInfo deh; extern DehInfo deh;
EXTERN_CVAR (Int, infighting) EXTERN_CVAR (Int, infighting)

View file

@ -36,7 +36,7 @@ DEFINE_ACTION_FUNCTION(AActor, A_Punch)
if (self->player != NULL) if (self->player != NULL)
{ {
AWeapon *weapon = self->player->ReadyWeapon; AWeapon *weapon = self->player->ReadyWeapon;
if (weapon != NULL) if (weapon != NULL && !(weapon->WeaponFlags & WIF_DEHAMMO))
{ {
if (!weapon->DepleteAmmo (weapon->bAltFire)) if (!weapon->DepleteAmmo (weapon->bAltFire))
return; return;
@ -78,7 +78,7 @@ DEFINE_ACTION_FUNCTION(AActor, A_FirePistol)
AWeapon *weapon = self->player->ReadyWeapon; AWeapon *weapon = self->player->ReadyWeapon;
if (weapon != NULL) if (weapon != NULL)
{ {
if (!weapon->DepleteAmmo (weapon->bAltFire)) if (!weapon->DepleteAmmo (weapon->bAltFire, true, 1))
return; return;
P_SetPsprite (self->player, ps_flash, weapon->FindState(NAME_Flash)); 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)); angle = self->angle + (pr_saw.Random2() * (Spread_XY / 255));
slope = P_AimLineAttack (self, angle, Range, &linetarget) + (pr_saw.Random2() * (Spread_Z / 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; 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)) if (!weapon->DepleteAmmo (weapon->bAltFire))
return; return;
} }
P_LineAttack (self, angle, Range, slope, damage, NAME_None, pufftype);
if (!linetarget) if (!linetarget)
{ {
if ((Flags & SF_RANDOMLIGHTMISS) && (pr_saw() > 64)) if ((Flags & SF_RANDOMLIGHTMISS) && (pr_saw() > 64))
@ -224,7 +222,7 @@ DEFINE_ACTION_FUNCTION(AActor, A_FireShotgun)
AWeapon *weapon = self->player->ReadyWeapon; AWeapon *weapon = self->player->ReadyWeapon;
if (weapon != NULL) if (weapon != NULL)
{ {
if (!weapon->DepleteAmmo (weapon->bAltFire)) if (!weapon->DepleteAmmo (weapon->bAltFire, true, 1))
return; return;
P_SetPsprite (player, ps_flash, weapon->FindState(NAME_Flash)); P_SetPsprite (player, ps_flash, weapon->FindState(NAME_Flash));
} }
@ -255,7 +253,7 @@ DEFINE_ACTION_FUNCTION(AActor, A_FireShotgun2)
AWeapon *weapon = self->player->ReadyWeapon; AWeapon *weapon = self->player->ReadyWeapon;
if (weapon != NULL) if (weapon != NULL)
{ {
if (!weapon->DepleteAmmo (weapon->bAltFire)) if (!weapon->DepleteAmmo (weapon->bAltFire, true, 2))
return; return;
P_SetPsprite (player, ps_flash, weapon->FindState(NAME_Flash)); P_SetPsprite (player, ps_flash, weapon->FindState(NAME_Flash));
} }
@ -360,7 +358,7 @@ DEFINE_ACTION_FUNCTION(AActor, A_FireCGun)
AWeapon *weapon = player->ReadyWeapon; AWeapon *weapon = player->ReadyWeapon;
if (weapon != NULL) if (weapon != NULL)
{ {
if (!weapon->DepleteAmmo (weapon->bAltFire)) if (!weapon->DepleteAmmo (weapon->bAltFire, true, 1))
return; return;
S_Sound (self, CHAN_WEAPON, "weapons/chngun", 1, ATTN_NORM); 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; AWeapon *weapon = self->player->ReadyWeapon;
if (weapon != NULL) if (weapon != NULL)
{ {
if (!weapon->DepleteAmmo (weapon->bAltFire)) if (!weapon->DepleteAmmo (weapon->bAltFire, true, 1))
return; return;
} }
P_SpawnPlayerMissile (self, PClass::FindClass("Rocket")); P_SpawnPlayerMissile (self, PClass::FindClass("Rocket"));
@ -449,7 +447,7 @@ DEFINE_ACTION_FUNCTION(AActor, A_FirePlasma)
AWeapon *weapon = self->player->ReadyWeapon; AWeapon *weapon = self->player->ReadyWeapon;
if (weapon != NULL) if (weapon != NULL)
{ {
if (!weapon->DepleteAmmo (weapon->bAltFire)) if (!weapon->DepleteAmmo (weapon->bAltFire, true, 1))
return; return;
FState *flash = weapon->FindState(NAME_Flash); FState *flash = weapon->FindState(NAME_Flash);
@ -478,7 +476,7 @@ static void FireRailgun(AActor *self, int RailOffset)
AWeapon *weapon = self->player->ReadyWeapon; AWeapon *weapon = self->player->ReadyWeapon;
if (weapon != NULL) if (weapon != NULL)
{ {
if (!weapon->DepleteAmmo (weapon->bAltFire)) if (!weapon->DepleteAmmo (weapon->bAltFire, true, 1))
return; return;
FState *flash = weapon->FindState(NAME_Flash); FState *flash = weapon->FindState(NAME_Flash);
@ -530,7 +528,7 @@ DEFINE_ACTION_FUNCTION(AActor, A_FireBFG)
AWeapon *weapon = self->player->ReadyWeapon; AWeapon *weapon = self->player->ReadyWeapon;
if (weapon != NULL) if (weapon != NULL)
{ {
if (!weapon->DepleteAmmo (weapon->bAltFire)) if (!weapon->DepleteAmmo (weapon->bAltFire, true, deh.BFGCells))
return; return;
} }
@ -623,7 +621,7 @@ DEFINE_ACTION_FUNCTION(AActor, A_FireOldBFG)
AWeapon *weapon = self->player->ReadyWeapon; AWeapon *weapon = self->player->ReadyWeapon;
if (weapon != NULL) if (weapon != NULL)
{ {
if (!weapon->DepleteAmmo (weapon->bAltFire)) if (!weapon->DepleteAmmo (weapon->bAltFire, true, 1))
return; return;
} }
self->player->extralight = 2; self->player->extralight = 2;

View file

@ -299,8 +299,8 @@ public:
AltFire, AltFire,
EitherFire EitherFire
}; };
bool CheckAmmo (int fireMode, bool autoSwitch, bool requireAmmo=false); bool CheckAmmo (int fireMode, bool autoSwitch, bool requireAmmo=false, int ammocount = -1);
bool DepleteAmmo (bool altFire, bool checkEnough=true); bool DepleteAmmo (bool altFire, bool checkEnough=true, int ammouse = -1);
protected: protected:
AAmmo *AddAmmo (AActor *other, const PClass *ammotype, int amount); 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_STAFF2_KICKBACK = 0x00002000, // the powered-up Heretic staff has special kickback
WIF_NOAUTOAIM = 0x00004000, // this weapon never uses autoaim (useful for ballistic projectiles) WIF_NOAUTOAIM = 0x00004000, // this weapon never uses autoaim (useful for ballistic projectiles)
WIF_MELEEWEAPON = 0x00008000, // melee weapon. Used by bots and monster AI. 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) WIF_CHEATNOTWEAPON = 0x08000000, // Give cheat considers this not a weapon (used by Sigil)

View file

@ -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 altFire;
int count1, count2; int count1, count2;
int enough, enoughmask; int enough, enoughmask;
int lAmmoUse1;
if ((dmflags & DF_INFINITE_AMMO) || (Owner->player->cheats & CF_INFINITEAMMO)) 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; count1 = (Ammo1 != NULL) ? Ammo1->Amount : 0;
count2 = (Ammo2 != NULL) ? Ammo2->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)) if (WeaponFlags & (WIF_PRIMARY_USES_BOTH << altFire))
{ {
enoughmask = 3; 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 (!((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; return false;
} }
@ -504,7 +514,14 @@ bool AWeapon::DepleteAmmo (bool altFire, bool checkEnough)
{ {
if (Ammo1 != NULL) 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) if ((WeaponFlags & WIF_PRIMARY_USES_BOTH) && Ammo2 != NULL)
{ {

View file

@ -368,6 +368,7 @@ struct StateCallData
// Macros to handle action functions. These are here so that I don't have to // Macros to handle action functions. These are here so that I don't have to
// change every single use in case the parameters change. // 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(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 // This distinction is here so that CALL_ACTION produces errors when trying to
// access a function that requires parameters. // access a function that requires parameters.