Quake Style - Quake 2 Tutorials
New/Upgraded Weapons - Thunderbolt for Quake 2!
Surely the title explains this enough!


Thunderbolt Tutorial/instructions for Quake2.
Uses TE_LIGHTNING for the effect.
120% as good as the original (lacking the original view model of course)

Build Time: 5 mins for working code, off and on, 4 hours play testing with friends=)

What you will need:
tut-ThunderBolt-Omega.zip (you wont *NEED* this, but it helps. because its the graphics/sounds *I* used/ripped for it, as well as the c/header files I modified for this.

I wrote this whole thing in DOS Edit, and built it In LCC. (yes, as you've seen by my other posts, i do use MSVC++ usually, but for this lcc is just damned quicker.

Part 1: G_LOCAL.H

Find the MeansOfDeath defines, and add these.

 
//OM-LGUN
#define MOD_TBOLT               34
#define MOD_TBOLT_WATER         35
#define MOD_TBOLT_DISCHARGE     36
//OM-LGUN-END
 


Then scroll down to the prototypes for g_utils.c (just search for "g_utils.c") and add at the end after vectoangles:

 
//OM-LGUN
void vectoangles2 (vec3_t vec, vec3_t angles);
//OM-LGUN-END
 


Now find the rest of the weapon defines and add:

 
//OM-LGUN
#define WEAP_TBOLT                      12 // or whatever your last free # is
//OM-LGUN-END
 


Now jump to the *END* of the file, (best place for them) and add the following:

 
//OM-LGUN
#define TBOLT_DAMAGE 20
#define TBOLT_CELLS  1
//OM-LGUN-END
 


Part 2: G_ITEMS.C

Add:

 
//OM-LGUN
void Weapon_Thunderbolt (edict_t *ent);
//OM-LGUN-END
 


after the BFG (or whatever your last weapon is)

Now add this item after the bfg, or somewhere in your itemlist[].

 
//OM-LGUN
/*QUAKED weapon_thunderbolt (.3 .3 1) (-16 -16 -16) (16 16 16)
*/
                   {
                                      "weapon_thunderbolt",
                                      Pickup_Weapon,
                                      Use_Weapon,
                                      Drop_Weapon,
                                      Weapon_Thunderbolt,
                                      "misc/w_pkup.wav",
                                      "models/weapons/g_hyperb/tris.md2", EF_ROTATE,
                                      "models/weapons/v_hyperb/tris.md2",
                                      "w_tbolt",
                                      "Thunderbolt",
                                      0,
                                      TBOLT_CELLS,
                                      "Cells",
                                      IT_WEAPON|IT_STAY_COOP,
                                      WEAP_TBOLT,
                                      NULL,
                                      0,
                                      "weapons/tesla.wav weapons/lstart.wav models/proj/lightning/tris.md2"
                   },
//OM-LGUN-END
 


Part 3: G_UTILS.C

Paste the following two functions in. (replace the old vectoangles function)

 
//OM-LGUN
//NEW vectoangles plus vectoangles2, from rogue source )fixes a bug and adds
//something new.
 
void vectoangles (vec3_t value1, vec3_t angles)
{
                   float           forward;
                   float           yaw, pitch;
                   
                   if (value1[1] == 0 && value1[0] == 0)
                   {
                                      yaw = 0;
                                      if (value1[2] > 0)
                                                         pitch = 90;
                                      else
                                                         pitch = 270;
                   }
                   else
                   {
                   // PMM - fixed to correct for pitch of 0
                                      if (value1[0])
                                                         yaw = (int) (atan2(value1[1], value1[0]) * 180 / M_PI);
                                      else if (value1[1] > 0)
                                                         yaw = 90;
                                      else
                                                         yaw = 270;
                                      if (yaw < 0)
                                                         yaw += 360;
 
                                      forward = sqrt (value1[0]*value1[0] + value1[1]*value1[1]);
                                      pitch = (int) (atan2(value1[2], forward) * 180 / M_PI);
                                      if (pitch < 0)
                                                         pitch += 360;
                   }
 
                   angles[PITCH] = -pitch;
                   angles[YAW] = yaw;
                   angles[ROLL] = 0;
}
 
void vectoangles2 (vec3_t value1, vec3_t angles)
{
                   float           forward;
                   float           yaw, pitch;
                   
                   if (value1[1] == 0 && value1[0] == 0)
                   {
                                      yaw = 0;
                                      if (value1[2] > 0)
                                                         pitch = 90;
                                      else
                                                         pitch = 270;
                   }
                   else
                   {
                   // PMM - fixed to correct for pitch of 0
                                      if (value1[0])
                                                         yaw = (atan2(value1[1], value1[0]) * 180 / M_PI);
                                      else if (value1[1] > 0)
                                                         yaw = 90;
                                      else
                                                         yaw = 270;
 
                                      if (yaw < 0)
                                                         yaw += 360;
 
                                      forward = sqrt (value1[0]*value1[0] + value1[1]*value1[1]);
                                      pitch = (atan2(value1[2], forward) * 180 / M_PI);
                                      if (pitch < 0)
                                                         pitch += 360;
                   }
 
                   angles[PITCH] = -pitch;
                   angles[YAW] = yaw;
                   angles[ROLL] = 0;
}
 


Part 4: G_WEAPON.C

Add the following code at the end:

 
//OM-LGUN
void fire_lightning (edict_t *self, vec3_t start, vec3_t aimdir, int damage)
{
                   vec3_t                          from;
                   vec3_t                          end;
                   vec3_t                          dir;
                   vec3_t                          forward, right, up;
                   trace_t                         tr;
                   int                                  mask;
                   edict_t                          *tmpobj;
 
                   vectoangles2 (aimdir, dir);
                   AngleVectors (dir, forward, right, up);
 
                   VectorMA (start, 8192, forward, end);
                   mask = MASK_SHOT|CONTENTS_SLIME|CONTENTS_LAVA|CONTENTS_WATER;
 
                   tr = gi.trace (start, NULL, NULL, end, self, mask);
 
                   if (tr.contents & (CONTENTS_SLIME|CONTENTS_LAVA|CONTENTS_WATER))
                   {
                                      tmpobj = G_Spawn();
                                      VectorCopy (tr.endpos, tmpobj->s.origin);
                                      tmpobj->movetype                     = MOVETYPE_NONE;
                                      tmpobj->solid                               = SOLID_NOT;
                                      tmpobj->classname                    = "tmpobject";
                                      tmpobj->s.modelindex               = 0;
                                      tmpobj->nextthink                       = level.time + 0.01;
                                      tmpobj->think                               = G_FreeEdict;
                                      gi.linkentity (tmpobj);
 
                                      T_RadiusDamage(tmpobj, self, damage*2, NULL, damage*4, MOD_TBOLT_WATER);
                   }
                   else
                   {
 
                                      if ((tr.ent != self) && (tr.ent->takedamage))
                                                         T_Damage (tr.ent, self, self, aimdir, tr.endpos, tr.plane.normal, damage, damage, 0, MOD_TBOLT);
                   }
 
                   // make the lightning bolt from the gun to the wall. (cute huh?)
                   gi.WriteByte (svc_temp_entity);
                   gi.WriteByte (TE_LIGHTNING);
                   gi.WriteShort (tr.ent - g_edicts);  
                   gi.WriteShort (self - g_edicts);    
                   gi.WritePosition (tr.endpos);
                   gi.WritePosition (start);
                   gi.multicast (start, MULTICAST_PVS);
 
                   // spawn some sparks at the destination (go sparky go!)
                   gi.WriteByte (svc_temp_entity);
                   gi.WriteByte (TE_WELDING_SPARKS);
                   gi.WriteByte (15);
                   gi.WritePosition (tr.endpos);
                   gi.WriteDir (vec3_origin);
                   gi.WriteByte (0xB2 + (rand()&3));
                   gi.multicast (tr.endpos, MULTICAST_PVS);
 
                   if (self->client)
                                      PlayerNoise(self, tr.endpos, PNOISE_IMPACT);
}
//OM-LGUN-END
 


Part 5: P_WEAPON.C

**paste the following code at the end of the file. (easy shiz eh?)

 
//OM-LGUN
void Weapon_Thunderbolt_Fire (edict_t *ent)
{
                   vec3_t       offset, start;
                   vec3_t       forward, right;
                   int ammo;
                   int damage;
                   int toasty;
 
                   if (!(ent->client->buttons & BUTTON_ATTACK))
                   {
                                      ent->client->ps.gunframe++;
                   }
                   else
                   {
                                      if (! ent->client->pers.inventory[ent->client->ammo_index] )
                                      {
                                                         if (level.time >= ent->pain_debounce_time)
                                                         {
                                                                            gi.sound(ent, CHAN_VOICE, gi.soundindex("weapons/noammo.wav"), 1, ATTN_NORM, 0);
                                                                            ent->pain_debounce_time = level.time + 1;
                                                         }
                                                         NoAmmoWeaponChange (ent);
                                      }
                                      else
                                      {
 
                                                         damage = TBOLT_DAMAGE;
 
                                                         if (is_quad)
                                                         {
                                                         toasty = 8000;
                                                         damage *= 4;
                                                         }
                                                         else
                                                                            toasty = 2000;
 
                                                         if (ent->client->ps.gunframe == 6) // if its starting. do the initial bolt sound
                                                                            gi.sound(ent, CHAN_WEAPON, gi.soundindex("weapons/lfire.wav"), 1, ATTN_NORM, 0);
 
                                                         gi.sound(ent, CHAN_AUTO, gi.soundindex("weapons/tesla.wav"), .5, ATTN_NORM, 0);
 
                                                         if (ent->waterlevel == 3) //ZZZZZZZZZAAAAAAAAAAAAAAAPPPPPPP!!!!!!
                                                         {
                                                                            ent->client->pers.inventory[ent->client->ammo_index] = 0;
                                                                            T_RadiusDamage(ent, ent, toasty, NULL, toasty, MOD_TBOLT_DISCHARGE);
                                                                            return;
                                                         }
 
 
                                                         AngleVectors (ent->client->v_angle, forward, right, NULL);
 
                                                         VectorScale (forward, -2, ent->client->kick_origin);
                                                         VectorSet(offset, 8, 8, ent->viewheight - 8);
 
                                                         P_ProjectSource (ent->client, ent->s.origin, offset, forward, right, start);
                                                         fire_lightning (ent, start, forward, damage);
 
                                                         PlayerNoise(ent, start, PNOISE_WEAPON);
 
                                                         if (! ( (int)dmflags->value & DF_INFINITE_AMMO ) )
                                                                            ent->client->pers.inventory[ent->client->ammo_index] -= TBOLT_CELLS;
                                      }
 
                   }
 
                   ent->client->ps.gunframe++;
                   if (ent->client->ps.gunframe == 12 && ent->client->pers.inventory[ent->client->ammo_index])
                                      ent->client->ps.gunframe = 7;
}
 
void Weapon_Thunderbolt (edict_t *ent)
{
                   static int   pause_frames[]         = {0};
                   static int   fire_frames[]              = {6, 7, 8, 9, 10, 11};
 
                   Weapon_Generic (ent, 5, 20, 49, 53, pause_frames, fire_frames, Weapon_Thunderbolt_Fire);
}
//OM-LGUN-END
 


Part 6: G_CLIENT.C

Now find the obituaries, and add these reqpectively:
Under if attacker = self ...

 
                   //OM-LGUN
                   case MOD_TBOLT_DISCHARGE:
                                      message = "made a shocking mistake";
                                      break;
                   case MOD_TBOLT_WATER:
                                      message = "feels the tingle from the electrified liquid";
                                      break;
 


Under if attacker && attacker->client ...

 
                   //OM-LGUN
                   case MOD_TBOLT:
                                      message = "accepts";
                                      message2 = "'s shaft";
                                      break;
                   case MOD_TBOLT_WATER:
                                      message = "feels the tingle from";
                                      message2 = "'s water shock";
                                      break;
                   case MOD_TBOLT_DISCHARGE:
                                      message = "suffers the extreme pain of";
                                      message2 = "'s suicidal discharge";
                                      break;
                   //OM-LGUN-END
 


Part 7: Rebuild

Surely that isn't too hard..

Part 8: Play. Frag. Kill. Fry. Shock. You know!

That should be about it.
If you have any questions, e-mail me.

Editor (nobody)'s note:
You will either have to give this weapon to the player when they spawn, or type "give Thunderbolt" at the console when you are playing singleplayer/cheats are enabled.

-- Credits:
   Tutorial by Omega Maelstrom
 QS Tutorials

-- Important:
   If you do use something from QuakeStyle in your mod, please give us credit.
   Our code is copyrighted, but we give permission to everyone to use it in any way they see fit, as long as we are recognized.