Dynamic Weapon Spawns: Difference between revisions

From Valve Developer Community
Jump to navigation Jump to search
(Typo)
No edit summary
 
(26 intermediate revisions by 8 users not shown)
Line 1: Line 1:
== Introduction ==
{{Abstract Coding}}
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 begining 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?
For years, many console FPS games have been using a great feature called dynamic weapon spawn. The best example to illustrate that feature is the Perfect Dark game. Indeed, you can choose up to six different weapons at the beginning of the game, then the game will put those weapons at whatever point the weapon was assigned to. Pistols should go in all slot 1 spawns, cloak generators at another, and so on.
 
This kind of feature might add some longevity to your map because each game would let you select different sets of weapons. This article describes how one would go about adding this to a PC game such as [[Half-Life 2]].


== Setting it up ==
== Setting it up ==


To do this I used a model entity, CWeaponSpawn. 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...
Creating a model entity called CWeaponSpawn seems to work.  
 
Thus, create a new file called '''weaponspawns.cpp''' or something and place it in the '''\game_shared''' folder so the file will be shared by both the client and the server dlls. Then, add the file to both projects in the Visual C++ editor.  
 
OK, now let's create the entity in the code, here's an empty shell of a model entity:


  #include "cbase.h"  
  #include "cbase.h"  
Line 36: Line 41:
  void Precache( void )
  void Precache( void )
  {
  {
  PrecacheModel ("models/w_rcp120.mdl"); / set a different model, something you have
  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 )
  void Think( void )
Line 58: Line 75:
  PRECACHE_REGISTER(pd_weaponspawner);
  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. Let's declare them, these go at the top of the file.
Whenever a server host / adminstrator has to set up options for their server, the best idea is to use Console Variables ( CVARs ). For that tutorial, we will use six different CVARs to implement our dynamic weapons system.
To make it tight, copy that piece of code at the very bottom of the file.


  ConVar mp_pdweapon1( "mp_pdweapon1", "1", FCVAR_NOTIFY|FCVAR_REPLICATED, "Weapon to create at weapon 1 areas" );
  ConVar mp_pdweapon1( "mp_pdweapon1", "1", FCVAR_NOTIFY|FCVAR_REPLICATED, "Weapon to create at weapon 1 areas" );
Line 67: Line 85:
  ConVar mp_pdweapon6( "mp_pdweapon6", "1", FCVAR_NOTIFY|FCVAR_REPLICATED, "Weapon to create at weapon 6 areas" );
  ConVar mp_pdweapon6( "mp_pdweapon6", "1", FCVAR_NOTIFY|FCVAR_REPLICATED, "Weapon to create at weapon 6 areas" );


Now we can 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
Now this is possible to let the server host set an integer value to those CVARs to set weapons. Another way to do it is to set them to strings. Thus the CVar would look like this:


  ConVar mp_pdweapon1( "mp_pdweapon1", "magsec4", FCVAR_NOTIFY|FCVAR_REPLICATED, "Weapon to create at weapon 1 areas" );
  ConVar mp_pdweapon1( "mp_pdweapon1", "magsec4", FCVAR_NOTIFY|FCVAR_REPLICATED, "Weapon to create at weapon 1 areas" );
Line 73: Line 91:
Using that you could check for what weapon goes with magsec4 and spawn it.
Using that you could check for what weapon goes with magsec4 and spawn it.


Something that annoyed me when I did this for HL1 was how I checked what weapon to spawn, I would check constants, this would mean if I had to remove or add an weapon in the list I had to change most of the values. So we will avoid this with an enum.
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
  typedef enum
Line 89: Line 107:
== Getting it to work ==
== Getting it to work ==


Now that we have everything set up we can get this to work. The magic happens in our entities Think Function. We need to check what slot in the set this ent caters for, and what weapon is assigned to that slot. To do that we have this.
Now everything is set up, it is possible to get it to work. The key resides in the entity 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;
  int m_iWeaponSlot;
Line 122: Line 140:
  }
  }


We now know what we are supposed to create. To create an entity we call this.
Now it comes clear what is supposed to be created. To create an entity this is called.


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


So we check BoundWeapon and spawn the appropriate item.
So BoundWeapon is checked and spawns the appropriate item.


  CBaseEntity * pEntity = NULL;
  CBaseEntity * pEntity = NULL;
