Flashlight and Lasersight

Since I have just finished and corrected the code for these and I see that no one else has written a tutorial on this, I thought that I would.

To introduce, this tutorial will produce the effects of lasersight and flashlight when implemented. The reason I have written this for BOTH flashlight AND lasersight is that the basic code for the entity and movement are the same and its simpler to write it once, right? You dont have to use both if you dont want to, its just here if you need it.

We will be making changes all across the board here. Both cgame and game will be modified:

g_cmds.c
g_weapon.c

cg_main.c ->really minor
cg_ents.c

g.local.h ->really minor
cg_local.h ->really minor
bg_public.h ->really minor

Just one note before we begin. I am not big on making comments here in this tutorial, but please do. Everywhere you modify the code, make a comment. You do not know (maybe you do) how helpful this can be, so PLEASE do it->it might save you from doing a whole lot of things over again :).

1. Setting Things Up -> the minor things

We first have to declare some functions, variables, and types so lets do it!

A1. Goto g_local.h, line ~490 and insert below:

	void Weapon_HookFree (gentity_t *ent);
	void Weapon_HookThink (gentity_t *ent);			
	void Laser_Gen (gentity_t *ent, int type);
	void Laser_Think( gentity_t *self );

A2. Goto g_local.h, line ~274 and insert below:

	int			lastKillTime;		// for multiple kill rewards

	qboolean	fireHeld;			// used for hook
	gentity_t	*hook;				// grapple hook if out			
	gentity_t	*lasersight;			// lasersight OR flashlight if in use

	// timeResidual is used to handle events that happen every second
	// like health / armor countdowns and regeneration
	int			timeResidual;

B. Goto cg_local.h, line ~568 and insert below:

	qhandle_t	viewBloodShader;
	qhandle_t	tracerShader;
	qhandle_t	crosshairShader[NUM_CROSSHAIRS];			
	qhandle_t	laserShader;


	qhandle_t	lagometerShader;

C. Goto cg_ents.c, line ~7 and insert:

	
	static void CG_LaserSight( centity_t *cent );

D. Goto cg_main.c, line ~585 and insert below:

	cgs.media.tracerShader = trap_R_RegisterShader( "gfx/misc/tracer" );
	cgs.media.selectShader = trap_R_RegisterShader( "gfx/2d/select" );

	for ( i = 0 ; i < NUM_CROSSHAIRS ; i++ ) {
		cgs.media.crosshairShader[i] = trap_R_RegisterShader( va("gfx/2d/crosshair%c", 'a'+i) );
	}			
	
	cgs.media.laserShader = trap_R_RegisterShader( "sprites/laser" );
	cgs.media.backTileShader = trap_R_RegisterShader( "gfx/2d/backtile" );
	cgs.media.noammoShader = trap_R_RegisterShader( "icons/noammo" );
	

E. Goto bg_public.h, line ~585 and insert below:

	ET_TELEPORT_TRIGGER,
	ET_INVISIBLE,
	ET_GRAPPLE,				// grapple hooked on wall			
	ET_LASER,				// lasersight entity type

	ET_EVENTS

What we have done here is: (A1) declared the laser spawner/despawner function. (A2) Added a subordinate entity to gclient, so we can from gclient structure control the client’s laser. (B) created a new shader for our lasersight in cgame. (C) Declared the laser sight draw function. (D) Told cgame what cg.media to assign to our shader. (E) created a new entity type ET_LASER.

2. Creating the Command

The first thing to do here is to give the client the ability to turn on or off the laser. If you are making a mod, you will most likely change this so that the client cannot simply type „laser“ in the console, rather make it a powerup or something.

As we all remember from before: go into g_cmds.c down to line ~1220 to the else if statements, and insert this code so that typing „laser“ and „flashlight“ in the console turn on/off the laser/flashlight.

	else if (Q_stricmp (cmd, "gc") == 0)
		Cmd_GameCommand_f( ent );
	else if (Q_stricmp (cmd, "setviewpos") == 0)
		Cmd_SetViewpos_f( ent );
			
	else if (Q_stricmp (cmd, "laser") == 0)
		Laser_Gen( ent, 1 );//1=Laser, 2=Flashlight
	else if (Q_stricmp (cmd, "flashlight") == 0)
		Laser_Gen( ent, 2 );

Basically, we have created two commands, which repond to console commands /laser and /flashlight, running our Laser_Gen function with 1 and 2, signifying flashlight and laser, repsectively.

3. Create the Stupid Entity

Go into g_weapon.c, at the very bottom: you can put it anywhere, but I place it here because it kinda pertains to weapon:

				
/*
============
Laser Sight Stuff

	Laser Sight / Flash Light Functions
============
*/

