QuakeZone
MOD Coding Tutorials
Quake II Coding Tutorials!

Introduction
Pre-requisites
Code
Notes
Exercises
Quake II Coding Tutorials

20 Laser Sight and Enhancements

Introduction Introduction
Related Links

18 Team Weapon Enhancements
Quakestyle's tutorials (for flashlight)
Inside3D's tutorials (for auto-aiming)
QDevel's tutorials (for wall-piercing railguns)

This tutorial shows you how to add a laser sight to your weapons. You know the kind of thing, the little red dot that appears on your opponent's chest micro- seconds before you frag them.

This is probably very similar to the laser sight tutorial on QDevels, but that uses a small gib model whereas I use the flashlight model with a red glow effect to give a more realistic effect.

The observant amongst you will note that this tutorial is extremely similar to Quakestyle's flashlight tutorial, in fact so similar as to be almost identical, and this is indeed the case.

This tutorial goes a bit further and adds some enhancements to the laser sight. In team rocket, a rail gun can replace rocket launchers in my team weapon enhancement, and I wanted to make sure I could create corresponding Tech enhancements to the railgun. The "multi" and "accelerator" techs were obvious enough, and were similar changes as I made for rockets.

For the "homing" Tech, I made railguns auto-aiming (see Inside3D's tutorial on how to do that). So when homing mode is operative, the laser sight is operative and will land on any opponents standing in front of you.

For the "blister" Tech, I implemented the QDevel's wall-piercing railgun. This means that the laser sight can target opponents in front but out of sight, and the red dot appears on the wall indicating their position. Lethal.

For team weapon enhancement, I only wanted to make the laser sight work on the railgun. There is an optional change therefore to show how to activate/de-activate the laser sight when a player switches team, or the host changes team weapons.

Pre-requisites Pre-requisites

  • tutorial 18, team weapon enhancement, if required
  • Inside3D's auto-aiming tutorial if required (this is written for blasters, but is easily adapted to any other weapon)
  • QDevel's wall-piercing railgun tutorial
Code Code


g_local.h

Add the following lines as indicated:

1. Add the following lines to structure client_persistant_t:

// PJT - laser sight <-- start of insert
	qboolean    homing_state;		// PJT - used to activate laser sight
	qboolean    blister_state;		// PJT - used to activate wall piercing (optional)
// PJT               <-- end of insert


2. Add the following line to the of structure edict_s:

	edict_t 	*lasersight;	// PJT - auto-aim rail - laser sight
		

p_client.c

Add the following lines to the start of player_die:

    // PJT - lasersight
    if ( self->lasersight )
    {
        G_FreeEdict(self->lasersight);
        self->lasersight = NULL;
    }
    // PJT
		
Add the following 2 new procedures to the end of the file. Read the notes in the comments about activating auto-aiming for the laser sight in lasersight_think:


/*
===============
lasersight_think
===============
*/
void lasersight_think (edict_t *self)
{
    vec3_t 	start,end,endp,offset;
    vec3_t 	forward,right,up;
    trace_t tr;

    // Note: comment out the following line if auto-aiming is required
    AngleVectors (self->owner->client->v_angle, forward, right, up);
    // Note: uncomment the following line if auto-aiming is required 
    // mod_GetLeadoffVec(self->owner, self->owner->s.origin, 5000, 1000, false, forward);
    VectorSet(offset,0 , 0, self->owner->viewheight-10);
    G_ProjectSource (self->owner->s.origin, offset, forward, right, start);
    VectorMA(start,8192,forward,end);
    tr = gi.trace (start,NULL,NULL, end,self->owner,CONTENTS_SOLID|CONTENTS_MONSTER|CONTENTS_DEADMONSTER);
    if (tr.fraction != 1)
    {
        VectorMA(tr.endpos,-4,forward,endp);
        VectorCopy(endp,tr.endpos);
    }
    if ((tr.ent->svflags & SVF_MONSTER) || (tr.ent->client))
    {
        if ((tr.ent->takedamage) && (tr.ent != self->owner))
        {
            self->s.skinnum = 1;
        }
    }
    else
        self->s.skinnum = 0;

    vectoangles(tr.plane.normal,self->s.angles);
    VectorCopy(tr.endpos,self->s.origin);
    gi.linkentity (self);
    self->nextthink = level.time + 0.1;
}


/*
===============
lasersight_make
===============
*/
void lasersight_make(edict_t *self)
{
    vec3_t    start,forward,right,end;

    if ( self->lasersight )
    {
        G_FreeEdict(self->lasersight);
        self->lasersight = NULL;
        return;
    }

    AngleVectors (self->client->v_angle, forward, right, NULL);
    VectorSet(end,100 , 0, 0);
    G_ProjectSource (self->s.origin, end, forward, right, start);
    self->lasersight = G_Spawn ();
    self->lasersight->owner = self;
    self->lasersight->movetype = MOVETYPE_NOCLIP;
    self->lasersight->solid = SOLID_NOT;
    self->lasersight->classname = "lasersight";
    self->lasersight->s.modelindex = gi.modelindex ("models/objects/flash/tris.md2");
    self->lasersight->s.skinnum = 0;

	self->lasersight->s.renderfx = RF_SHELL_RED; 	// make positron rockets red.
  	self->lasersight->s.effects |= EF_COLOR_SHELL;
	self->lasersight->s.effects |= 0x10000000;//transparency
    // self->flashlight->s.effects |= EF_HYPERBLASTER;	// original flashlight tutorial

    self->lasersight->think 	= lasersight_think;
    self->lasersight->nextthink = level.time + 0.1;
}

// PJT
		
If you have installed the team weapon mode, add the following lines to the end of procedure AssignWeapon:

	// PJT rail laser sight
	// 1. deactivate for rocket teams
	if ( (self->client->resp.teamrail == 0) && self->lasersight )
    	{
       	G_FreeEdict(self->lasersight);
       	self->lasersight = NULL;
       	return;
    	}
	// 2. activate for rail teams if required
	if ( (self->client->resp.teamrail == 1) && self->client->pers.homing_state && !self->lasersight)
		lasersight_make(self);
	// PJT	
		

g_cmds.c

Add the following lines to ClientCommand as indicated:

// PJT - enable laser sight	<-- start of insert
	else if (Q_stricmp (cmd, "homing") == 0)
		Cmd_Homing_f (ent);
// PJT				<-- end of insert

// PJT - enable wall piercing 	<-- start of insert (optional)
	else if (Q_stricmp (cmd, "blister_on") == 0)
		Cmd_Blister_on_f (ent);
// PJT				<-- end of insert
		
Then insert the following new procedures:

//PJT - activate auto-aiming
/*
=================
Cmd_Homing_f
CCH: whole new function for adjusting homing missile state
=================
*/
void Cmd_Homing_f (edict_t *ent)
{
	int		i;

	// uncomment following lines if yuo need a pick-up first
	// PJT - tech pickups
	// if (!ent->client->pers.inventory[ITEM_INDEX(FindItem("Homing Tech"))])
	// {
	//	gi.cprintf (ent, PRINT_HIGH, "Homing Tech required.\n");
	//	return;
	// }
	// PJT

	i = ent->client->pers.homing_state;

	switch (i)
	{
	case 1:
		ent->client->pers.homing_state = 0;
		break;
	case 0:
	default:
		ent->client->pers.homing_state = 1;
		break;
	}
	// PJT - rail homing laser sight
	if (ent->client->resp.teamrail == 1)
		lasersight_make(ent);
	// PJT

}
// PJT	

//PJT activate wall-piercing railgun

/*
=================
Cmd_Blister_on_f
PJT: whole new function for adjusting blister missile state
=================
*/
void Cmd_Blister_on_f (edict_t *ent)
{
	int		i;

	// uncomment following lines if yuo require a pick-up first
	// PJT - tech pickups
	//if (!ent->client->pers.inventory[ITEM_INDEX(FindItem("Blister tech"))])
	//{
	//	gi.cprintf (ent, PRINT_HIGH, "Blister Tech required.\n");
	//	return;
	//}
	// PJT

	i = ent->client->pers.blister_state;

	switch (i)
	{
	case 1:
		ent->client->pers.blister_state = 0;
		break;
	case 0:
	default:
		ent->client->pers.blister_state = 1;
		break;
	}
}
// PJT
		

p_weapon.c

If you have implemented Inside3D's auto-aiming tutorial, and have also added wall piercing slugs, amend procedure mod_GetLeadoffVec as follows:

	while (other) {		// PJT <-- locate this line
		// PJT following line amended
		if (((other->svflags & SVF_MONSTER) || (strcmp(other->classname, "LaserDrone") == 0) || other->client)
			&& other->health>0 && other!=self) { /*might have to modify these*/
			// PJT - target out of sight opponents for wall-piercing railgun
			if (infront(self, other))
			  if ( (visible(self, other) && !self->client->pers.blister_state) ||
                 		self->client->pers.blister_state)
			  {
			// PJT	
		
Notes Notes to code


g_local.h
  • homing_state and (optionally) blister_state are 2 new flags in the persistant (across levels) part of the client structure, for activating auto-aiming and wall-piercing modes respectively

p_client.c
  • player_die is amended to switch off the laser sight on player death
  • lasersight_think points the red dot in front of the player, and you can uncomment the call to mod_GetLeadoffVec to target the nearest player
  • lasersight_make creates the red dot, using the flashlight model and a red shell effect
  • AssignWeapon was created by the Team Weapon tutorial, to give the appropriate weapon to each player when he joins or switches teams. It has been modified to activate the laser sight if a player now holds the railgun and has homing mode swithced on, and de-activate it if he now holds a rocket launcher.

g_cmds.c
  • 2 new commands are added to switch on auto-aiming mode. Each commands calls a separate procedure which sets the corresponding flag in the client structure (see g_local.h above)
  • when auto-aiming is switched, lasersight_make is called to activate or de-activate the laser sight if the player is holding a railgun

p_weapon.c
  • mod_GetLeadoffVec was added by Inside3D's auto-aiming tutorial to return the entity and vector of a targeted player, if any. This is called by lasersight_think to enable the laser sight to auto-aim
  • it has been modified to target players who may be out of sight, by not checking the infront function if wall-piercing ("blister_state") is on
Exercises Exercises

  • change the colour of the dot, say, per team?


Page last updated 7th April 2001