Magazine style reloads: Difference between revisions

From Valve Developer Community
Jump to navigation Jump to search
mNo edit summary
No edit summary
 
(28 intermediate revisions by 18 users not shown)
Line 1: Line 1:
{{lang|Magazine style reloads}}
This will be a short and simple one!
This will be a short and simple one!
Say you want to make each shot and each reload count? In CS you can reload anytime you feel like. Some bullets will be deducted and that is all. However, in real life, you will not start to load the half-empty mag, you pull out a new one, and throw away the used one. OK you don't throw it away but anyway. This system can be found in BF2, and some other games.
Say you want to make each shot and reload count? In CS you can reload anytime you feel like. Some bullets will be deducted and that is all.
So now to the code:
Look up the '''basecombatweapon_shared.cpp''' and go to the ''FinishReload'' procedure. You will mod this part:


if ( UsesClipsForAmmo1() )
However, in real life, you will not start to load the half-empty mag, you pull out a new one, and toss the used one. This system can be found in BF2, and some other games.
{
int primary = min( GetMaxClip1() - m_iClip1, pOwner->GetAmmoCount(m_iPrimaryAmmoType));
m_iClip1 += primary;
pOwner->RemoveAmmo( primary, m_iPrimaryAmmoType);
}


to look like this:
== Creating a Boolean Variable ==
if ( UsesClipsForAmmo1() )
{
'''//'''int primary = min( GetMaxClip1() - m_iClip1, pOwner->GetAmmoCount(m_iPrimaryAmmoType));
'''m_iClip1 = GetMaxClip1();'''
'''pOwner->RemoveAmmo( 1, m_iPrimaryAmmoType);'''
}


This tells the game to fully reload the gun and deduct 1 from the iPrimaryAmmoType. From now on, the iPrimaryAmmoType will contain the number of full clips instead of the number of bullets.
So now to the code; look up the '''basecombatweapon_shared.h''' and search for <code>m_bFiresUnderwater</code> (line 115) to find this code:
<source lang=cpp>
CNetworkVar( int, m_iClip2 ); // number of shots left in the secondary weapon clip, -1 it not used
bool m_bFiresUnderwater; // true if this weapon can fire underwater
</source>


If you step on a weapon with ammo in the clip, the game grants you the ammo in the clip. Unfortunately, in a clip style ammo system, this will grant 30 clips instead of 30 bullets. To correct this find '''BaseCombatCharacter.cpp''' and go to the '''Weapon_EquipAmmoOnly''' function. Change the code:
In-between these two lines you create a variable:
int primaryGiven = (pWeapon->UsesClipsForAmmo1()) ? pWeapon->m_iClip1 : pWeapon->GetPrimaryAmmoCount();
<source lang=cpp highlight=2>
int secondaryGiven = (pWeapon->UsesClipsForAmmo2()) ? pWeapon->m_iClip2 : pWeapon->GetSecondaryAmmoCount();
CNetworkVar( int, m_iClip2 ); // number of shots left in the secondary weapon clip, -1 it not used
int takenPrimary  = GiveAmmo( primaryGiven, pWeapon->m_iPrimaryAmmoType);  
bool m_bMagazineStyleReloads; // true if this weapon reloads by removing magazines (remaining bullets)
int takenSecondary = GiveAmmo( secondaryGiven, pWeapon->m_iSecondaryAmmoType);
bool m_bFiresUnderwater; // true if this weapon can fire underwater
</source>


To read:
int primaryGiven = (pWeapon->UsesClipsForAmmo1()) ? pWeapon->m_iClip1 : pWeapon->GetPrimaryAmmoCount();
int secondaryGiven = (pWeapon->UsesClipsForAmmo2()) ? pWeapon->m_iClip2 : pWeapon->GetSecondaryAmmoCount();
// Don't give 30 clips for picking up a weapon
// with 30 bullets.
if (primaryGiven > 0) primaryGiven = 1;
if (secondaryGiven > 0) secondaryGiven = 1;
int takenPrimary  = GiveAmmo( primaryGiven, pWeapon->m_iPrimaryAmmoType);
int takenSecondary = GiveAmmo( secondaryGiven, pWeapon->m_iSecondaryAmmoType);


