QuakeZone
MOD Coding Tutorials
Quake II Coding Tutorials!

Introduction
Pre-requisites
Code
Notes
Exercises
Quake II Coding Tutorials

14 Tech Pick-ups

Introduction Introduction

This tutorial describes how to create CTF/lithium-style Tech pick-ups. These pick-ups can then be assigned various "powers" - in this example we will be adding multi, homing, blister and accelerater power-ups for rockets.

Pre-requisites Pre-requisites

  • tutorial 13, Creating Random Spawn Points.
  • the Tech models and pcx files from CTF, which are available from the Quake 2 Downloads page on this site.
Code Code


g_local.h

Firstly locate the first line below and insert the next line:

#define IT_POWERUP		32
#define IT_TR_TECH		512		// PJT - tech pickups	
		
Add to the end of structure level_locals_t:

	float		next_techdrop;		// PJT - tech pickups - time to next Tech pickup drop	
		
Add the following lines at the indicated location:

// PJT - tech pick-ups
extern cvar_t *sv_techspawns;			// number of Tech pick-ups to spawn
extern cvar_t *sv_techlife;			// duration of Tech pick-up
// PJT <-- end of insert

#define world	(&g_edicts[0])
		
And again at this location:

// PJT - tech types
#define TR_TECH_MULTI			"Multi Tech"
#define TR_TECH_HOMING			"Homing Tech"
#define TR_TECH_BLISTER			"Blister Tech"
#define TR_TECH_ACCEL			"Accelerator Tech"
#define TR_TECH_TYPES			4
// PJT <-- end of insert

//
// g_monster.c
//
void monster_fire_bullet (edict_t *self, vec3_t start, vec3_t dir, int damage, int kick, int hspread,
		
Next, add the following prototype at indicated location:
//
// p_hud.c
//
void init_hud_rocket_strings(void);		// PJT - new hud
		
And finally, add at the end of the file:

// PJT - tech pickups
extern char *tech_types[];
// PJT
		

q_shared.h

Add the following line at the indicated position:
#define MAX_CONFIGSTRINGS	(CS_GENERAL+MAX_GENERAL)
#define CS_TR_STRINGS       	(CS_ITEMS+200)			// PJT - tech pickups in p_hud.c
		

g_items.c

Near the start of the file at the indicated location:

// PJT - rocket techs
char	*tech_types[] =
	{
		TR_TECH_MULTI,
		TR_TECH_HOMING,
		TR_TECH_BLISTER,
		TR_TECH_ACCEL
	} ;
// PJT <-- end of insert

qboolean	Pickup_Weapon (edict_t *ent, edict_t *other);
		
Locate the structure item_list, and add to the bottom as indicated:

	// PJT - tech pick-ups

	{
		"item_multi_tech",
		NULL,
		NULL,
		NULL,
		NULL,
		"items/pkup.wav",
		NULL,
	    0,
		NULL,
/* icon */		"techs/tech1",
		TR_TECH_MULTI,
		0,
		1,
		NULL,
		IT_TR_TECH,
		0,
		NULL,
		0,
		""	},

	{
		"item_homing_tech",
		NULL,
		NULL,
		NULL,
		NULL,
		"items/pkup.wav",
		NULL,
	    0,
		NULL,
/* icon */		"techs/tech2",
		TR_TECH_HOMING,
		0,
		1,
		NULL,
		IT_TR_TECH,
		0,
		NULL,
		0,
		""	},

	{
		"item_blister_tech",
		NULL,
		NULL,
		NULL,
		NULL,
		"items/pkup.wav",
		NULL,
	    0,
		NULL,
/* icon */		"techs/tech3",
		TR_TECH_BLISTER,
		0,
		1,
		NULL,
		IT_TR_TECH,
		0,
		NULL,
		0,
		""	},

	{
		"item_accel_tech",
		NULL,
		NULL,
		NULL,
		NULL,
		"items/pkup.wav",
		NULL,
	    0,
		NULL,
/* icon */		"techs/tech4",
		TR_TECH_ACCEL,
		0,
		1,
		NULL,
		IT_TR_TECH,
		0,
		NULL,
		0,
		""	},


	// PJT <-- end of insert

	// end of list marker
	{NULL}
};
		