Line 151: Line 169:
  }
  }


Also, if you are using the blank MP sdk like I am, you will notice that weapon's dont respawn if 'dropped' this seems to include createing an entity like this, if so call this afterwards.
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 );
  pEntity->RemoveSpawnFlags( SF_NORESPAWN );
Line 157: Line 175:
== Getting it in-game ==
== Getting it in-game ==


Ok, so we now have our shiny new entities, how do we get them to work?
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.
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.
Line 163: Line 181:
  @include "hl2mp.fgd"
  @include "hl2mp.fgd"


if you don't use the hl2mp FGD, replace that with whatever fgd you use in your mod. Time to declare our new model ent.
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"
  @PointClass base(Angles) studio("models/w_rcp120.mdl") = pd_weaponspawner : "An Perfect Dark weapon spawn point"
Line 170: Line 188:
  ]
  ]


(note: 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.
{{note|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 ==
== 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.scr file in a text editor(it's not a windows screensaver) and add 6 of these to it.
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"
  "mp_pdweapon1"
Line 181: Line 201:
  {
  {
  LIST
  LIST
  "Disable Spawner" "0"
  "Disabled" "0"
  "Falcon 2" "1"
  "Falcon 2" "1"
  "DY357" "2"
  "DY357" "2"
  "Shotgun" "3"
  "Shotgun" "3"
  "AR34" "4"
  "AR34" "4"
  "Callisto NTG" "5"
  "Callisto NTG" "5"
  "RCP-120" "6"
  "RCP-120" "6"
  }
  }
  { "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 if they don't read the manual you are going to write or I'll kill you ;)
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:
  //Weapon Spawn settings:
Line 204: Line 224:
== Reference ==
== Reference ==


Well, copy-pasting my ent might leave you with a bit of a tangle, [http://draco.dajoob.com/files/pdspawners.cpp heres my complete file]
Since copy-pasting this whole entity might leave you with a bit of a tangle, [http://draco.dajoob.com/files/pdspawners.cpp here is the whole file].


If you have any problems you can PM me here [[User_talk:Draco]]
If you have any problems you can leave a message in the [[User:Draco|original author's talk page]] or you can attempt to contact him on IRC at <code>#ausbg</code> on GameSurge.
Or you can contact me at #ausbg on GameSurge IRC.


== See also ==
*[[Dynamic Weapon Spawns (Advanced)]]
[[Category:Tutorials]] [[Category:Programming]]
[[Category:Tutorials]] [[Category:Programming]]

Latest revision as of 03:09, 2 August 2013

Abstract Coding series Discuss your thoughts - Help us develop the articles or ideas you want

Levels & XP | Optimization | Procedural Textures | Sights & Sniperrifles | Special effects | Vehicles | Threads | Save Game Files | Night Vision | Non-offensive Weapons | Dynamic Weapon Spawns | Dynamic Weapon Spawns (Advanced)

For years, many console FPS games have been using a great feature called dynamic weapon spawn. The best example to illustrate that feature is the Perfect Dark game. Indeed, you can choose up to six different weapons at the beginning of the game, then the game will put those weapons at whatever point the weapon was assigned to. Pistols should go in all slot 1 spawns, cloak generators at another, and so on.

This kind of feature might add some longevity to your map because each game would let you select different sets of weapons. This article describes how one would go about adding this to a PC game such as Half-Life 2.

Setting it up

Creating a model entity called CWeaponSpawn seems to work.

Thus, create a new file called weaponspawns.cpp or something and place it in the \game_shared folder so the file will be shared by both the client and the server dlls. Then, add the file to both projects in the Visual C++ editor.

OK, now let's create the entity in the code, here's an empty shell of a model entity:

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

Whenever a server host / adminstrator has to set up options for their server, the best idea is to use Console Variables ( CVARs ). For that tutorial, we will use six different CVARs to implement our dynamic weapons system. To make it tight, copy that piece of code at the very bottom 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 this is possible to let the server host set an integer value to those CVARs to set weapons. Another way to do it is to set them to strings. Thus the CVar would look like this:

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 key resides in the entity 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

Since copy-pasting this whole entity might leave you with a bit of a tangle, here is the whole file.

If you have any problems you can leave a message in the original author's talk page or you can attempt to contact him on IRC at #ausbg on GameSurge.

See also