QuakeZone
MOD Coding Tutorials
Quake II Coding Tutorials!

Introduction
Pre-requisites
Code
Notes
Exercises
Quake II Coding Tutorials

18 Team Weapons

Introduction Introduction
Related Links

15 Teamplay Enhancements

This tutorial demonstrates how to allocate different weapons to different teams. In this example for 2 teams, the host can configure either team to use rocket launchers or railguns, thus giving team rocket arena, rail arena or rocket versus rail.

Pre-requisites Pre-requisites

You will require tutorial 15, team play enhancements, plus any tutorials required for that.

Code Code


g_local.h

Add the following lines as indicated:


1. Locate the teamplay enhancements cvars:

extern cvar_t *sv_allowswitch;			// allow team switching
extern cvar_t *sv_teamrail1;			// team rail options  <-- insert 
extern cvar_t *sv_teamrail2;			//                    <-- insert

2. Locate prototypes for p_client.c:

void AssignSkin(edict_t *ent, char *s);		// PJT - team play
void AssignWeapon(edict_t *ent);		// PJT - team rail    <-- insert

3. Locate struct client_respawn_t:

	int 	    team;			// PJT - team play
	int         teamrail;			// PJT - rail option  <-- insert

4. Locate table team_Table (added for teamplay enhancements):

// PJT - team play
typedef struct
{
	char	*name;				// team name
	int	score;				// team score
	int     teamrail;			// team rail option <-- insert
} team_data;
extern team_data team_table[];
// PJT
		

p_client.c

Make folloiwng changes as indicated:

1. At the top of the code, add the cvars as indicated:

cvar_t *sv_allowswitch; // PJT - teams - allow switching (teamplay enhancements)
cvar_t *sv_teamrail1;	// PJT - team rail option   <-- insert
cvar_t *sv_teamrail2;   // PJT                      <-- insert

2. Locate the table team_table and totally replace with the following:

// PJT - team play
team_data	team_table[] =
{
	{"",     0, 0},
	{"Blue", 0, 0},
	{"Red",  0, 1}
}
;
// PJT

3. Just before procedure AssignTeam (added for teamplay enhancements) add following new procedure:

// PJT - team rocket / rail
void AssignWeapon(edict_t *self)
{
	gitem_t		*item;

	// determine rocket/rail option for team
	self->client->resp.teamrail = team_table[self->client->resp.team].teamrail;

	// give rockets, no rails
	if (self->client->resp.teamrail == 0)
	{

    	item = FindItem("Slugs");
    	self->client->pers.selected_item = ITEM_INDEX(item);
    	self->client->pers.inventory[self->client->pers.selected_item] = 0;
    	item = FindItem("Railgun");
    	self->client->pers.selected_item = ITEM_INDEX(item);
    	self->client->pers.inventory[self->client->pers.selected_item] = 0;

    	item = FindItem("Rockets");
    	self->client->pers.selected_item = ITEM_INDEX(item);
    	self->client->pers.inventory[self->client->pers.selected_item] = 5;
    	item = FindItem("Rocket Launcher");
    	self->client->pers.selected_item = ITEM_INDEX(item);
    	self->client->pers.inventory[self->client->pers.selected_item] = 1;
		item->use(self, item);

		self->client->pers.max_rockets = 50;
		self->client->pers.max_slugs   = 0;

	}

	// give rail, no rockets
	else
	{

    	item = FindItem("Rockets");
    	self->client->pers.selected_item = ITEM_INDEX(item);
    	self->client->pers.inventory[self->client->pers.selected_item] = 0;
    	item = FindItem("Rocket Launcher");
    	self->client->pers.selected_item = ITEM_INDEX(item);
    	self->client->pers.inventory[self->client->pers.selected_item] = 0;

    	item = FindItem("Slugs");
    	self->client->pers.selected_item = ITEM_INDEX(item);
    	self->client->pers.inventory[self->client->pers.selected_item] = 10;
    	item = FindItem("Railgun");
    	self->client->pers.selected_item = ITEM_INDEX(item);
    	self->client->pers.inventory[self->client->pers.selected_item] = 1;
		item->use(self, item);

	self->client->pers.max_rockets = 0;
	self->client->pers.max_slugs   = 50;
	}
} 

// PJT

4. At the end of procedure AssignTeam, add the following line as indicated:

        self->client->resp.team = best;
	AssignWeapon(self);		// PJT - team weapon  <-- insert

5. In procedure ClientThink, add the folloiwng lines as indicated:

	// PJT - team weapon  <-- insert
	if (client->resp.teamrail != team_table[client->resp.team].teamrail)
		AssignWeapon(ent);
	// PJT		      <-- end of insert

	if (level.intermissiontime)   // <-- locate this line 
	
		

g_save.c

In procedure InitGame, add following lines near end as indicated (this is code that was added for the teamplay enhancements) :

	sv_allowswitch = gi.cvar("sv_allowswitch","0",  CVAR_SERVERINFO);		// allow team switching  <-- locate this line
	sv_teamrail1 = gi.cvar("sv_teamrail1","0", CVAR_SERVERINFO);			// team rail options     <-- start of insert
	sv_teamrail2 = gi.cvar("sv_teamrail2","1", CVAR_SERVERINFO);			//
	team_table[1].teamrail = sv_teamrail1->value;					//
	team_table[2].teamrail = sv_teamrail2->value;					//			 <-- end of insert 
		

