
Quake DeveLS - Sword Of The Highlander
Author: Patrick
Wagstrom(Pridkett)
Difficulty: REALLY FRICKIN HARD
Pulling Quake 2 out of the
box, I could hardly wait to play my favorite form of deathmatch with my
friends, lumberjack! But alas, there was no melee weapon in Quake 2. So I got
to thinking about making a cool mod for Quake 2 and decided well, Highlander is
pretty cool. So while this isn't exactly highlander, because there ain't stuff
like quickening and what not (thats in the works), this code lets you play
lumberjack quake (without models or anything though).
On a side note, I had NO
CLUE how hard this was going to be when I first started working on the sword. I
thought (there's my problem, thinking) that it was going to be a relatively
easy procedure to add in the sword, but I found out that I had to modify many a
files in order to do it, and create some new ones...thus the really frickin hard
rating.
Note:
Text in red = Quake II's code
Text in
blue = Our code
Text in
gray = Comments
//::Pridkett = Beginning of my code
//!Pridkett = End of my code
Well, first things first,
we need to create the item for the sword in quake 2's item database so we open
up the file g_item.c and begin editing. Find the spot where it has the list of
all the items in the game. It should begin around line 990 or somewhere there
abouts...then find this sections of code beneath it and insert the definition
for the sword:
/*QUAKED weapon_bfg (.3 .3 1) (-16 -16 -16) (16 16 16)*/ { "weapon_bfg", Pickup_Weapon, Use_Weapon, Drop_Weapon, Weapon_BFG, "misc/w_pkup.wav", "models/weapons/g_bfg/tris.md2", EF_ROTATE, "models/weapons/v_bfg/tris.md2",/* icon */ "w_bfg",/* pickup */ "BFG10K", 0, 50, "Cells", IT_WEAPON, NULL, 0,/* precache */ "sprites/s_bfg1.sp2 sprites/s_bfg2.sp2 sprites/s_bfg3.sp2 weapons/bfg__f1y.wav weapons/bfg__l1a.wav weapons/bfg__x1b.wav weapons/bfg_hum.wav" }, //::Pridkett /* weapon_sword always owned, never in the world */ { "weapon_sword", NULL, Use_Weapon, //How to use NULL, Weapon_Sword, //What the function is "misc/w_pkup.wav", NULL, 0, "models/weapons/v_blast/tris.md2", //The models stuff "w_blaster", //Icon to be used "Sword", //Pickup name 0, 0, NULL, IT_WEAPON, NULL, 0, "weapons/blastf1a.wav misc/lasfly.wav" //The sound of the blaster //This is precached }, //!Pridkett
In addition at the top of
this file we need to make an additional definition now because we referred to a
function that is unknown as of right now. So look and add in this code that is
just a new function prototype:
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); //::Pridkettvoid Weapon_Sword (edict_t *ent);//!Pridkett
Okay. So now the game
knows that the weapon at least EXISTS. But now we have to do some handling so
we can actually use the weapon in the game. The first thing we have to do is to
give us a sword, because none of the levels have swords on them...yet. So lets
open up p_client.c and do some editing on that. Look for the
InitClientPersistant function and do some modifications as shown. This routine
essentially tells it to find the item called a "Sword" and then give
the person one of them.
void InitClientPersistant (gclient_t *client){ gitem_t *item; memset (&client->pers, 0, sizeof(client->pers)); item = FindItem("Sword"); client->pers.inventory[ITEM_INDEX(item)] = 1; item = FindItem("Blaster"); client->pers.selected_item = ITEM_INDEX(item); client->pers.inventory[client->pers.selected_item] = 1; client->pers.weapon = item; client->pers.health = 100; client->pers.max_health = 100; client->pers.max_bullets = 200; client->pers.max_shells = 100; client->pers.max_rockets = 50; client->pers.max_grenades = 50; client->pers.max_cells = 200; client->pers.max_slugs = 50;}
Now comes the REAL fun
part. I suggest you make another file for this part because it gets to a be a
lot of new stuff. Most of these functions are based off the blaster, because it
is the easiest to understand as towards how it works. I called mine g_highln.h
because eventually I plan to have more highlander-esque features involved in
here.
/* * sword variable definitions, I have this here because the sword is most likely still unbalanced * I know this because I killed a super tank with it without taking ANY damaga */#define SWORD_NORMAL_DAMAGE 100#define SWORD_DEATHMATCH_DAMAGE 150#define SWORD_KICK 500
These are a few constants
that I needed to declare because I am not quite sure how to balance out the
sword with the other weapons in the game. They should be pretty obvious as
towards what they do. I think I need to decrease the kick and damage because
you can pretty much kill a super tank with sword and have no resistance.
Each of the weapons in
quake 2 has quite a few smaller functions with confusing names. In keeping with
that spirit, I have named them along the same lines. Its quite obvious that
Carmack never took any formal programming classes because his code is horrible
to read... non-standard caps and function names....arghh...anyway, these are
just other functions with small modifications
/*=============fire_sword attacks with the beloved sword of the highlander edict_t *self - entity producing it, yourself vec3_t start - The place you are vec3_t aimdir - Where you are looking at in this case int damage - the damage the sword inflicts int kick - how much you want that bitch to be thrown back=============*/ void fire_sword ( edict_t *self, vec3_t start, vec3_t aimdir, int damage, int kick){ //You may recognize a lot of this from the fire lead command, which //is the one that I understood best what the hell was going on trace_t tr; //Not entirely sure what this is, I know that it is used //to trace out the route of the weapon being used...gotta limit it vec3_t dir; //Another point I am unclear about vec3_t forward; //maybe someday I will know a little bit vec3_t right; //better about what these are vec3_t up; vec3_t end; tr = gi.trace (self->s.origin, NULL, NULL, start, self, MASK_SHOT); if (!(tr.fraction < 1.0)) //I can only assume this has something to do //with the progress of the trace { vectoangles(aimdir,dir); AngleVectors(dir,forward,right,up); //possibly sets some of the angle vectors //as standards? VectorMA (start, 8192, forward, end); //This does some extension of the vector... //note how short I have this attack going } //The fire_lead had an awful lot of stuff in here dealing with the effect of the shot //upon water and whatnot, but a sword doesn't make you worry about that sort of stuff //thats why highlanders are so damn cool. if (!((tr.surface) && (tr.surface->flags & SURF_SKY))) { if (tr.fraction < 1.0) { if (tr.ent->takedamage) { //This tells us to damage the thing that in our path...hehe T_Damage (tr.ent, self, self, aimdir, tr.endpos, tr.plane.normal, damage, kick, 0); } else { if (strncmp (tr.surface->name, "sky", 3) != 0) { gi.WriteByte (svc_temp_entity); gi.WriteByte (TE_GUNSHOT); gi.WritePosition (tr.endpos); gi.WriteDir (tr.plane.normal); gi.multicast (tr.endpos, MULTICAST_PVS); if (self->client) PlayerNoise(self, tr.endpos, PNOISE_IMPACT); } } } } return;}
Now that we have the
actual mechanism for use of the sword down, we need to make some routines for
the sword to act as a weapon. These two routines are almost exact clones of how
the blaster works. Minus the fact that there no spawned entity for the damage.
The entity created dies almost immediately, so it does not travel, and thus
needs to be in contact in order to damage something.
void sword_attack (edict_t *ent, vec3_t g_offset, int damage){ vec3_t forward, right; vec3_t start; vec3_t offset; if (is_quad) damage *= 4; AngleVectors (ent->client->v_angle, forward, right, NULL); VectorSet(offset, 24, 8, ent->viewheight-8); VectorAdd (offset, g_offset, offset); P_ProjectSource (ent->client, ent->s.origin, offset, forward, right, start); VectorScale (forward, -2, ent->client->kick_origin); ent->client->kick_angles[0] = -1; fire_sword (ent, start, forward, damage, SWORD_KICK );} void Weapon_Sword_Fire (edict_t *ent){ int damage; if (deathmatch->value) damage = SWORD_DEATHMATCH_DAMAGE; else damage = SWORD_NORMAL_DAMAGE; sword_attack (ent, vec3_origin, damage); ent->client->ps.gunframe++;}
Now as we saw when we
created the sword weapon there is one function we need to define. That is the
function that the item calls when it is used. Quake 2 uses a generic weapon
handling routine to do everything and just passes it functions, so lets keep
with the scheme of things. The frames listed at the bottom are those of the
blaster. So you really can't tell the difference when you are using the blaster
or sword right now, other than the fact that the blaster fires and the sword
doesn't.
void Weapon_Sword (edict_t *ent){ static int pause_frames[] = {19, 32, 0}; static int fire_frames[] = {5, 0}; Weapon_Generic (ent, 4, 8, 52, 55, pause_frames, fire_frames, Weapon_Sword_Fire);} //!Pridkett
My next tutorial will
hopefully be one on how to make it so you have to attack people with the sword
to kill them. Eventually I hope to have a total conversion (well at least rules
wise) down for you to read.
Now to make this part of
the code work, you need to include it in the program somewhere. Open up
p_weapon.c and add this at the last line:
#include "g_highln.h"
To make things a little
easier, I have included the entire g_highln.h file here for you. It should help
in speeding some things up for you, but you still need to add the other stuff:
//::Pridkett /* * Highlander Quake 2 * Main Header File * Author(s): Pridkett (pridkett@null.net) * * Last Revision: 1/1/98 - Wow...the sword actually works now * 12/29/97 - Initial Code */ /* * sword variable definitions, I have this here because the sword is most likely still unbalanced * I know this because I killed a super tank with it without taking ANY damaga */#define SWORD_NORMAL_DAMAGE 100#define SWORD_DEATHMATCH_DAMAGE 150#define SWORD_KICK 500 /*=============fire_sword attacks with the beloved sword of the highlander edict_t *self - entity producing it, yourself vec3_t start - The place you are vec3_t aimdir - Where you are looking at in this case int damage - the damage the sword inflicts int kick - how much you want that bitch to be thrown back=============*/ void fire_sword ( edict_t *self, vec3_t start, vec3_t aimdir, int damage, int kick){ //You may recognize a lot of this from the fire lead command, which //is the one that I understood best what the hell was going on trace_t tr; //Not entirely sure what this is, I know that it is used //to trace out the route of the weapon being used...gotta limit it vec3_t dir; //Another point I am unclear about vec3_t forward; //maybe someday I will know a little bit vec3_t right; //better about what these are vec3_t up; vec3_t end; tr = gi.trace (self->s.origin, NULL, NULL, start, self, MASK_SHOT); if (!(tr.fraction < 1.0)) //I can only assume this has something to do //with the progress of the trace { vectoangles(aimdir,dir); AngleVectors(dir,forward,right,up); //possibly sets some of the angle vectors //as standards? VectorMA (start, 8192, forward, end); //This does some extension of the vector... //note how short I have this attack going } //The fire_lead had an awful lot of stuff in here dealing with the effect of the shot //upon water and whatnot, but a sword doesn't make you worry about that sort of stuff //thats why highlanders are so damn cool. if (!((tr.surface) && (tr.surface->flags & SURF_SKY))) { if (tr.fraction < 1.0) { if (tr.ent->takedamage) { //This tells us to damage the thing that in our path...hehe T_Damage (tr.ent, self, self, aimdir, tr.endpos, tr.plane.normal, damage, kick, 0); } else { if (strncmp (tr.surface->name, "sky", 3) != 0) { gi.WriteByte (svc_temp_entity); gi.WriteByte (TE_GUNSHOT); gi.WritePosition (tr.endpos); gi.WriteDir (tr.plane.normal); gi.multicast (tr.endpos, MULTICAST_PVS); if (self->client) PlayerNoise(self, tr.endpos, PNOISE_IMPACT); } } } } return;} void sword_attack (edict_t *ent, vec3_t g_offset, int damage){ vec3_t forward, right; vec3_t start; vec3_t offset; if (is_quad) damage *= 4; AngleVectors (ent->client->v_angle, forward, right, NULL); VectorSet(offset, 24, 8, ent->viewheight-8); VectorAdd (offset, g_offset, offset); P_ProjectSource (ent->client, ent->s.origin, offset, forward, right, start); VectorScale (forward, -2, ent->client->kick_origin); ent->client->kick_angles[0] = -1; fire_sword (ent, start, forward, damage, SWORD_KICK );} void Weapon_Sword_Fire (edict_t *ent){ int damage; if (deathmatch->value) damage = SWORD_DEATHMATCH_DAMAGE; else damage = SWORD_NORMAL_DAMAGE; sword_attack (ent, vec3_origin, damage); ent->client->ps.gunframe++;} void Weapon_Sword (edict_t *ent){ static int pause_frames[] = {19, 32, 0}; static int fire_frames[] = {5, 0}; Weapon_Generic (ent, 4, 8, 52, 55, pause_frames, fire_frames, Weapon_Sword_Fire);} ////!Pridkett
|
This
site, and all content and graphics displayed on it, |