[Tutorial] Railgun

Discuss programming in the QuakeC language.
Post Reply
Dr. Shadowborg
InsideQC Staff
Posts: 1120
Joined: Sat Oct 16, 2004 3:34 pm

[Tutorial] Railgun

Post by Dr. Shadowborg »

Okay, here's how to do a railgun in QuakeC. I'm going to present this with the assumption that you already know how to compile a progs.dat, etc. This is provided in a cut and paste with comments format. Read the comments if you want to know how it works.

Open your weapons.qc file, and add this:

Code: Select all

void(float damage) W_FireRailgun =
{
local entity tempen;
local vector orgen, orgo, dir, dorg;
local float donerail, rlooplimit;

// tempen = temp entity, used for self shuffling.
// orgen = origin end
// orgo = start origin for rail
// dir = aim direction
// dorg = damage origin, used for particles, effects, etc.
// rlooplimit = runaway loop limiter, breakpoint for end of rail

	rlooplimit = 20; // only allow 20 hits

// lets make those v_forward, v_up, and v_right vectors
	makevectors(self.v_angle);

// Railgun is not free of charge.
	self.currentammo = self.ammo_cells = self.ammo_cells - 1;

// Lets make a sound
	sound (self, CHAN_AUTO, "weapons/lstart.wav", 1, ATTN_NORM);

// punch the screen for recoil
	self.punchangle_x = -4;

// Set our rail origin point.
	orgo = self.origin + self.view_ofs + v_up*-6;

// set our aiming direction 
	dir = aim(self, 100000); //v_forward;

// start a traceline
	traceline(orgo, orgo+dir*2048, FALSE, self);

// set damage point incase we didn't hit anything
	dorg = trace_endpos - dir*4;

// set up our loop, if we're done (i.e. hit a wall) or 
// hit our loop limit, exit
	if(trace_ent.takedamage)
	{
		while(rlooplimit > 0)
		{
// if the trace_ent can take damage...
			if(trace_ent.takedamage && trace_ent != world)
			{  
// set our endpoint 4 units ahead
			 orgen = trace_endpos + dir*4;
// set our damage origin 4 units back
			 dorg = trace_endpos - dir*4;
// spawn some blood 
			 SpawnBlood (dorg, '0 0 0', 30);
// do the damage
			 T_Damage (trace_ent, self, self, damage);
// play a sound
			 sound (trace_ent, CHAN_WEAPON, "weapons/lhit.wav", 1, ATTN_NORM);
//	Don't pentetrate buttons or anything SOLID_BSP
			 if(trace_ent.solid == SOLID_BSP)
			  rlooplimit = 0;
			}
			else
			{
// didn't hit anything?
// set our damage origin back 4 units
			 dorg = trace_endpos - dir*4;

// Done tracing the rail shot
			 rlooplimit = 0;
			}
// traceline doesn't register hits against self, so we store self in
// tempen, then set the self to trace_ent; We can now fire a new 
// trace, then reset self back to the player who fired the rail, 
// then we reduce the loop counter
		 tempen = self;
		 self = trace_ent;
		 traceline(orgen, orgen + dir*2048, FALSE, self);
		 self = tempen;
		 rlooplimit = rlooplimit - 1;
		}
	}

// 	spawn an explosion sprite if we have impacted a wall
	if(!(trace_fraction == 1.0))
	{ 
	 newmis = spawn();
	 newmis.solid = SOLID_NOT;
	 newmis.takedamage = DAMAGE_NO;
	 setmodel(newmis, "progs/s_explod.spr");
	 setsize(newmis, '0 0 0', '0 0 0');
	 setorigin(newmis, dorg);
	 newmis.think = BecomeExplosion;
	 newmis.nextthink = time;	
	}

// Spawn an entity for the effect
	newmis = spawn();
	newmis.solid = SOLID_NOT;
	setorigin(newmis, orgo);
	newmis.think = SUB_Remove;
	newmis.nextthink = time + 0.1;

// generate an effect from the entity to the endpoint
	WriteByte (MSG_BROADCAST, SVC_TEMPENTITY);
	WriteByte (MSG_BROADCAST, TE_LIGHTNING2);
	WriteEntity (MSG_BROADCAST, newmis);
	WriteCoord (MSG_BROADCAST, newmis.origin_x);
	WriteCoord (MSG_BROADCAST, newmis.origin_y);
	WriteCoord (MSG_BROADCAST, newmis.origin_z);
	WriteCoord (MSG_BROADCAST, dorg_x);
	WriteCoord (MSG_BROADCAST, dorg_y);
	WriteCoord (MSG_BROADCAST, dorg_z);
};
Right above this:

Code: Select all

/*
===============================================================================

PLAYER WEAPON USE

===============================================================================
*/
Now, unless you know how to add / replace new weapons, for testing, we will simply replace the lightning gun with the railgun. Go down to W_Attack() and change this:

Code: Select all

	else if (self.weapon == IT_LIGHTNING)
	{
		player_light1();
		self.attack_finished = time + 0.1;
		sound (self, CHAN_AUTO, "weapons/lstart.wav", 1, ATTN_NORM);
	}
to this:

Code: Select all

	else if (self.weapon == IT_LIGHTNING)
	{
		player_shot1();
		W_FireRailgun(100); // 100 damage
		self.attack_finished = time + 0.8;
	}
Compile, then run quake. Line up those shots and enjoy! :)
Post Reply