Dropping empty magazines (GoldSrc): Difference between revisions

From Valve Developer Community
Jump to navigation Jump to search
(pov cleanup)
m (for weapons featured in various Valve games)
 
(7 intermediate revisions by 4 users not shown)
Line 1: Line 1:
== Introduction ==
A small visual effect can add a lot of polish to your game. One such effect is actually dropping your empty clips/magazines when you reload a suitable gun. If you time it right, players will see other players drop the empty clip when the player animation does so. For this tutorial, it is recommended that you use a blank multiplayer SDK, for it will yield greater results in the long run.  
A small visual effect can add a lot of polish to your game. One such effect is actually dropping your empty clips/magazines when you reload a suitable gun. If you time it right, players will see other players drop the empty clip when the player animation does so. For this tutorial, it is recommended that you use a blank multiplayer SDK, for it will yield greater results in the long run.  


{{note|<code>...</code> amidst the code means that there are other, unrelated lines there.}}
{{note|<code>...</code> amidst the code means that there are other, unrelated lines there.}}


== Ejecting the clip ==
= Ejecting the clip =


To do this in [[Half-Life]] (The Specialists apparently did something similar for their clip dropping), we will use the <code>EjectBrass</code> function but with the clip model as the model instead of a bullet. <code>EjectBrass</code> in [[Source]] is a little different, taking a model index as the argument. For this purpose, we have to create our own function.
To do this in [[Half-Life]] (The Specialists apparently did something similar for their clip dropping), we will use the <code>EjectBrass</code> function but with the clip model as the model instead of a bullet. <code>EjectBrass</code> in [[Source]] is a little different, taking a model index as the argument. For this purpose, we have to create our own function.


Add the following to <code>c_te_legacytempents.cpp</code>.
Add the following to '''c_te_legacytempents.cpp''':
<source lang=cpp>
void CTempEnts::EjectClip( const Vector &pos1, const QAngle &angles, const QAngle &gunAngles, model_t *pModel )
{
if ( pModel == NULL )
return;
 
C_LocalTempEntity *pTemp = TempEntAlloc( pos1, (model_t *)pModel );
 
if ( pTemp == NULL )
return;
 
pTemp->m_nBody = 0;
 
pTemp->flags |= ( FTENT_COLLIDEWORLD | FTENT_FADEOUT | FTENT_GRAVITY | FTENT_ROTATE );
 
pTemp->m_vecTempEntAngVelocity[0] = random->RandomFloat( -1024, 1024 );
pTemp->m_vecTempEntAngVelocity[1] = random->RandomFloat( -1024, 1024 );
pTemp->m_vecTempEntAngVelocity[2] = random->RandomFloat( -1024, 1024 );
 
// Face forward
pTemp->SetAbsAngles( gunAngles );


void CTempEnts::EjectClip( const Vector &pos1, const QAngle &angles, const QAngle &gunAngles, model_t *pModel )
pTemp->SetRenderMode( kRenderNormal );
{
pTemp->tempent_renderamt = 255; // Set this for fadeout
if ( pModel == NULL )
return;
C_LocalTempEntity *pTemp = TempEntAlloc( pos1, ( model_t * ) pModel );
if ( pTemp == NULL )
return;
pTemp->m_nBody = 0;
pTemp->flags |= ( FTENT_COLLIDEWORLD | FTENT_FADEOUT | FTENT_GRAVITY | FTENT_ROTATE );
pTemp->m_vecTempEntAngVelocity[0] = random->RandomFloat(-1024,1024);
pTemp->m_vecTempEntAngVelocity[1] = random->RandomFloat(-1024,1024);
pTemp->m_vecTempEntAngVelocity[2] = random->RandomFloat(-1024,1024);
//Face forward
pTemp->SetAbsAngles( gunAngles );
pTemp->SetRenderMode( kRenderNormal );
pTemp->tempent_renderamt = 255; // Set this for fadeout
Vector dir;
AngleVectors( angles, &dir );
dir *= random->RandomFloat( 150.0f, 200.0f );
pTemp->SetVelocity( Vector(dir[0] + random->RandomFloat(-64,64),
dir[1] + random->RandomFloat(-64,64),
dir[2] + random->RandomFloat(  0,64) ) );
pTemp->die = gpGlobals->curtime + 20;
}


Make sure you add the prototype to the <code>CTempEnts</code> class, found in <code>c_te_legacytempents.h</code>.
Vector dir;


class CTempEnts : public ITempEnts
AngleVectors( angles, &dir );
{
...
virtual void EjectClip( const Vector &pos1, const QAngle &angles, const QAngle &gunAngles, model_t *pModel );
...
};


Then, add it to <code>ITempEnts</code>, found in the same file.
dir *= random->RandomFloat( 150.0f, 200.0f );


class ITempEnts
pTemp->SetVelocity( Vector(dir[0] + random->RandomFloat( -64, 64 ),
{
dir[1] + random->RandomFloat( -64, 64 ),
...
dir[2] + random->RandomFloat( 0,64 ) ) );
virtual void EjectClip( const Vector &pos1, const QAngle &angles, const QAngle &gunAngles, model_t *pModel ) = 0;
  ...
};


== Reloading ==
pTemp->die = gpGlobals->curtime + 20;
}
</source>


