Backpack Drop Add-On

Title: Backpack Drop Add-On
Difficulty: Moderate
By: Maj.Bitch
Email: peblair@frontiernet.com
Date: 05-05-99
Note: Please give credit where credit is due.
======================================================
I thought about this a long time ago but then forgot
about it until I was reminded, by accident, when Havik
posted on the message board a request for a Tutorial
about a player dropping a backpack full of their inventory
items when they died. Previously, the only thing
the player dropped when they died was their current weapon
and the quad (if they had it). Now, when the player dies
a backpack comes flying off which contains all their present
inventory.

What it does:

The server admin enables a sv_backpack var to turn this feature ON/OFF.

If ON, when a player dies (and since they won't be needing these
items any longer), a separate backpack entity is created which is
loaded up with all their inventory items and current quantities.
Their backpack goes flying off their body and bounces around until
it comes to a stop. The blood all over the pack even attracts flies!
hehe.. The pack contains all their current inventory (including
weapons, ammo, quad, and everything else they were carrying at the
time!!) The pack entity lasts for 20 seconds then disappears so after
you kill the bastard, make sure you pick up their pack before somebody
else does!


HINT: You could easily modify this code so that you could toss out
a backpack full of your inventory so that other members of your team
could pick it up!


Okay! Let's get started!

======================================================
Let's open up your g_locals.h file and add the server
config var first like so:


extern cvar_t *sv_backpack;    // 1=ON  - Enable Backpack dropping


And let's formally declare this new variable at the top
of your g_main.c file like so:


cvar_t *sv_backpack;


======================================================
Let's set the initial state of this in your InitGame()
function by adding this line:


  sv_backpack=gi.cvar("sv_backpack","1", CVAR_SERVERINFO);


NOTE: The '1' will set the default state to ON. Change it
to a '0' if you want the default state to be OFF.. Your choice..

======================================================
Now, go into your p_client.c file and find your player_die()
function. We need to make a quick change there..


void player_die(edict_t *self, edict_t *inflictor, edict_t *attacker, int damage, vec3_t point) {
int n;
static int i;

  // if no-one died, then exit..
  if (!G_EntExists(self)) return;

  // Reset these variables.
  VectorClear(self->avelocity);
  self->takedamage=DAMAGE_YES;
  self->movetype=MOVETYPE_TOSS;
  self->s.modelindex2=0;  // remove linked weapon model, if any
  self->s.angles[PITCH]=0.0;
  self->s.angles[ROLL]=0.0;
  self->s.sound=0;
  self->client->weapon_sound=0;
  self->maxs[2]= -8.0; // set player's view just above ground..
  self->svflags|=SVF_DEADMONSTER;

  if (!self->deadflag) {
    // Automatic respawn in 1 second
    self->client->respawn_time=level.time+1.0;
    // Make player look at killer..
    LookAtKiller(self, inflictor, attacker);
    self->client->ps.pmove.pm_type=PM_DEAD;
    // Play any obituaries for this player
    ClientObituary(self, inflictor, attacker);
//============  ADD THIS HERE ================
    // If backpack feature enabled then..
    if (sv_backpack->value==1.0)
      // Toss out a backpack full of player's items
      TossClientBackpack(self);
    else
      // Toss out the player's current weapon
//===========   STOP HERE ====================
      TossClientWeapon(self);
    if (deathmatch->value)
      Cmd_Help_f(self); // show scores

This will check to see if the backpack feature has been
enabled by the server admin. If so, then the normal
TossClientWeapon() function is by-passed and the new
function called TossClientBackpack() is enabled...

======================================================
Okay, let's put together all the stuff we'll need for
this new TossClientBackpack() functionality..

First, add this new variable array to the bottom of your
edict_s struct.


  int      packitems[MAX_ITEMS]; // New for Backpack (Maj.Bitch)


We'll need an array of items in the edict_t struct because
the backpack will be a new entity and the only inventory
array so far is in the client struct (and the backpack won't
be a client) so we have to add this here.

======================================================
Okay, now comes the easy part.

At a point just above your player_die() function, cut and
paste in these new functions..

======================================================


//=========================================================
// Touch function for when backpack is picked up by other..
//=========================================================
void Backpack_Touch(edict_t *pack, edict_t *other, cplane_t *plane, csurface_t *surf) {
int i,quantity;
gitem_t *item=itemlist;

  if (!G_EntExists(other)) return;

  // Immediately turn OFF Flies effect
  back->s.effects &= ~EF_FLIES;
  pack->s.sound=0;

  // Play standard item pickup sound...
  gi.sound(other, CHAN_ITEM, gi.soundindex("items/pkup.wav"), 1.0, ATTN_NORM, OFF);

  // Step thru array of items in backpack..
  for (i=0; i<game.num_items; i++, item++) {
    quantity = pack->packitems[ITEM_INDEX(item)];
    if (!quantity) continue;
    other->client->pers.inventory[ITEM_INDEX(item)] += quantity; }

  gi.unlinkentity(pack);  // Make pack disappear..
}

//======================================================
void Backpack_Think(edict_t *pack) {
  // Turn ON Flies effect around pack..
  pack->s.effects = EF_FLIES;
  // Play Flies sound effect..
  pack->s.sound = gi.soundindex("infantry/inflies1.wav");
  // Make pack disappear in 20 seconds
  pack->nextthink=level.time+20.0;
  pack->think=G_FreeEdict;
}

//=========================================================
// When player dies, toss out backpack full of their items!!
//=========================================================
void TossClientBackpack(edict_t *player) {
int i,quantity;
gitem_t *item=itemlist;
edict_t *pack;
vec3_t forward, right, up;

  if (!deathmatch->value) return;

  // Create backpack entity
  pack=G_Spawn();

  // ----------------------------
  // Now.. Fill up the Backpack
  // ----------------------------

  // Look thru list of all possible items..
  for (i=0; i<game.num_items; i++, item++) {
    if (!item->classname) continue;
    // Does player have any of this item in their inventory?
    quantity = player->client->pers.inventory[ITEM_INDEX(item)];
    if (!quantity) continue;
    // Then.. add this item to the backpack
    pack->packitems[ITEM_INDEX(item)] = quantity; }

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

  pack->owner=world;
  pack->movetype=MOVETYPE_TOSS;
  pack->takedamage=DAMAGE_NO;
  pack->health=0;
  pack->deadflag = DEAD_DEAD;  // So not in game..
  pack->solid = SOLID_TRIGGER;
  pack->s.modelindex = gi.modelindex("models/items/pack/tris.md2");
  pack->s.effects = EF_NONE;
  VectorCopy(player->s.origin,pack->s.origin);
  pack->s.origin[2] += 32;
  VectorSet(pack->mins,-16,-16,-16); // Size of standard Q2 pack
  VectorSet(pack->maxs, 16, 16, 16);
  AngleVectors(player->s.angles, forward, right, up);
  VectorScale(forward, 300, pack->velocity);
  VectorMA(pack->velocity, 200+crandom()*10.0, up, pack->velocity);
  VectorMA(pack->velocity, crandom()*10.0, right, pack->velocity);
  VectorClear(pack->avelocity); // No angular rotation
  VectorClear(pack->s.angles);  // No tilt..
  pack->isabot=true; // Disable all printf() messages..

  pack->touch=Backpack_Touch;

  pack->nextthink=level.time+2.0; // In 2 secs, make Flies effect..
  pack->think=Backpack_Think;

  gi.linkentity(pack);
}


The comments should indicate what is happening in each of the functions..
======================================================
======================================================

That's it!! Now get out there and KILL ALL THE BASTARDS AND
THEN STEAL THE SHIT THEY'VE BEEN CARRYING AROUND WITH'EM!

Have Fun!!

Maj.Bitch