Now you will recieve once clip, instead of 30. While this fix has the potential to add some bullets into the game, it is better than nothing. A better fix would require a system where you can save clips without throwing them away.
Now go to '''basecombatweapon_shared.cpp''' and search to <code>m_bFiresUnderwater</code> (lines 2253 and 2300):
<source lang=cpp>
DEFINE_FIELD( m_bFiresUnderwater, FIELD_BOOLEAN ),
</source>


Once again you need to define it like so:
<source lang=cpp highlight=1>
DEFINE_FIELD( m_bMagazineStyleReloads, FIELD_BOOLEAN ),
DEFINE_FIELD( m_bFiresUnderwater, FIELD_BOOLEAN ),
</source>


You will notice, that the game will still grant you 30 or more bullets if you pick up an ammo crate. Now go to the '''items.h''' and mod this part to your liking. You will receive the amount of full clips, you specify here. Whenever you pick up an ammo crate.
#define SIZE_AMMO_PISTOL 12
#define SIZE_AMMO_PISTOL_LARGE 100
#define SIZE_AMMO_SMG1 30
#define SIZE_AMMO_SMG1_LARGE 225
#define SIZE_AMMO_AR2 20
#define SIZE_AMMO_AR2_LARGE 100
#define SIZE_AMMO_RPG_ROUND 1
#define SIZE_AMMO_SMG1_GRENADE 1
#define SIZE_AMMO_BUCKSHOT 20
#define SIZE_AMMO_357 6
#define SIZE_AMMO_357_LARGE 20
#define SIZE_AMMO_CROSSBOW 6
#define SIZE_AMMO_AR2_ALTFIRE 1


You get some ammo at the start. That should be modded as well. Go to the '''hl2mp_player.cpp''' and look up the ''GiveDefaultItems'' procedure. Mod the part below to your liking. You will get the specified amount of clips at start.
Now that you have the variable set up, look into the <code>CBaseCombatWeapon</code> constructor (line 54) where:
CBasePlayer::GiveAmmo( 255, "Pistol");
<source lang=cpp>
CBasePlayer::GiveAmmo( 45, "SMG1");
m_bReloadsSingly = false;
CBasePlayer::GiveAmmo( 1, "grenade" );
</source>
CBasePlayer::GiveAmmo( 6, "Buckshot");
CBasePlayer::GiveAmmo( 6, "357" );


Ever noticed, that the game auto-reloads your gun if you holster it? This is not soo good, if you have this clip system.
And depending on what you want, you either default magazine style reloads to be on or off:
To remove this go to the '''weapon_hl2mpbasehlmpcombatweapon.cpp''' and find the ''ItemHolsterFrame'' procedure. Comment out the
<source lang=cpp highlight=2>
FinishReload();
m_bReloadsSingly = false;
Line and you are good.
m_bMagazineStyleReloads = false;
</source>


Finally, it is not realistic to hold 200 clips. Decrease this value in '''hl2mp_gamerules.cpp'''. At the end of the file there is an ''AmmoDef'' part like the one below.
== The Actual Code ==
def.AddAmmoType("AR2", DMG_BULLET, TRACER_LINE_AND_WHIZ, 0, 0, 60, BULLET_IMPULSE(200, 1225), 0 );
 
The 60 shows the max amount of ammo, the player can hold. If you change it to 5 then you can have maximum 5 reloads.
Now here's the main part of the code. Look at the <code>FinishReload</code> procedure (line 1986) and you will find:
You are now finished!
<source lang=cpp>
It would take more tinkering to have a true clip system, which does not throw away the used clips.
if ( UsesClipsForAmmo1() )
{
int primary = min( GetMaxClip1() - m_iClip1, pOwner->GetAmmoCount(m_iPrimaryAmmoType));
m_iClip1 += primary;
pOwner->RemoveAmmo( primary, m_iPrimaryAmmoType);
}
</source>
 