Now, this will have to be added to one of your guns. Find one that will suit the effect, such as a pistol. In this tutorial, we will use the Falcon 2 pistol from [[User:Draco|Draco]]'s mod.
Make sure you add the prototype to the <code>CTempEnts</code> class, found in '''c_te_legacytempents.h''':
<source lang=cpp>
class CTempEnts : public ITempEnts
{
...
virtual void EjectClip( const Vector &pos1, const QAngle &angles, const QAngle &gunAngles, model_t *pModel );
...
};
</source>


bool CWeaponFalcon2::Reload()
Then, add it to <code>ITempEnts</code>, found in the same file:
{
<source lang=cpp>
...
class ITempEnts
#ifdef CLIENT_DLL
{
model_t *pModel = (model_t *)engine->LoadModel( pWeaponInfo.szClipModel );
...
tempents->EjectClip( pPlayer->GetLocalOrigin(), pPlayer->GetLocalAngles(), pPlayer->GetAbsAngles(), pModel );
virtual void EjectClip( const Vector &pos1, const QAngle &angles, const QAngle &gunAngles, model_t *pModel ) = 0;
#endif
...
...
};
}
</source>
 
= Reloading =
 
Now, this will have to be added to one of your guns. Find one that will suit the effect, such as a pistol. In this tutorial, we will use the Falcon 2 pistol from [[User:Draco|Draco]]'s mod:
<source lang=cpp>
bool CWeaponFalcon2::Reload()
{
...
#ifdef CLIENT_DLL
model_t *pModel = (model_t *)engine->LoadModel( pWeaponInfo.szClipModel );
tempents->EjectClip( pPlayer->GetLocalOrigin(), pPlayer->GetLocalAngles(), pPlayer->GetAbsAngles(), pModel );
#endif
...
}
</source>


As you can see this is all on the client. First you load a model into a model info pointer for use as the clip model. The weapon scripts are used to specify the path to model, as is demonstrated shortly.
As you can see this is all on the client. First you load a model into a model info pointer for use as the clip model. The weapon scripts are used to specify the path to model, as is demonstrated shortly.
Line 84: Line 86:
That will spawn a clip model that bounces, collides, falls and stays alive for 20 seconds.  
That will spawn a clip model that bounces, collides, falls and stays alive for 20 seconds.  


== Loading the model with the weapon script ==
= Loading the model with the weapon script =


Another thing Source has over HL1 is the weapon script parser. We shall add another token to the scripts that you can use to set a model for <code>EjectClip</code>.
Another thing Source has over HL1 is the weapon script parser. We shall add another token to the scripts that you can use to set a model for <code>EjectClip</code>.


Find the parser files. In the blank sdk it is called <code>sdk_weapon_parse</code>. Open <code>sdk_weapon_parse.h</code> (or equivalent for your sdk) and add the following to <code>CSDKWeaponInfo</code>:
Find the parser files. In the blank SDK it is called '''sdk_weapon_parse.h/cpp'''. Open '''sdk_weapon_parse.h''' (or equivalent for your SDK) and add the following to <code>CSDKWeaponInfo</code>:
 