Then, at the end of the file, add the following 2 functions:

// PJT - tech pick-ups

//=========================================================
// Touch function for when Tech-pickup is picked up by other..
//=========================================================
void SpawnTech_Touch(edict_t *tech, edict_t *other, cplane_t *plane, csurface_t *surf)
{
	int 	i, j, quantity, itemno;
	gitem_t *item=itemlist;

  // 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

  // check player already has pickup
  itemno = ITEM_INDEX(FindItem(tech_types[tech->rocket_type - 1]));
  if (other->client->pers.inventory[itemno])
	  return;

  // Immediately turn OFF effects
  tech->s.effects = 0;
  tech->s.sound   = 0;

  // Play standard item pickup sound...
  gi.sound(other, CHAN_ITEM, gi.soundindex("items/protect.wav"), 1.0, ATTN_NORM, 0);  // PJT last 0 was 'OFF'

  // pick up item
  other->client->pers.inventory[itemno] 		= 1;
  other->client->ps.stats[STAT_PICKUP_ICON] 	= gi.imageindex(FindItem(tech_types[tech->rocket_type - 1])->icon);
  other->client->ps.stats[STAT_PICKUP_STRING] 	= CS_TR_STRINGS + 30 + tech->rocket_type - 1; // ??
  other->client->pickup_msg_time 				= level.time + 3.0;

  gi.unlinkentity(tech);  	// Make tech disappear..
  G_FreeEdict (tech);	  	// really make it disappear

}


//=========================================================
// Toss a tech pickup from a random spawnpoint
//=========================================================
void SpawnTech(void)
{
int 	i,quantity;
float	rnum;
gitem_t *item=itemlist;
edict_t *tech;
vec3_t 	forward, right, up, vrandom;
edict_t	*dest;

  // if (!deathmatch->value) return;	// PJT -commented out - allow techs in coop/sp

  // Create backpack entity
  tech=G_Spawn();

  // Choose random tech type, and give appropriate model

  tech->rocket_type = (int)(random()*4)+1;
  tech->s.renderfx = 0;
  switch (tech->rocket_type)
  {
	case 1:		// multi tech
  	  tech->s.modelindex 	= gi.modelindex("models/techs/multi/tris.md2");
	  break;
	case 2:		// homing tech
  	  tech->s.modelindex 	= gi.modelindex("models/techs/homing/tris.md2");
  	  break;
	case 3:		// blister tech
  	  tech->s.modelindex 	= gi.modelindex("models/techs/blister/tris.md2");
  	  break;
	case 4:		// accelerator tech
  	  tech->s.modelindex 	= gi.modelindex("models/techs/accel/tris.md2");
  	  break;
  }

  // pick random destination
  // dest = SelectRandomSpawnPoint();  // SelectRandomDeathmatchSpawnPoint();

  // ------------------------------
  // Finish making the pack entity
  // ------------------------------

  tech->owner		= world;
  tech->movetype	= MOVETYPE_TOSS;
  tech->takedamage	= DAMAGE_NO;
  tech->health		= 0;
  tech->deadflag 	= DEAD_DEAD;  // So not in game..
  tech->solid 		= SOLID_TRIGGER;

  // tech->s.modelindex 	= gi.modelindex("models/objects/rocket/tris.md2");
  tech->s.effects 	= EF_ROTATE;	//EF_COLOR_SHELL & EF_ROTATE;
  // VectorCopy(dest->s.origin,pack->s.origin);
  VectorCopy(level.spawn_points[(int)(random()*level.num_spawn_points)] , tech->s.origin);
  tech->s.origin[2] 	+= 32;

  VectorSet(tech->mins,-16,-16,-16); // Size of standard Q2 tech pick-up
  VectorSet(tech->maxs, 16, 16, 16);
  //AngleVectors(dest->s.angles, forward, right, up);
  VectorSet(forward, 10,  0,   0);
  VectorSet(right,   0,   10,  0);
  VectorSet(up,      0,   0,   10);
  VectorScale(forward, 20, tech->velocity);
  VectorMA(tech->velocity, crandom()*10.0+20.0, 	up, 	 tech->velocity);
  VectorMA(tech->velocity, crandom()*20.0-10.0, 	right, 	 tech->velocity);
  VectorMA(tech->velocity, crandom()*20.0-10.0, 	forward, tech->velocity); // PJT random direction
  VectorClear(tech->avelocity); // No angular rotation
  VectorClear(tech->s.angles);  // No tilt..

  tech->touch		= SpawnTech_Touch;
  tech->nextthink 	= level.time + sv_techlife->value;  	// destroy time.
  tech->think     	= G_FreeEdict;	 					  	// destroy function

  gi.linkentity(tech);
}
// PJT
		

