Dynamic Weapon Spawns

From Valve Developer Community
Revision as of 15:50, 26 April 2006 by Ts2do (talk | contribs) ({{note}})
Jump to navigation Jump to search
Broom icon.png
This article or section should be converted to third person to conform to wiki standards.

Introduction

A feature that has been in many console FPS games for years is dynamic weapon spawns. The best example was Perfect Dark. You could choose 6 different weapons at the beginning of the round, and then the game would place those weapons at whatever point the weapon was assigned to. Pistols might go in all slot 1 spawns, cloak generators at another.

This sort of thing can add longevity to some maps, as each game you could use a different set of weapons. So, how would one go about adding this to a PC game such as Half-Life 2?

Setting it up

To do this creating a model entity such as CWeaponSpawn seems to work. Create a new cpp file, call it weaponspawns.cpp or something and place it in the game_shared folder, this goes on both the client and server. Add the file to both projects. Let's make the entity, here's an empty shell of an model ent...

#include "cbase.h" 
#ifdef CLIENT_DLL
	#define CWeaponSpawner C_WeaponSpawner 
#endif
class CWeaponSpawner : public CBaseAnimating
{
	DECLARE_CLASS( CWeaponSpawner, CBaseAnimating );
	DECLARE_NETWORKCLASS(); 
	DECLARE_PREDICTABLE();
	DECLARE_DATADESC();

	int m_iWeaponSlot;

public:

	void Spawn( void )
	{
		Precache( );
		
		BaseClass::Spawn( );
		AddEffects( EF_NODRAW );
		SetMoveType( MOVETYPE_NONE );
		SetSolid( SOLID_NONE );
		SetThink( &CWeaponSpawner::Think );
		SetNextThink( gpGlobals->curtime + 3 );
	}
	void Precache( void )
	{
		PrecacheModel ("models/w_rcp120.mdl"); // set a different model, something you have
               //Precache all entities spawned at these, to prevent late caching(ingame stutters suck)
               UTIL_PrecacheOther("weapon_falcon2");
               UTIL_PrecacheOther("weapon_magsec4");
               UTIL_PrecacheOther("weapon_dy357");
               UTIL_PrecacheOther("weapon_dy357lx");
               UTIL_PrecacheOther("weapon_cmp");
               UTIL_PrecacheOther("weapon_cyclone");
               UTIL_PrecacheOther("weapon_laptopgun");
               UTIL_PrecacheOther("weapon_rcp120");
               UTIL_PrecacheOther("weapon_proximitymine");
               UTIL_PrecacheOther("weapon_remotemine");
               UTIL_PrecacheOther("weapon_timedmine");
       }

	void Think( void )
	{
	}
};

IMPLEMENT_NETWORKCLASS_ALIASED( WeaponSpawner, DT_WeaponSpawner )
BEGIN_NETWORK_TABLE( CWeaponSpawner, DT_WeaponSpawner )
END_NETWORK_TABLE()
BEGIN_PREDICTION_DATA( CWeaponSpawner )
END_PREDICTION_DATA()
BEGIN_DATADESC( CWeaponSpawner )
	DEFINE_KEYFIELD( m_iWeaponSlot, FIELD_INTEGER, "ForWeapon" ),
#ifndef CLIENT_DLL
	DEFINE_THINKFUNC( Think ),
#endif
END_DATADESC()
LINK_ENTITY_TO_CLASS(pd_weaponspawner, CWeaponSpawner);
PRECACHE_REGISTER(pd_weaponspawner);

When it comes to letting server admins set an option, nothing beats a CVAR(or ConVar if you prefer). We will use 6 different weapon slots in our set. It's time to declare them, these go at the top of the file.

ConVar	mp_pdweapon1( "mp_pdweapon1", "1", FCVAR_NOTIFY|FCVAR_REPLICATED, "Weapon to create at weapon 1 areas" );
ConVar	mp_pdweapon2( "mp_pdweapon2", "1", FCVAR_NOTIFY|FCVAR_REPLICATED, "Weapon to create at weapon 2 areas" );
ConVar	mp_pdweapon3( "mp_pdweapon3", "1", FCVAR_NOTIFY|FCVAR_REPLICATED, "Weapon to create at weapon 3 areas" );
ConVar	mp_pdweapon4( "mp_pdweapon4", "1", FCVAR_NOTIFY|FCVAR_REPLICATED, "Weapon to create at weapon 4 areas" );
ConVar	mp_pdweapon5( "mp_pdweapon5", "1", FCVAR_NOTIFY|FCVAR_REPLICATED, "Weapon to create at weapon 5 areas" );
ConVar	mp_pdweapon6( "mp_pdweapon6", "1", FCVAR_NOTIFY|FCVAR_REPLICATED, "Weapon to create at weapon 6 areas" );

Now it's possible to let the server admin set an integer value to those CVARs to set weapons. Another way to do it is to set them to strings, like

ConVar	mp_pdweapon1( "mp_pdweapon1", "magsec4", FCVAR_NOTIFY|FCVAR_REPLICATED, "Weapon to create at weapon 1 areas" );

Using that you could check for what weapon goes with magsec4 and spawn it.

