- 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;
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);

View File

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

View File

@ -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;

View File

@ -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)

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 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;

View File

@ -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.