p_hud.c

Add the following procedure to the end of the file:
// PJT - new HUD - define HUD strings
void init_hud_rocket_strings(void)
{

	// PJT - tech pick-ups
	gi.configstring (CS_TR_STRINGS +30, "Multi Tech");
	gi.configstring (CS_TR_STRINGS +31, "Homing Tech");
	gi.configstring (CS_TR_STRINGS +32, "Blister Tech");
	gi.configstring (CS_TR_STRINGS +33, "Accel Tech");
	// PJT

}
// PJT <-- end of insert
		

g_spawn.c

Add the following lines to procedure SP_worldspawn as indicated:
	// set configstrings for items
	SetItemNames ();

	// PJT - set configstrings for team rocket HUD (see p_hud.c)
	init_hud_rocket_strings();
	// PJT - end of insert
		

g_main.c

Add the following lines as indicated near the top of the file:
edict_t		*g_edicts;

// PJT - Tech pick-ups
cvar_t	*sv_techspawns;
cvar_t	*sv_techlife;
// PJT <-- end of insert
		
Add the following procedure just before G_RunFrame:
// PJT - Tech pick-ups

/*
================
DropTechPickups

Drops the Tech Pick-ups
================
*/
void DropTechPickups (void)
{
	int i;

	// don't process if not time yet
	if (level.time < level.next_techdrop) return;

	// else spawn out rockets
	for (i=0; ivalue; i++)
	{
		SpawnTech();
	}

	// set next Tech drop time
	level.next_techdrop = level.time + sv_techlife->value;

}

// PJT
		
Finally in G_RunFrame add following lines as indicated:
	//  PJT - see if Tech Pick-ups need dropping
	DropTechPickups();
	// PJT <-- end of insert

	// see if needpass needs updated
	CheckNeedPass ();
		

g_save.c

Add the following lines to the end of procedure InitGame:
	// PJT - Tech pick-ups
	sv_techspawns = gi.cvar("sv_techspawns","5", CVAR_SERVERINFO);	// number of Tech pick-ups
	sv_techlife   = gi.cvar("sv_techlife", "30", CVAR_SERVERINFO);	// duration of Tech pick-up
	level.next_techdrop = 0.0;										// PJT - Tech pick-ups
	// PJT
		

g_cmds.c

Add the following lines to procedure Cmd_Give_f as indicated:
	
	if (give_all)
	{
		for (i=0 ; ipickup) 		// PJT - allow tech items
			// 	continue;
			// if (it->flags & (IT_ARMOR|IT_WEAPON|IT_AMMO)) 	// PJT - only TR items
			if (!(it->flags & (IT_TR_TECH))) 	// PJT - only Tech items
				continue;
			// PJT - team rocket
			if (it->flags & (IT_TR_TECH))						// item - only have 1
				ent->client->pers.inventory[i] = 1;
			else if	(ent->client->pers.inventory[i] < 5 )					// pickup - dont give more than 5
				ent->client->pers.inventory[i] = 5;
			// PJT
		}
		return;
	}
		