This seemed to be an annoyance when it was done for HL1, and how to check what weapon to spawn, a way to do this would check constants, though this would mean if you had to remove or add an weapon in the list you would have to change most of the values. So it's best to avoid this with an enum.

typedef enum
{
	SPAWN_OFF = 0,
	SPAWN_FALCON2,
	SPAWN_DY357,
	SPAWN_SHOTGUN,
	SPAWN_AR34,
	SPAWN_CALLISTO,
	SPAWN_RCP120,
} WeaponsToSpawn;


Getting it to work

Now everything is set up, it is possible to get it to work. The magic happens in the entity's Think Function. It's necessary to check what slot in the set this ent caters for, and what weapon is assigned to that slot. To do that this can be done.

int m_iWeaponSlot;

and

DEFINE_KEYFIELD( m_iWeaponSlot, FIELD_INTEGER, "ForWeapon" ),

It is an key value for a level designer to set, more on that later. We can now check for the weapon like so.

int BoundWeapon = 0;
switch (m_iWeaponSlot)
{
	case 1: 
		BoundWeapon = mp_pdweapon1.GetInt();
		break;
	case 2: 
		BoundWeapon = mp_pdweapon2.GetInt();
		break;
	case 3: 
		BoundWeapon = mp_pdweapon3.GetInt();
		break;
	case 4: 
		BoundWeapon = mp_pdweapon4.GetInt();
		break;
	case 5: 
		BoundWeapon = mp_pdweapon5.GetInt();
		break;
	case 6: 
		BoundWeapon = mp_pdweapon6.GetInt();
		break;
}

Now it comes clear what is supposed to be created. To create an entity this is called.

CBaseEntity::Create( "weapon_falcon2", GetLocalOrigin(), GetLocalAngles() );

So BoundWeapon is checked and spawns the appropriate item.

CBaseEntity * pEntity = NULL;
switch (BoundWeapon)
{
	case SPAWN_FALCON2:
		pEntity = CBaseEntity::Create( "weapon_falcon2", GetLocalOrigin(), GetLocalAngles() );
		break;
	case SPAWN_DY357:
		pEntity = CBaseEntity::Create( "weapon_dy357", GetLocalOrigin(), GetLocalAngles() );
		break;
	case SPAWN_SHOTGUN:
		pEntity = CBaseEntity::Create( "weapon_shotgun", GetLocalOrigin(), GetLocalAngles() );
		break;
	case SPAWN_AR34:
		pEntity = CBaseEntity::Create( "weapon_ar34", GetLocalOrigin(), GetLocalAngles() );
		break;
	case SPAWN_CALLISTO:
		pEntity = CBaseEntity::Create( "weapon_callisto", GetLocalOrigin(), GetLocalAngles() );
		break;	
	case SPAWN_RCP120:
		pEntity = CBaseEntity::Create( "weapon_rcp120", GetLocalOrigin(), GetLocalAngles() );
		break;
}

Also, if you are using the blank MP SDK, you will notice that weapons don't respawn if 'dropped' this seems to include creating an entity like this, if so call this afterwards.

pEntity->RemoveSpawnFlags( SF_NORESPAWN );

Getting it in-game

Ok, so this shiney new entity has been created, how is it going to work in game?

If you don't have your own mod FGD yet it might be time to make one. Create an ordinary text file with the .fgd extension and add this line to it.

@include "hl2mp.fgd"

If you don't use the hl2mp FGD, replace that with whatever fgd you use in your mod. Time to declare the new model ent.

@PointClass base(Angles) studio("models/w_rcp120.mdl") = pd_weaponspawner : "An Perfect Dark weapon spawn point"
[
	ForWeapon(integer)		: "For what weapon slot?" : 1   : "What weapon slot can use this point?(1-6)"
]
Note.pngNote:Set a different model or icon, something you have

Now you can place one in hammer. Set the forweapon value to something between 1 and 6 and compile the map. Place one for each slot so you can see it all working.

Further

This is all well and good, but what if your admin doesn't know about this? What if he or she is a bumbling fool? You should keep in mind the old 1:10 ratio, assume anyone who uses or edits your program is 10x more stupid than you are. To help listen server users work this out you can add it to the server options menu when you create game. Open the settings_default.scr file in a text editor (it's not a windows screensaver) and add the following.

"mp_pdweapon1"
{
	"Weapon 1"
	{
		LIST
		"Disabled"	"0"
		"Falcon 2"	"1"
		"DY357"		"2"
		"Shotgun"	"3"
		"AR34"		"4"
		"Callisto NTG"	"5"
		"RCP-120"	"6"
	}
	{ "6" }
}

Then you should be a sport and add them to the server.cfg file when you distribute the mod, so any admin who goes to edit them might guess what they mean.

//Weapon Spawn settings:
mp_pdweapon1 1 //falcon 2
mp_pdweapon2 2 //magnum
mp_pdweapon3 3 //shotteh
mp_pdweapon4 4 //assault rifle
mp_pdweapon5 5 //alien wafflebat gun
mp_pdweapon6 0 //disabled

Reference

Well, copy-pasting this ent might leave you with a bit of a tangle, heres the complete file

If you have any problems you can leave a message here or you can contact him at #ausbg on GameSurge IRC.

See also