Randomizer Gamemode

From Valve Developer Community
Jump to navigation Jump to search

This tutorial will showcase how to create a Randomizer gamemode.

The code listed in this tutorial has been tested in Source SDK 2013 Base Multiplayer, but it should work in other versions of the Source SDK without much hassle.

This is more of a copy/paste tutorial, so not a lot of programming knowledge is required. However, reading the tutorial fully will provide a lot of context to what we are doing here.

Concept

What we'll be doing is a very basic implementation of a Randomizer gamemode, inspired by custom server-hosted gamemodes like Team Fortress 2 Randomizer.

What will happen is we will replace GiveAllItems with a more complicated function, GiveItems. This will allow us to update the weapon list for both the impulse 101 command and the randomizer gamemode. If the server host decides to enable the gamemode, we will replace the default items and weapons with a certain number of items from the list. This system will also provide ammo for those specific weapons.

Implementation

hl2mp_player.h (or any mod-equivalent player.h file)

Scroll down to:

void GiveAllItems( void );

and replace that line of text with:

void GiveItems( bool bGiveAll = false );

This will cause errors in hl2mp_player.cpp. Don't worry, we will fix these in a bit.

hl2mp_player.cpp (or any mod-equivalent player.cpp file)

Above

#define HL2MP_COMMAND_MAX_RATE 0.3

at the beginning of the file, add the following lines:

ConVar sv_randomizer( "sv_randomizer", "0", FCVAR_NOTIFY, "" );
ConVar sv_randomizer_weaponcount( "sv_randomizer_weaponcount", "5", FCVAR_NOTIFY, "" );

These ConVars will allow us to turn on the gamemode and set the number of weapons we want given to our player.

Next, scroll down to void CHL2MP_Player::GiveAllItems( void ) and replace the entire function with:

void CHL2MP_Player::GiveItems( bool bGiveAll )
{
	EquipSuit();

	static const char *g_pszAllWeaponNames[] =
	{
		"weapon_stunstick",
		"weapon_crowbar",
		"weapon_pistol",
		"weapon_357",
		"weapon_smg1",
		"weapon_ar2",
		"weapon_shotgun",
		"weapon_frag",
		"weapon_crossbow",
		"weapon_rpg",
		"weapon_slam",
		"weapon_physcannon"
	};

	static const char *g_pszAllAmmoNames[] =
	{
		"AR2",
		"AR2AltFire",
		"Pistol",
		"SMG1",
		"smg1_grenade",
		"Buckshot",
		"357",
		"rpg_round",
		"XBowBolt",
		"grenade",
		"slam"
	};

	if ( bGiveAll )
	{
		// Give us everything.

		int WeaponList = ARRAYSIZE( g_pszAllWeaponNames );
		for ( int i = 0; i < WeaponList; ++i )
		{
			GiveNamedItem( g_pszAllWeaponNames[i] );
		}

		int AmmoList = ARRAYSIZE( g_pszAllAmmoNames );
		for ( int i = 0; i < AmmoList; ++i )
		{
			CBasePlayer::GiveAmmo( 999, g_pszAllAmmoNames[i] );
		}
	}
	else
	{
		// Give us some weapons.
		int nWeapons = ARRAYSIZE( g_pszAllWeaponNames );
		int weaponCount = Clamp( sv_randomizer_weaponcount.GetInt(), 1, nWeapons );
		for ( int i = 0; i < weaponCount; ++i )
		{
			int randomChoice = rand() % nWeapons;
			GiveNamedItem( g_pszAllWeaponNames[randomChoice] );
		}

		// Give us ammo for our new weapons.
		int WeaponList = ARRAYSIZE( g_pszAllWeaponNames );
		for ( int i = 0; i < WeaponList; ++i )
		{
			CBaseCombatWeapon *pCheckWeapon = Weapon_OwnsThisType( g_pszAllWeaponNames[i] );
			if ( pCheckWeapon )
			{
				CBasePlayer::GiveAmmo( RandomInt( 10, 999 ), pCheckWeapon->GetPrimaryAmmoType() );
				CBasePlayer::GiveAmmo( RandomInt( 10, 999 ), pCheckWeapon->GetSecondaryAmmoType() );
			}
		}
	}
}

Let's go over this.

At the beginning, we call EquipSuit();, which gives us the HEV suit and enables the HUD. Simple enough.

Next, we make 2 lists: g_pszAllWeaponNames and g_pszAllAmmoNames. These lists store every weapon and ammo type ever in the game. g_pszAllAmmoNames is not used in the randomizer gamemode, but rather to give us all ammo types when we execute the impulse 101 command. When you add a new weapon or ammo type, make sure to add it to these lists!

Now we get to the meat of the entire system. if the argument bGiveAll is true, it will give us every weapon and ammo type on the lists. If not, it will randomize every weapon listed in g_pszAllWeaponNames, and give a certain number of weapons to us. Afterwards, we give us a randomized amount of ammo for each weapon.

Now, let's change a few things in order to get this system to work. Scroll down to void CHL2MP_Player::Spawn(void) and go to the code block at the beginning that looks like this:

if ( !IsObserver() )
{
	pl.deadflag = false;
	RemoveSolidFlags( FSOLID_NOT_SOLID );

	RemoveEffects( EF_NODRAW );
		
	GiveDefaultItems();
}

and change it to:

if ( !IsObserver() )
{
	pl.deadflag = false;
	RemoveSolidFlags( FSOLID_NOT_SOLID );

	RemoveEffects( EF_NODRAW );
		
	if ( sv_randomizer.GetBool() )
	{
		GiveItems();
	}
	else
	{
		GiveDefaultItems();
	}
}

Whenever we spawn, it will check to see if sv_randomizer is enabled or not. If so, we will give the player random items. If not, we will give the player the normal HL2:DM loadout.

Finally, let's fix the impulse 101 command. Scroll down to void CHL2MP_Player::CheatImpulseCommands( int iImpulse ) and you will see a code block that looks like this:

switch ( iImpulse )
{
	case 101:
		{
			if( sv_cheats->GetBool() )
			{
				GiveAllItems();
			}
		}
		break;

	default:
		BaseClass::CheatImpulseCommands( iImpulse );
}

Replace it with:

switch ( iImpulse )
{
	case 101:
		{
			if( sv_cheats->GetBool() )
			{
				GiveItems( true );
			}
		}
		break;

	default:
		BaseClass::CheatImpulseCommands( iImpulse );
}

This will allow impulse 101 to give us every weapon again, just like old times.

Conclusion

After compiling your code, check to see if it works in-game by starting a server and putting in the command sv_randomizer 1. After you have done that, respawn your player. You should have a completely randomized loadout!

Feel free to mess around with the code to get different results. Maybe you could use a script file for the weapon/ammo names!