Post tutorial 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:

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

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

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

void Weapon_Null (edict_t *ent);

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

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

{
        "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:

#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

#define MOD_PUNCH    34

Now, go to p_client.c, and find

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

under it, add this:

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:

PrecacheItem (FindItem ("Blaster"));

replace with

PrecacheItem (FindItem ("Hands"));

Find

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

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

Find

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

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

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

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

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

Replace with

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 Author
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
Share
Related Games
Quake 2
Quake 2 Single & Multiplayer First Person Shooter
Related Engines
id Tech 2
id Tech 2 GPL Released Dec 1997
Related Groups
Activision
Activision Developer & Publisher
id Software
id Software Developer & Publisher with 2 members