If, for example, you have a command procedure within g_cmds.c to activate homing missiles, but only if the player has a homing Tech, add the following lines to the start of that procedure:
	// PJT - tech pickups
	if (!ent->client->pers.inventory[ITEM_INDEX(FindItem(TR_TECH_HOMING))])
	{
		gi.cprintf (ent, PRINT_HIGH, "Homing Tech required.\n");
		return;
	}
	// PJT
		

cmds.cfg

Add the following commands to your config file:
set sv_techspawns   5
set sv_techlife     30
		
Notes Notes to code


g_local.h
  • IT_TR_TECH is the item type for Tech pick-ups in the item list (see g_items.c)
  • next_techdrop is the level timer for when to drop Tech pick-ups
  • the cvars are external pointers to server variables (see cmds.cfg)
  • the next defines are the item names for the Tech pick-ups
  • a prototype for init_hud_rocket_strings in p_hud.c is required
  • tech_types is an external table that holds the Tech pick-up names

q_shared.h
  • CR_TR_STRINGS points to a set of "slots" for holding the Tech pick-up names for displaying on the hud (see p_hud.c)

g_items.c
  • tech_types is defined here (pointed at in g_local.h) , using the string constants from g_local.h - alter this table for the Techs you require
  • next the different Tech pick-ups are added to the item_list:
    • add or remove entries to suit the needs for your mod
    • the item names are defined by the constants in g_local.h
    • use item type of IT_TR_TECH (again see g_local.h)
    • the icon names (techs/tech1 etc) must exist as pcx files in your game folder's "pics" folder
  • SpawnTech_Touch is the "touch" function for the Tech pick-ups, called when another entity touches the Tech:
    • if touching object is not an active client, don't do anything
    • if player already has this pick-up, don't pick this item up
    • otherwise a pick-up sound is played and the tech pick-up is added to the player's inventory, and the pick-up is (temporarily) displayed on the player's hud
    • the Tech entity is "destroyed"
  • SpawnTech is called (from g_main.c) when a Tech is spawned (duh):
    • the type is chosed from 1 of 4 at random, and the corresponding model assigned (alter for your own purposes)
    • various flags set, to give it a "toss" motion, and make it touchable and rotating
    • a random spawn point is chosen from the spawn-point table, and the Tech is "thrown" from that point to scatter it
    • finally the touch function is set, its lifetime is set, and the Tech is created

p_hud.c
  • init_hud_rocket_strings sets up the Tech names to be displayed on the HUD

g_spawn.c
  • SP_Worldspawn, called at the start of every game, calls init_hud_rocket_strings in p_hud.c

g_main.c
  • the Tech pick-up server variables (cvar) are defined
  • DropTechPickups is called every game frame:
    • if the Tech pickups are not due yet (set by one of the cvars above), the routine is quick exited
    • otherwise, a number of Tech pick-ups are spawned, again set by a cvar
    • the next time period for dropping is set
  • G_RunFrame, which processes every game time frame, calls DropTechPickups every frame to see if Techs need to be spawned

g_save.c
  • InitGame sets the cvar variables and sets the first Tech drop

g_cmds.c
  • Cmd_Give_f has been changed so that if "give all" is entered at the console, the player is given the Tech pick-ups
  • an example is given of how to check for ownership of a Tech pick-up before activating homing missiles

cmds.cfg
  • following variables to add to your config file:
    • sv_techspawns sets the number of Tech Pick-ups spanwed at a time
    • sv_techlife sets how long the Tech pick-up lasts, and the interval between spawning
Exercises Exercises

  • define yuor own Tech power-ups, and add new models and icons


Page last updated 6th January 2001