Created By: Robert Elsner
eMail: t.n.matthews@mail.utexas.edu
Difficulty Scale: Medium/Hard


 
Intro 

This is a one part tutorial that creates an entirely new entity, assigns new key/value pairs and sets it up for map authors to include in their own maps.

 
General Setup  

A .MAP with the entity defined below included and positioned.

   
 
Tutorial Code 

Step 1 
Open your quake2 project and prepare to edit! 

Step 2 
Open g_items.c goto line 1945, and just after the }, append a line and the following code. 
 { 
  "item_ammo_pack", 
  Pickup_Ammo_Pack, 
  NULL, 
  NULL, 
  NULL, 
  "items/pkup.wav", 
  "models/items/pack/tris.md2", EF_ROTATE, 
  NULL, 
/* icon */  "i_pack", 
/* pickup */ "TW ammo pack", 
/* width */  2, 
  180, 
  NULL, 
  0, 
  NULL, 
  0, 
/* precache */ "" 
 }, 

Step 3

Goto line 19 and append this code after the definition of Weapon_BFG.
qboolean Pickup_Ammo_Pack (edict_t *ent, edict_t *other);
Step 4


Open g_save.c and goto line 6.  Insert the following code.
        {"ammo", FOFS(ammo), F_LSTRING},
        {"ammo_amount", FOFS(ammo_amount), F_INT},
Step 5 

Open g_local.h and goto line 977. Append this code to the edict_t definition. 
 char*  ammo; 
 int   ammo_amount; 
 

Step 6


Open g_spawn.c and at line 126 append this code.
void SP_item_ammo_pack (edict_t *self);
At line 136, insert this code.
        {"item_ammo_pack", SP_item_ammo_pack},
Step 7

Create a new file named ammo.c and add it to the project file.  Insert the following code into ammo.c.
#include "g_local.h"

void droptofloor (edict_t *ent);  // So we can have access to that func

void SP_item_ammo_pack (edict_t *self)
{
        self->model = "models/items/pack/tris.md2";
        self->count = 100;
        SpawnItem (self, FindItem ("TW ammo pack"));
}


qboolean Pickup_Ammo_Pack (edict_t *ent, edict_t *other)
{
        gitem_t *item;
        int             index;

        if ( ent->ammo && ent->ammo_amount ) // Do we have an ammo amount and type?
        {
                if ( !Q_stricmp ( ent->ammo, "rockets" ) ) // Is it rockets or other things?
                {
                        item = FindItem ( "Rockets" ); // It's rockets, find that Item
                        if ( item ) // NOT null, so we must have something
                        {
                                index = ITEM_INDEX ( item ); // get it's index number
                                other->client->pers.inventory[index] += ent->ammo_amount; // add ammo_amount
                        }
                }
                else if ( !Q_stricmp ( ent->ammo, "shells" ) )
                {
                        item = FindItem ( "Shells" );
                        if ( item )
                        {
                                index = ITEM_INDEX ( item );
                                other->client->pers.inventory[index] += ent->ammo_amount;
                        }
                }
                else if ( !Q_stricmp ( ent->ammo, "slugs" ) )
                {
                        item = FindItem ( "Slugs" );
                        if ( item )
                        {
                                index = ITEM_INDEX ( item );
                                other->client->pers.inventory[index] += ent->ammo_amount;
                        }
                }
                else if ( !Q_stricmp ( ent->ammo, "cells" ) )
                {
                        item = FindItem ( "Cells" );
                        if ( item )
                        {
                                index = ITEM_INDEX ( item );
                                other->client->pers.inventory[index] += ent->ammo_amount;
                        }
                }
                else if ( !Q_stricmp ( ent->ammo, "grenades" ) )
                {
                        item = FindItem ( "Grenades" );
                        if ( item )
                        {
                                index = ITEM_INDEX ( item );
                                other->client->pers.inventory[index] += ent->ammo_amount;
                        }
                }
                SetRespawn ( ent, 20 );
                return true;
        }
        return false;
}
   
 
Ending 

Step 2 simply creates the gitem_t definition of our item, item_ammo_pack.  You will see all of the other items useable by Quake2 (health, armor, keys, etc) defined here.  The first entry is the classname.  The second is what to do incase a player touches our item.  The third, fourth and fifth control using, dropping and weapon think functions respectively.  The sixth is the sound to make if Pickup_Ammo_Pack returns true.  The seventh is the model and the eigth are the model flags (EF_ROTATE makes it spin). The tenth is the icon to show in the HUD and the eleventh is what to say when the player gets this item. The other fields are unused. 

Step 3 simply defines Pickup_Ammo_Pack in g_items.c so the item's touch function can be called. 

Step 4 is the most important.  It defines our NEW key/value pairs for entities.  Ammo is a string, and specifies rockets, shells, slugs, cells or grenades (case sensitive). Ammo_amount is an integer and specifies how much to add (or subtract) when a player picks up this pack.  FOFS returns the position in the edict_s structure that the entity loading code can expect to put the values for the declared keys. VERY important to define these variables in the edict_t structure. 

Step 5 does the very important part mentioned above. 

Step 6 declares SP_item_ammo_pack, but it also defines the item's classname and spawn function.  SP_item_ammo_pack MUST be callable by any function in g_spawn.c or your code won't compile. 

Step 7 creates the entity and manges it being touched. The line 
SpawnItem (self, FindItem ("TW ammo pack"));  
tells the game engine to go ahead and spawn this entity (edict) and FindItem takes a classname and returns a gitem_s structure. 
Pickup_Ammo_Pack checks to see what sort of ammo we should give the recipient. The function FindItem is one of the more useful functions when dealing with items and entities (edicts).  The function ITEM_INDEX returns the index # of the item which is used to increment the proper ammo column of the player who touched the pack.
SetRespawn ( ent, 20 );
That function tells the game engine to respawn this same item in level.time + 20 ticks.  From a touch function for edicts, a return value of true indicates the player can pickup the entity they ran over.  Returning false tells the engine to leave it.

Using this entity in MAP files looks something like this:
{
"classname" "item_ammo_pack"
"origin" "-120 -256 300"
"ammo" "rockets"
"ammo_amount" "15"
}