mirror of
https://github.com/ZDoom/qzdoom.git
synced 2024-11-28 06:53:58 +00:00
- 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:
parent
35f0b32a7f
commit
b044c134d3
6 changed files with 129 additions and 25 deletions
|
@ -162,6 +162,41 @@ struct CodePointerAlias
|
|||
};
|
||||
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
|
||||
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<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);
|
||||
|
|
|
@ -230,6 +230,7 @@ struct DehInfo
|
|||
BYTE ExplosionStyle;
|
||||
fixed_t ExplosionAlpha;
|
||||
int NoAutofreeze;
|
||||
int BFGCells;
|
||||
};
|
||||
extern DehInfo deh;
|
||||
EXTERN_CVAR (Int, infighting)
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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)
|
||||
|
||||
|
|
|
@ -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,20 +502,27 @@ 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;
|
||||
}
|
||||
if (!altFire)
|
||||
{
|
||||
if (Ammo1 != NULL)
|
||||
{
|
||||
if (ammouse >= 0 && (WeaponFlags & WIF_DEHAMMO))
|
||||
{
|
||||
Ammo1->Amount -= ammouse;
|
||||
}
|
||||
else
|
||||
{
|
||||
Ammo1->Amount -= AmmoUse1;
|
||||
}
|
||||
}
|
||||
if ((WeaponFlags & WIF_PRIMARY_USES_BOTH) && Ammo2 != NULL)
|
||||
{
|
||||
Ammo2->Amount -= AmmoUse2;
|
||||
|
|
|
@ -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.
|
||||
|
|
Loading…
Reference in a new issue