void Laser_Gen( gentity_t *ent, int type )	{
	gentity_t	*las;
	int oldtype;

	//Get rid of you?
	if ( ent->client->lasersight) {
		  oldtype = ent->client->lasersight->s.eventParm;
		  G_FreeEntity( ent->client->lasersight );
		  ent->client->lasersight = NULL;
		  if (oldtype == type)
			  return;
	}

	las = G_Spawn();

	las->nextthink = level.time + 10;
	las->think = Laser_Think;
	las->r.ownerNum = ent->s.number;
	las->parent = ent;
	las->s.eType = ET_LASER;

	//Lets tell it if flashlight or laser
	if (type == 2)	{
		las->s.eventParm = 2; //tells CG that it is a flashlight
		las->classname = "flashlight";
	}
	else {
		las->s.eventParm = 1; //tells CG that it is a laser sight
		las->classname = "lasersight";
	}

	ent->client->lasersight = las;
}

void Laser_Think( gentity_t *self )	{
	vec3_t		end, start, forward, up;
	trace_t		tr;

	//If Player Dies, You Die -> now thanks to Camouflage!
	if (self->parent->client->ps.pm_type == PM_DEAD)  {
		G_FreeEntity(self);
		return;
	}

	//Set Aiming Directions
	AngleVectors(self->parent->client->ps.viewangles, forward, right, up);
	CalcMuzzlePoint(self->parent, forward, right, up, start);
	VectorMA (start, 8192, forward, end);

	//Trace Position
	trap_Trace (&tr, start, NULL, NULL, end, self->parent->s.number, MASK_SHOT );

	//Did you not hit anything?
	if (tr.surfaceFlags & SURF_NOIMPACT || tr.surfaceFlags & SURF_SKY)	{
		self->nextthink = level.time + 10;
		trap_UnlinkEntity(self);
		return;
	}

	//Move you forward to keep you visible
	if (tr.fraction != 1)	VectorMA(tr.endpos,-4,forward,tr.endpos);

	//Set Your position
	VectorCopy( tr.endpos, self->r.currentOrigin );
	VectorCopy( tr.endpos, self->s.pos.trBase );

	vectoangles(tr.plane.normal, self->s.angles);

	trap_LinkEntity(self);

	//Prep next move
	self->nextthink = level.time + 10;
}


Well that will do all the stuff that creates, deletes, and moves our entity. The first function generates the entity and sets it into thinking every 1/100 second, and it thinks in the bottom function by tracing the player’s view, and setting the laser’s position at the end of the trace. Just some notes: I like to sneak variables into unused slots whenever possible to prevent over-modification (like the weapon drop function -> there’re no need for all those changes he made). This is the reason why I have chosen s.eventparm to sneak whether its a lasersight or flashlight. Then, in cgame, we will be able to differentiate and draw the thing.

4. Color it, Light it, Whatever

These are modifications to cgame so that an ET_LASER will either create a sprite, or a light, whichever it actually is. To start, we have to modify the switch statement in cg_ents.c at about ln. 712.

	case ET_SPEAKER:
		CG_Speaker( cent );
		break;
	case ET_GRAPPLE:
		CG_Grapple( cent );
		break;			
	case ET_LASER:
		CG_LaserSight( cent );
		break;
	}
}

This just tells us that when we have an ET_LASER centity, we will call CG_LaserSight, which will be responsible for actually adding the graphic to the scene. At the bottom of cg_ents.c:

/*
==================
CG_LaserSight
  Creates the laser
==================
*/

static void CG_LaserSight( centity_t *cent )  {
	refEntity_t			ent;


	// create the render entity
	memset (&ent, 0, sizeof(ent));
	VectorCopy( cent->lerpOrigin, ent.origin);
	VectorCopy( cent->lerpOrigin, ent.oldorigin);

	if (cent->currentState.eventParm == 1)
	{
		ent.reType = RT_SPRITE;
		ent.radius = 2;
		ent.rotation = 0;
		ent.customShader = cgs.media.laserShader;
		trap_R_AddRefEntityToScene( &ent );
	}
	else	{
		trap_R_AddLightToScene(ent.origin, 200, 1, 1, 1);
	}

	
}

This function is fairly simple. What it does first is that it differentiates the laser and flashlight. If it is a lasersight, then it sets the refentity to a sprite with our shader. If not, it simply adds a white light with intensity 200 (which turns out to be directly related to the radius of the spot). Basically, this would be the end of the story except for one thing -> where we gonna get our shader?

5. Screw it, We’re Done

Yeah I wish. Fortunately, you can go ahead and compile, cause we’re done with the source. Now we have to make 1. your shader and 2. your graphic. This should be a piece of cake for all you shader people out there. If not, then create a file in the /scripts directory with extension .shader (or append a preexisting shader). Here it is:

sprites/laser
{
	cull disable
	{
		clampmap sprites/laser.jpg
		blendfunc GL_ONE GL_ONE
        }
}

Now we have to make our image. Fortunately, for all y’all who cant make any images, I have one ready for you: For those who cannot read the above shader, you won’t know where to put it, and I’m not gonna say, you should be able to figure it out :). Well guys, that’s it. We’re done, so go play your game with your laser sight, and make whatever modifications you need to make, cause c-mon, you cant just copy and paste, you have to learn. So anyway, thanks for letting me write this and I think maybe this will be worth something to someone. Oh, BTW, thanks to Quake DeveLS for letting me look over the Q2 laser. It really made me see why mine wasnt working (dang linkentity).

Good Luck all coders -> Drive C: