Report article RSS Feed Adding a new weapon (Melee)

Adding a new weapon is simple.. adding a MELEE weapons isn't as easy! If you've tried coding in Q2, you'll notice that you cannot use Fire_Hit for player weapons! This will fix that with a whole new Fire Function! Get ready to knife em up!

Posted by Paril on Feb 5th, 2006
Intermediate Server Side Coding.

[page=New weapon (Melee)]
Ok, first we need to define the fire function.

Open up g_weapon.c, head to the bottom, all the way to the bottom... further... after fire_bfg.. further... yes, right there. Add all this in straight from the bottom:

code:
/*
/*
==================
Fire_Punch
Commented for Moddb Tutorial
==================
*/

void fire_punch (edict_t *self, vec3_t start, vec3_t aim, int reach, int damage, int kick, int quiet, int mod)
{
  vec3_t    forward, right, up;
  vec3_t    v;
  vec3_t    point;
  trace_t   tr;

  vectoangles2 (aim, v);                   //
  AngleVectors (v, forward, right, up);    //
  VectorNormalize (forward);               //
  VectorMA( start, reach, forward, point); // Aiming stuff

  //see if the hit connects
  tr = gi.trace(start, NULL, NULL, point, self, MASK_SHOT);
  if(tr.fraction ==  1.0)
  {
    if(!quiet) //not needed, it's better to follow my later steps
      //gi.sound (self, CHAN_WEAPON, gi.soundindex ("weapons/swish.wav"), 1, ATTN_NORM, 0);
    return;
  }

  if(tr.ent->takedamage == DAMAGE_YES || tr.ent->takedamage == DAMAGE_AIM) // Make sure they took damage
  {
    // pull the player forward if you do damage
    VectorMA(self->velocity, 75, forward, self->velocity); // Pull forward
    VectorMA(self->velocity, 75, up, self->velocity); // Pull up a tad bit. You can't slide&#59;)

    // do the damage
    // FIXME - make the damage appear at right spot and direction
      T_Damage (tr.ent, self, self, vec3_origin, tr.ent->s.origin, vec3_origin, damage, kick/2,
                  DAMAGE_ENERGY, mod); // Time to Slice my friends
                  gi.sound(self, CHAN_WEAPON, gi.soundindex("weapons/phitw1.wav"), 1, ATTN_IDLE, 0); // Used for my Punch.
                               //Rename and use for whatever
   
    if(!quiet)
      gi.sound (self, CHAN_WEAPON, gi.soundindex ("weapons/meatht.wav"), 1, ATTN_NORM, 0); // Don't change this.
                            //This is only used if your weapon is not quiet.. Chainfist isn't quiet, knife is
  }
  else
  {
    if(!quiet)
      gi.sound (self, CHAN_WEAPON, gi.soundindex ("weapons/tink1.wav"), 1, ATTN_NORM, 0); //same as above

    VectorScale (tr.plane.normal, 256, point);
    gi.WriteByte (svc_temp_entity);
    gi.WriteByte (TE_SPARKS); //make sparks, not gunshot..
    gi.WritePosition (tr.endpos);
    gi.WriteDir (point);
    gi.multicast (tr.endpos, MULTICAST_PVS);
        gi.sound (self, CHAN_AUTO, gi.soundindex("weapons/phitw2.wav") , 1, ATTN_NORM, 0);  //hit wall sound
  }
}
 

Whew. That's alot of code.

Now, let's go to p_weapon.c, and make sure you've got a model! Put this code to the way bottom, like g_weapon

code:
/*
=======================
Punching/Melee
=======================
*/

void Null_Fire(edict_t *ent)
{
  int i;
  vec3_t    start;
  vec3_t    forward, right;
  vec3_t    angles;
  int     damage = 5; //change to whatever
  int     kick = 2; //ditto here
  vec3_t    offset;

  if (ent->client->ps.gunframe == 11) //rename 11 to after you're attack frame
  {
    ent->client->ps.gunframe++;
    return;
  }

  AngleVectors (ent->client->v_angle, forward, right, NULL);

  VectorScale (forward, -2, ent->client->kick_origin);
  ent->client->kick_angles[0] = -2;

  VectorSet(offset, 0, 8,  ent->viewheight-8 );
  P_ProjectSource (ent->client, ent->s.origin, offset, forward, right, start); //where does the hit start from?

  if (is_quad)
  {
    damage *= 4;
    kick *= 4;
  }

  // get start / end positions
  VectorAdd (ent->client->v_angle, ent->client->kick_angles, angles);
  AngleVectors (angles, forward, right, NULL);
  VectorSet(offset, 0, 8, ent->viewheight-8 );
  P_ProjectSource (ent->client, ent->s.origin, offset, forward, right, start);
  fire_punch (ent, start, forward, 45, damage, 200, 1, MOD_PUNCH); // yep, matches the fire_ function

  ent->client->ps.gunframe++; //NEEDED
  PlayerNoise(ent, start, PNOISE_WEAPON); //NEEDED

//  if (! ( (int)dmflags->value & DF_INFINITE_AMMO ) )
//    ent->client->pers.inventory[ent->client->ammo_index]--; // comment these out to prevent the Minus NULL Ammo bug
}

void Weapon_Null (edict_t *ent)
{
  static int  pause_frames[]  = {10, 21, 0};
  static int  fire_frames[] = {6, 0}; // Frame stuff here

  Weapon_Generic (ent, 3, 9, 22, 24, pause_frames, fire_frames, Null_Fire);
}
 

Ok, now, let's head to g_items.c, near the beginning.

You'll see

code:
void Weapon_Blaster (edict_t *ent);
void Weapon_Shotgun (edict_t *ent);
void Weapon_SuperShotgun (edict_t *ent);
void Weapon_Machinegun (edict_t *ent);
void Weapon_Chaingun (edict_t *ent);
void Weapon_HyperBlaster (edict_t *ent);
void Weapon_RocketLauncher (edict_t *ent);
void Weapon_Grenade (edict_t *ent);
void Weapon_GrenadeLauncher (edict_t *ent);
void Weapon_Railgun (edict_t *ent);
void Weapon_BFG (edict_t *ent);

under that, add

code:
void Weapon_Null (edict_t *ent);

That was easy! now, go lower, and find:

code:
/* weapon_blaster (.3 .3 1) (-16 -16 -16) (16 16 16)
always owned, never in the world
*/
  {
    "weapon_blaster",
    NULL,
    Use_Weapon,
    NULL,
    Weapon_Blaster,
    "misc/w_pkup.wav",
    NULL, 0,
    "models/weapons/v_blast/tris.md2",
/* icon */   "w_blaster",
/* pickup */  "Blaster",
    0,
    0,
    NULL,
    IT_WEAPON|IT_STAY_COOP,
    WEAP_BLASTER,
    NULL,
    0,
/* precache */ "weapons/blastf1a.wav misc/lasfly.wav"
  },

above that, add this:

code:
{
        "weapon_null",           //  The map entity name. dont include this in a map whatever you do.
        NULL,                    // The pickup function
        Use_Weapon,              // How to use
        NULL,                    // the drop function
       Weapon_Null,             //What the use function is
       "misc/w_pkup.wav",
       "models/nullweapon.md2",0,
       "models/nullweapon.md2", //The models stuff.(This is my Hands model)
       "w_blaster",             //Icon to be used. you could create another, you probably should
       "Hands",             //Pickup name. use this to give the item to someone at the start of the game
        0,
        0,
        NULL,
        IT_WEAPON|IT_STAY_COOP,
        WEAP_BLASTER,            // the model index, just an integer defined in g_local.h
        NULL,
        0,
        ""
},
 

Ok, now that that's done, let's head on over to g_local.c Find:

code:
#define MOD_EXIT      28
#define MOD_SPLASH      29
#define MOD_TARGET_LASER  30
#define MOD_TRIGGER_HURT  31
#define MOD_HIT       32
#define MOD_TARGET_BLASTER  33
#define MOD_FRIENDLY_FIRE 0x8000000
 

below them, add

code:
#define MOD_PUNCH    34

Now, go to p_client.c, and find

code:
case MOD_TELEFRAG:
  message = "tried to invade";
  message2 = "'s personal space";
  break;
 

under it, add this:

code:
case MOD_PUNCH:
  message = "took";
  message2 = "'s fist in the face";
  break;
 

Now that we have the means of death, let's replace the blaster with it! Find:

code:
PrecacheItem (FindItem ("Blaster"));

replace with

code:
PrecacheItem (FindItem ("Hands"));

Find

code:
if (weapon && !oldcount)
  {
    if (other->client->pers.weapon != ent->item && ( !deathmatch->value || other->client->pers.weapon == FindItem("Blaster") ) )
      other->client->newweapon = ent->item;
  }
 

Replace with

code:
if (weapon && !oldcount)
  {
    if (other->client->pers.weapon != ent->item && ( !deathmatch->value || other->client->pers.weapon == FindItem("Hands") ) )
      other->client->newweapon = ent->item;
  }

Find

code:
item = self->client->pers.weapon;
  if (! self->client->pers.inventory[self->client->ammo_index] )
    item = NULL;
  if (item && (strcmp (item->pickup_name, "Blaster") == 0))
    item = NULL;
 

Replace again with

code:
item = self->client->pers.weapon;
  if (! self->client->pers.inventory[self->client->ammo_index] )
    item = NULL;
  if (item && (strcmp (item->pickup_name, "Hands") == 0))
    item = NULL;
 

Find

code:
if ( ent->client->pers.inventory[ITEM_INDEX(FindItem("shells"))]
    &&  ent->client->pers.inventory[ITEM_INDEX(FindItem("shotgun"))] )
  {
    ent->client->newweapon = FindItem ("shotgun");
    return;
  }
  ent->client->newweapon = FindItem ("Blaster");
}

Replace with

code:
if ( ent->client->pers.inventory[ITEM_INDEX(FindItem("shells"))]
    &&  ent->client->pers.inventory[ITEM_INDEX(FindItem("shotgun"))] )
  {
    ent->client->newweapon = FindItem ("shotgun");
    return;
  }
  ent->client->newweapon = FindItem ("Hands");
}

Find

code:
item = FindItem("Blaster");
        client->pers.selected_item = ITEM_INDEX(item);
        client->pers.inventory[client->pers.selected_item] = 1;
 

Replace with

code:
item = FindItem("Hands");
        client->pers.selected_item = ITEM_INDEX(item);
        client->pers.inventory[client->pers.selected_item] = 1;

There you go! Now go punch some Strogg, and go punch and humiliate your foes.

Post comment Comments
SvenBoogie
SvenBoogie Feb 12 2006, 4:25pm says:

Why is this in the f.e.a.r. section?

+1 vote     reply to comment
Paril
Paril Feb 12 2006, 7:51pm says:

Shouldn't be..?

+1 vote     reply to comment
MOST
MOST Jun 5 2010, 12:21pm says:

good

+1 vote     reply to comment
flobblob
flobblob Jul 28 2010, 9:46am says:

Jesus! Look at all the coding! When I tried to do even a basic mod with quake 3 and just changed the rocket speed, then clicked compile, It wrecked the game. It always happens to what I do with other ones like Doom.

+1 vote     reply to comment
Post a Comment
click to sign in

You are not logged in, your comment will be anonymous unless you join the community today (totally free - or sign in with your social account on the right) which we encourage all contributors to do.

2000 characters limit; HTML formatting and smileys are not supported - text only

Tutorial
Browse
Tutorials
Report Abuse
Report article
Related Games
Quake 2
Quake 2 Single & Multiplayer First Person Shooter