p_weapon.c

Add the following lines as indicated:

1. To Weapon_RocketLauncher_Fire:

	if (! ( (int)dmflags->value & DF_INFINITE_AMMO ) )			//                <-- locate this line
		if (ent->client->pers.inventory[ent->client->ammo_index] > 1)	// PJT team rail  <-- insert this line
			ent->client->pers.inventory[ent->client->ammo_index]--;

2. To weapon_railgun_fire:

	if (! ( (int)dmflags->value & DF_INFINITE_AMMO ) )			//                <-- locate this line
		if (ent->client->pers.inventory[ent->client->ammo_index] > 1)	// PJT team rail  <-- insert this line
			ent->client->pers.inventory[ent->client->ammo_index]--;
		

g_cmds.c

Add the folloiwng lines to Cmd_Switch_f as indicated:

	ent->client->resp.team  	= i;
	ent->client->resp.teamrail 	= team_table[ent->client->resp.team].teamrail;	// PJT - team rail <-- insert

	then....

	AssignSkin(ent, Info_ValueForKey (ent->client->pers.userinfo, "skin"));
	AssignWeapon(ent);		// PJT - team rail  <-- insert
		

g_items.c

You may have certain items in your game that you may not want to be picked up, depending on what weapon a player is holding. Locate the touch function for your item, and, as an example, add the following line near the start of your procedure:

  // don't bother if not active player
  if (!G_EntExists(other)) 	return;
  if (!other->client) 		return;		// only clients can pick up
  if (other->health < 1) 	return;		// dead people can't pickup
  if (other->client->resp.teamrail == 1)	return;		// PJT - not for rail <-- insert
		

g_main.c

In procedure G_RunFrame, locate the code previously added for the teamplay enhancements and insert the following lines as indicated:

	// PJT - teamplay enhancements
	if (level.framenum % 35 == 0)
	{
		gi.configstring (CS_TR_STRINGS +40, sv_teamname1->string);
		gi.configstring (CS_TR_STRINGS +41, sv_teamname2->string);
		team_table[1].name = sv_teamname1->string;
		team_table[2].name = sv_teamname2->string;
		// PJT - team rail options  <-- start of insert
		sv_teamrail1 = gi.cvar("sv_teamrail1","0", CVAR_SERVERINFO);
		sv_teamrail2 = gi.cvar("sv_teamrail2","1", CVAR_SERVERINFO);
		if ((team_table[1].teamrail != sv_teamrail1->value) ||
			(team_table[2].teamrail != sv_teamrail2->value))
			gi.bprintf (PRINT_HIGH, "Host has changed team weapons.\n");
		team_table[1].teamrail = sv_teamrail1->value;
		team_table[2].teamrail = sv_teamrail2->value;
		// PJT			   <-- end of insert
	}
	// PJT
		

cmds.cfg

Add the following lines to your Quake config file:

				
set sv_teamrail1    0
set sv_teamrail2    1
		
Notes Notes to code


g_local.h
  • externs for 2 server variables (cvars) are added to hold the weapons for each team
  • prototype added for AssignWeapon
  • teamrail, which holds the weapon option for a player's current team, has been added to the clients respawn data structure
  • teamrail, which holds the weapon option for a team, has been added to the team table data structure

p_client.c
  • 2 server variables (cvars) are added to hold the weapons for each team
  • default weapon values are added to the team table structure (0 = rockets, 1 = railguns)
  • AssignWeapon assigns a weapon for a player according to their team:
    • first, their team weapon option is set from the team table
    • if rockets are indicated, player is given rocket launcher and ammo, and all railguns and ammo are removed
    • and vice versa for railguns
    • item->use is called to force selection of the weapon
  • AssignWeapon has been added to AssignTeam to force weapon change when a player is assigned to a team or switches team
  • ClientThink is called every game frame to update a player:
    • if the host has changed the weapon option for the player's team, AssignWeapon is called to give the player the new weapon

g_save.c
  • InitGame is called when a new game starts:
    • the team weapon options are read from the server variables and stored in the team table

p_weapon.c
  • the rocket launcher and railgun fire procedures have been amended so that you never run out of ammo (thus you may default to another weapon). If you don't require this change, don't make these changes!

g_cmds.c
  • the Switch function was added by the teamplay enhancements to allow players to swap sides:
    • player's weapon option updated from the team table
    • a call to AssignWeapon is made to switch to new weapon

g_items.c
  • by way of example, in my Team Rocket add-on, I wanted to make sure players on a railgun team did not collect rocket pick-ups (because they are of no use t them)

g_main.c
  • G_RunFrame is called every game frame:
    • the team weapon server variables for both teams are checked every 3.5 seconds to avoid overflow problems
    • if they have changed, a message is sent to all players so there are no surprises
    • the team table is updated with the new weapon options. The players are given the new weapons in ClientThink (see p_client.c above)

cmds.cfg
  • the team weapon options defaults are set (0 = rockets, 1 = railguns)
Exercises Exercises

  • Try defining other team weapons, e.g. chaingun


Page last updated 5th April 2001