Change the last two lines to look like this:
<source lang=cpp highlight=5>
if ( UsesClipsForAmmo1() )
{
int primary = min( GetMaxClip1() - m_iClip1, pOwner->GetAmmoCount( m_iPrimaryAmmoType ) );
m_iClip1 += primary;
pOwner->RemoveAmmo( m_bMagazineStyleReloads ? min( pOwner->GetAmmoCount( m_iPrimaryAmmoType,GetMaxClip1() ) : primary, m_iPrimaryAmmoType );
}
</source>
 
All that has happened is that if the flag is turned on: you make the clip full again, and remove a whole clip out from the total ammo count.
To display the number of magazines you just have to divide the total amount of ammo by a full clip size and round up.
 
{{Note|This technique "hacks" a magazine-style reload instead of creating a native implementation, thus the player can still receive individual bullets.}}
 
== Covering Your Ass ==
 
Ever noticed that the game auto-reloads your gun if you holster it? This is not good, especially the way this code is implemented; to remove this go to '''weapon_hl2mpbasehlmpcombatweapon.cpp''' or '''basehlcombatweapon_shared.cpp''' and find the <code>ItemHolsterFrame</code> procedure. Comment out <code>FinishReload();</code>
 
== An Example ==
 
Now an example to turn on magazine-style reloads for any weapon:
<source lang=cpp highlight=11>
CWeaponPistol::CWeaponPistol( void )
{
m_flSoonestPrimaryAttack = gpGlobals->curtime;
m_flAccuracyPenalty = 0.0f;
 
m_fMinRange1 = 24;
m_fMaxRange1 = 1500;
m_fMinRange2 = 24;
m_fMaxRange2 = 200;
 
m_bMagazineStyleReloads = true; // Magazine style reloads
m_bFiresUnderwater = true;
}
</source>
 
== Extension ==
 
If you want to only lose 1 of your total ammo instead of the real amount, change:
<source lang=cpp>
m_iClip1 = m_bMagazineStyleReloads ? GetMaxClip1() : m_iClip1 + primary;
pOwner->RemoveAmmo( m_bMagazineStyleReloads ? GetMaxClip1() : primary, m_iPrimaryAmmoType);
</source>
to:
<source lang=cpp highlight=2>
m_iClip1 = m_bMagazineStyleReloads ? GetMaxClip1() : m_iClip1 + primary;
pOwner->RemoveAmmo( m_bMagazineStyleReloads ? 1 : primary, m_iPrimaryAmmoType );
</source>
 
== The Actual Code (FIX) ==
 
If you use the code above then it will work perfectly except...
If you have a full clip that looks like this (12:6)
And then you shoot to (3:6)
And then you reload it becomes (12:0)
It gives you a full clip back instead of the partially full clip you found.
<source lang=cpp>
if ( UsesClipsForAmmo1() )
{
int primary = min( GetMaxClip1() - m_iClip1, pOwner->GetAmmoCount( m_iPrimaryAmmoType ) );
if ( pOwner->GetAmmoCount( m_iPrimaryAmmoType ) >= GetMaxClip1() )
{
m_iClip1 = m_bMagazineStyleReloads ? GetMaxClip1() : m_iClip1 + primary;
pOwner->RemoveAmmo( m_bMagazineStyleReloads ? GetMaxClip1() : primary, m_iPrimaryAmmoType );
}
else
{
m_iClip1 = pOwner->GetAmmoCount( m_iPrimaryAmmoType );
pOwner->RemoveAmmo( GetMaxClip1(), m_iPrimaryAmmoType );
}
}
</source>
 
[[Category:Weapons programming]]
[[Category:Programming]]
[[Category:Tutorials]]

Latest revision as of 11:20, 24 May 2022

English (en)中文 (zh)Translate (Translate)

This will be a short and simple one! Say you want to make each shot and reload count? In CS you can reload anytime you feel like. Some bullets will be deducted and that is all.

However, in real life, you will not start to load the half-empty mag, you pull out a new one, and toss the used one. This system can be found in BF2, and some other games.

Creating a Boolean Variable

So now to the code; look up the basecombatweapon_shared.h and search for m_bFiresUnderwater (line 115) to find this code:

CNetworkVar( int, m_iClip2 );	// number of shots left in the secondary weapon clip, -1 it not used
bool	m_bFiresUnderwater;	// true if this weapon can fire underwater

In-between these two lines you create a variable:

CNetworkVar( int, m_iClip2 );	// number of shots left in the secondary weapon clip, -1 it not used
bool	m_bMagazineStyleReloads;	// true if this weapon reloads by removing magazines (remaining bullets)
bool	m_bFiresUnderwater;	// true if this weapon can fire underwater


Now go to basecombatweapon_shared.cpp and search to m_bFiresUnderwater (lines 2253 and 2300):

DEFINE_FIELD( m_bFiresUnderwater, FIELD_BOOLEAN ),

Once again you need to define it like so:

DEFINE_FIELD( m_bMagazineStyleReloads, FIELD_BOOLEAN ),
DEFINE_FIELD( m_bFiresUnderwater, FIELD_BOOLEAN ),


Now that you have the variable set up, look into the CBaseCombatWeapon constructor (line 54) where:

m_bReloadsSingly	= false;

And depending on what you want, you either default magazine style reloads to be on or off:

m_bReloadsSingly	= false;
m_bMagazineStyleReloads = false;

The Actual Code

Now here's the main part of the code. Look at the FinishReload procedure (line 1986) and you will find:

if ( UsesClipsForAmmo1() )
{
	int primary = min( GetMaxClip1() - m_iClip1, pOwner->GetAmmoCount(m_iPrimaryAmmoType));	
	m_iClip1 += primary;
	pOwner->RemoveAmmo( primary, m_iPrimaryAmmoType);
}

Change the last two lines to look like this:

if ( UsesClipsForAmmo1() )
{
	int primary = min( GetMaxClip1() - m_iClip1, pOwner->GetAmmoCount( m_iPrimaryAmmoType ) );	
	m_iClip1 += primary;
	pOwner->RemoveAmmo( m_bMagazineStyleReloads ? min( pOwner->GetAmmoCount( m_iPrimaryAmmoType,GetMaxClip1() ) : primary, m_iPrimaryAmmoType );
}

All that has happened is that if the flag is turned on: you make the clip full again, and remove a whole clip out from the total ammo count. To display the number of magazines you just have to divide the total amount of ammo by a full clip size and round up.

Note.pngNote:This technique "hacks" a magazine-style reload instead of creating a native implementation, thus the player can still receive individual bullets.

Covering Your Ass

Ever noticed that the game auto-reloads your gun if you holster it? This is not good, especially the way this code is implemented; to remove this go to weapon_hl2mpbasehlmpcombatweapon.cpp or basehlcombatweapon_shared.cpp and find the ItemHolsterFrame procedure. Comment out FinishReload();

An Example

Now an example to turn on magazine-style reloads for any weapon:

CWeaponPistol::CWeaponPistol( void )
{
	m_flSoonestPrimaryAttack = gpGlobals->curtime;
	m_flAccuracyPenalty = 0.0f;

	m_fMinRange1		= 24;
	m_fMaxRange1		= 1500;
	m_fMinRange2		= 24;
	m_fMaxRange2		= 200;

	m_bMagazineStyleReloads = true; // Magazine style reloads
	m_bFiresUnderwater	= true;
}

Extension

If you want to only lose 1 of your total ammo instead of the real amount, change:

m_iClip1 = m_bMagazineStyleReloads ? GetMaxClip1() : m_iClip1 + primary;
pOwner->RemoveAmmo( m_bMagazineStyleReloads ? GetMaxClip1() : primary, m_iPrimaryAmmoType);

to:

m_iClip1 = m_bMagazineStyleReloads ? GetMaxClip1() : m_iClip1 + primary;
pOwner->RemoveAmmo( m_bMagazineStyleReloads ? 1 : primary, m_iPrimaryAmmoType );

The Actual Code (FIX)

If you use the code above then it will work perfectly except... If you have a full clip that looks like this (12:6) And then you shoot to (3:6) And then you reload it becomes (12:0) It gives you a full clip back instead of the partially full clip you found.

if ( UsesClipsForAmmo1() )
{
	int primary = min( GetMaxClip1() - m_iClip1, pOwner->GetAmmoCount( m_iPrimaryAmmoType ) );	
	if ( pOwner->GetAmmoCount( m_iPrimaryAmmoType ) >= GetMaxClip1() )
	{
		m_iClip1 = m_bMagazineStyleReloads ? GetMaxClip1() : m_iClip1 + primary;
		pOwner->RemoveAmmo( m_bMagazineStyleReloads ? GetMaxClip1() : primary, m_iPrimaryAmmoType );
	}
	else
	{
		m_iClip1 = pOwner->GetAmmoCount( m_iPrimaryAmmoType );
		pOwner->RemoveAmmo( GetMaxClip1(), m_iPrimaryAmmoType );
	}
}