<source lang=cpp>
class CSDKWeaponInfo : public FileWeaponInfo_t
class CSDKWeaponInfo : public FileWeaponInfo_t
{
{
...
...
char szClipModel[MAX_WEAPON_STRING]; // Clip model of this weapon
char szClipModel[MAX_WEAPON_STRING]; // Clip model of this weapon
...
...
};
};
 
</source>
Then, open <code>sdk_weapon_parse.cpp</code> and add this:


void CSDKWeaponInfo::Parse( KeyValues *pKeyValuesData, const char *szWeaponName )
Then, open '''sdk_weapon_parse.cpp''' and add this:
{
<source lang=cpp>
...
void CSDKWeaponInfo::Parse( KeyValues *pKeyValuesData, const char *szWeaponName )
Q_strncpy( szClipModel, pKeyValuesData->GetString( "clipmodel" ), MAX_WEAPON_STRING );
{
...
...
}
V_strncpy( szClipModel, pKeyValuesData->GetString( "clipmodel" ), MAX_WEAPON_STRING );
...
}
</source>


Now you can simply find the model path in the code by accessing <code>szClipModel</code>, like so:
Now you can simply find the model path in the code by accessing <code>szClipModel</code>, like so:
<source lang=cpp>
const CSDKWeaponInfo &pWeaponInfo = GetSDKWpnData();
model_t *pModel = (model_t *)engine->LoadModel( pWeaponInfo.szClipModel );
</source>


const CSDKWeaponInfo &pWeaponInfo = GetSDKWpnData();
It is easy to make new tokens for anything in your weapon script.
model_t *pModel = (model_t *)engine->LoadModel( pWeaponInfo.szClipModel );


It is easy to make new tokens for anything in your weapon script.
Now let's add that to the script itself. Find your weapons script file. In this tutorial, it's '''PerfectDark/scripts/weapon_falcon2.txt''':
<source lang=ini>
// Weapon data is loaded by both the Game and Client DLLs.
"printname"                    "#PD_FALCON2"
"viewmodel"                    "models/v_falcon2.mdl"
"playermodel"                  "models/w_falcon2.mdl"
"clipmodel"                    "models/w_falconclip.mdl"
"PlayerAnimationExtension"      "Pistol"
</source>


Now let's add that to the script itself. Find your weapons script file. In this tutorial, it's <code>PerfectDark/scripts/weapon_falcon2.cpp</code>.
If you have any problems you can PM the original author at [[User_talk:Draco|his talk page]] or you can attempt to contact him on IRC at <code>#ausbg</code> on GameSurge.


// Weapon data is loaded by both the Game and Client DLLs.
{{DISPLAYTITLE:Dropping empty magazines}}
"printname" "#PD_FALCON2"
"viewmodel" "models/v_falcon2.mdl"
"playermodel" "models/w_falcon2.mdl"
"clipmodel" "models/w_falconclip.mdl"
"PlayerAnimationExtension" "Pistol"


If you have any problems you can PM the original author at [[User_talk:Draco|his talk page]] or you can attempt to contact him on IRC at <code>#ausbg</code> on GameSurge.
[[Category:GoldSrc]]


[[Category:Tutorials]]
[[Category:Tutorials]]
[[Category:Programming]]

Latest revision as of 04:05, 11 April 2023

A small visual effect can add a lot of polish to your game. One such effect is actually dropping your empty clips/magazines when you reload a suitable gun. If you time it right, players will see other players drop the empty clip when the player animation does so. For this tutorial, it is recommended that you use a blank multiplayer SDK, for it will yield greater results in the long run.

Note.pngNote:... amidst the code means that there are other, unrelated lines there.

Ejecting the clip

To do this in Half-Life (The Specialists apparently did something similar for their clip dropping), we will use the EjectBrass function but with the clip model as the model instead of a bullet. EjectBrass in Source is a little different, taking a model index as the argument. For this purpose, we have to create our own function.

Add the following to c_te_legacytempents.cpp:

void CTempEnts::EjectClip( const Vector &pos1, const QAngle &angles, const QAngle &gunAngles, model_t *pModel )
{
	if ( pModel == NULL )
		return;

	C_LocalTempEntity *pTemp = TempEntAlloc( pos1, (model_t *)pModel );

	if ( pTemp == NULL )
		return;

	pTemp->m_nBody	= 0;

	pTemp->flags |= ( FTENT_COLLIDEWORLD | FTENT_FADEOUT | FTENT_GRAVITY | FTENT_ROTATE );

	pTemp->m_vecTempEntAngVelocity[0] = random->RandomFloat( -1024, 1024 );
	pTemp->m_vecTempEntAngVelocity[1] = random->RandomFloat( -1024, 1024 );
	pTemp->m_vecTempEntAngVelocity[2] = random->RandomFloat( -1024, 1024 );

	// Face forward
	pTemp->SetAbsAngles( gunAngles );

	pTemp->SetRenderMode( kRenderNormal );
	pTemp->tempent_renderamt = 255; // Set this for fadeout

	Vector dir;

	AngleVectors( angles, &dir );

	dir *= random->RandomFloat( 150.0f, 200.0f );

	pTemp->SetVelocity( Vector(dir[0] + random->RandomFloat( -64, 64 ),
						dir[1] + random->RandomFloat( -64, 64 ),
						dir[2] + random->RandomFloat(  0,64 ) ) );

	pTemp->die = gpGlobals->curtime + 20;
}

Make sure you add the prototype to the CTempEnts class, found in c_te_legacytempents.h:

class CTempEnts : public ITempEnts
{
	...
	virtual void EjectClip( const Vector &pos1, const QAngle &angles, const QAngle &gunAngles, model_t *pModel );
	...
};

Then, add it to ITempEnts, found in the same file:

class ITempEnts
{
	...
	virtual void EjectClip( const Vector &pos1, const QAngle &angles, const QAngle &gunAngles, model_t *pModel ) = 0;
	...
};

Reloading

Now, this will have to be added to one of your guns. Find one that will suit the effect, such as a pistol. In this tutorial, we will use the Falcon 2 pistol from Draco's mod:

bool CWeaponFalcon2::Reload()
{
	...
#ifdef CLIENT_DLL
	model_t *pModel = (model_t *)engine->LoadModel( pWeaponInfo.szClipModel );
	tempents->EjectClip( pPlayer->GetLocalOrigin(), pPlayer->GetLocalAngles(), pPlayer->GetAbsAngles(), pModel );
#endif
	...
}

As you can see this is all on the client. First you load a model into a model info pointer for use as the clip model. The weapon scripts are used to specify the path to model, as is demonstrated shortly.

That will spawn a clip model that bounces, collides, falls and stays alive for 20 seconds.

Loading the model with the weapon script

Another thing Source has over HL1 is the weapon script parser. We shall add another token to the scripts that you can use to set a model for EjectClip.

Find the parser files. In the blank SDK it is called sdk_weapon_parse.h/cpp. Open sdk_weapon_parse.h (or equivalent for your SDK) and add the following to CSDKWeaponInfo:

class CSDKWeaponInfo : public FileWeaponInfo_t
{
	...
	char	szClipModel[MAX_WEAPON_STRING]; // Clip model of this weapon
	...
};

Then, open sdk_weapon_parse.cpp and add this:

void CSDKWeaponInfo::Parse( KeyValues *pKeyValuesData, const char *szWeaponName )
{
	...
	V_strncpy( szClipModel, pKeyValuesData->GetString( "clipmodel" ), MAX_WEAPON_STRING );
	...
}

Now you can simply find the model path in the code by accessing szClipModel, like so:

const CSDKWeaponInfo &pWeaponInfo = GetSDKWpnData();
model_t *pModel = (model_t *)engine->LoadModel( pWeaponInfo.szClipModel );

It is easy to make new tokens for anything in your weapon script.

Now let's add that to the script itself. Find your weapons script file. In this tutorial, it's PerfectDark/scripts/weapon_falcon2.txt:

// Weapon data is loaded by both the Game and Client DLLs.
"printname"                     "#PD_FALCON2"
"viewmodel"                     "models/v_falcon2.mdl"
"playermodel"                   "models/w_falcon2.mdl"
"clipmodel"                     "models/w_falconclip.mdl"
"PlayerAnimationExtension"      "Pistol"

If you have any problems you can PM the original author at his talk page or you can attempt to contact him on IRC at #ausbg on GameSurge.