Turrets: Part1


Welcome to the first part of my tutorials on how to add auto defence turrets to quake 3

This series of tutorials will show you how to add user placeable turrets to quake 3. All the code will be added to g_cmds.c (game) for now. I may get round to showing you how to add it in a new file in a later tutorial.

Some of the credit has to go to Inolen for coproduction the code base


open up g_local.h (game) Here we will add a new variable to store the turrets spawn location, and the direction it is to fire in

Find the code below in the file

	gitem_t		*item;			// for bonus items

	qboolean	botDelayBegin;

};

add the variable vec3_t turloc; to make the code look like

	gitem_t		*item;			// for bonus items

	qboolean	botDelayBegin;

// Fuzzysteve turret location
	vec3_t		turloc;
};

Thats us done here


Now open up g_cmds.c (game) and scroll down to the bottom

Find the code below

	else if (Q_stricmp (cmd, "gc") == 0)
		Cmd_GameCommand_f( ent );
	else if (Q_stricmp (cmd, "setviewpos") == 0)
		Cmd_SetViewpos_f( ent );
	else
		trap_SendServerCommand( clientNum, va("print \"unknown cmd %s\n\"", cmd ) );
}

and modify it to match the code below

	else if (Q_stricmp (cmd, "setturretlocation") == 0)
		Cmd_SetTurLoc_f( ent );
	else if (Q_stricmp (cmd, "spawnturret") == 0)
		Cmd_SpawnTurret_f( ent );
	else if (Q_stricmp (cmd, "setviewpos") == 0)
		Cmd_SetViewpos_f( ent );
	else
		trap_SendServerCommand( clientNum, va("print \"unknown cmd %s\n\"", cmd ) );
}

Here we have modified the code so that if you enter \setturretlocation or \spawnturret a function will be called. the functions to be called don't exist yet. they are added in the next section


Now , at the top of g_cmds.c (game) paste the code below in, just after #include "g_local.h". I'll explain what things do after the code block

/*
=======================
Turret code. for spawning and seting the spawn location of turrets
=======================
*/

void turret_findenemy( gentity_t *ent){
	gentity_t *target;
	vec3_t	eorg,org;
	int		j,rad;

/*
This function goes throught the entity list and check the distance to every entity untill it finds one that fits these rules:

it's not the turret itself.
it's not the creator of the turret (ent->parent)
it's still alive;
it's within a certain distance of the turret (set by rad. rad is current;y set to 500 q3 units. if you change it here, you have to change it in turretthink)
it has to be a client (player or bot)

if it fits these rules, the entity found is set as the turrets enemy.
if no entity is found, enemy is set to NULL;

This function will be refined in a later tutorial
*/


	rad=500;
	VectorCopy(ent->r.currentOrigin,org);

	target = g_entities;

	for (; target < &g_entities[level.num_entities]; target++)
	{
		if (target==ent)
			continue;
		if (target==ent->parent)
			continue;
		if (target->health<0)
			continue;
		if (!target->inuse)
			continue;
		for (j=0; j<3; j++)
			eorg[j] = org[j] - (target->r.currentOrigin[j] + (target->r.mins[j] + target->r.maxs[j])*0.5);
		if (VectorLength(eorg) > rad)
			continue;
		if(!target->client)
			continue;
		ent->enemy=target;
		return;
	}

	ent->enemy=NULL;
}



void turret_trackenemy( gentity_t *ent){
/*
Simple one this
it merely works out the angle to the enemy, then sets ent->turloc (in this case being used to store the angle (as a normalised difference vector)) to the enemy, and the turrets angles (ent->s.apos.trbase) to face the enemy
it then relinks the turret to finalise the changes
This is probably the final version.
*/

vec3_t dir;


VectorSubtract(ent->enemy->r.currentOrigin,ent->r.currentOrigin,dir);
VectorNormalize(dir);
VectorCopy(dir,ent->turloc);
vectoangles(dir,dir);
VectorCopy( dir,ent->s.apos.trBase );
trap_LinkEntity (ent);

}

void turret_fireonenemy( gentity_t *ent){
/*
Fires the plasma at the enemy. Doesn't check to see if it will hit, and doesn't lead the enemy at all.

The event is being added so you can see what is happening (the models and such are handled by cgame. the event just tells it to display them

ent->count is there to stop the gun firing as fast as the turret thinks. decrease the 200 to make it fire faster.

this function will be refined in a later tutorial
*/

fire_plasma( ent->parent, ent->r.currentOrigin, ent->turloc );
G_AddEvent( ent, EV_FIRE_WEAPON, 0 );
ent->count=level.time+200;
}


void turret_think( gentity_t *ent){
/*

/*
This function is called very regularly.
if the turret has no enemy, it tries to find one.
if the enemy is dead it tries to find a new one
if the enemy has moved out of range it tries to find a new one (the 500 is the number of quake units the enemy should be within)

The turret is then told to track onto the enemy
If a certain amount of time has passed since it last fired, it fires again

Very possible the final version
*/

vec3_t distance;



ent->nextthink=level.time+10;


if (!ent->enemy)
	turret_findenemy(ent);
if (!ent->enemy)
	return;
if (ent->enemy->health<0)
	turret_findenemy(ent);
if (!ent->enemy)
	return;
VectorSubtract(ent->r.currentOrigin,ent->enemy->r.currentOrigin,distance);
if (VectorLength(distance)>500)
	turret_findenemy(ent);

if(!ent->enemy)
	return;

turret_trackenemy(ent);
if (ent->count<level.time)
turret_fireonenemy(ent);

}



void Cmd_SpawnTurret_f( gentity_t *ent ){
/*
This function spawns the turret at the location set.
I'll comment the individual lines
*/

	gentity_t *turret; 	// The object to hold the turrets details.

	turret=G_Spawn();
	turret->parent=ent;
	turret->eventTime=200; // makes the firing sequence go away after 200 milliseconds.
	turret->s.weapon=WP_PLASMAGUN; // which weapon will be fired (graphics only)
	turret->classname="turret";	// not really needed yet. it may be later.
	turret->s.modelindex = G_ModelIndex("models/weapons2/machinegun/machinegun.md3");
	turret->model = "models/weapons2/machinegun/machinegun.md3";
	turret->s.modelindex2 = G_ModelIndex("models/weapons2/machinegun/machinegun.md3");
// the three lines above set the model to be displayed. currently its just the machinegun.
	VectorSet( turret->r.mins, -15, -15, -15 );
	VectorSet( turret->r.maxs, 30, 30, 30);
// these two lines set the size of the turret. doesn't do anything as the turret is not solid, but this will change
	turret->think=turret_think; // what the turret does
	turret->nextthink=level.time+100; // when the turret will activate
	G_SetOrigin( turret, ent->turloc ); // sets where the turret is
	trap_LinkEntity (turret); // adds the finalized turret.

}


void Cmd_SetTurLoc_f( gentity_t *ent ){
/*
This just sets the variable holding the turrets location and prints it out on screen (for debugging. you can drop the trap servercommand line if you want)
*/

VectorCopy(ent->r.currentOrigin,ent->turloc);
trap_SendServerCommand( ent-g_entities, va("print \"Location Set. %f %f %f \n\"",ent->r.currentOrigin[0],ent->r.currentOrigin[1],ent->r.currentOrigin[2]));

}

This code will allow a turret to be spawned which will fire plasma at anyone but the owner. Comments have been added for any bits of code which aren't obvious.


This code should allow you to add very basic (and not particularly nice looking) turrets to the game. They will be refined in the other tutorials


To the next tutorial in this series
Back to the tutorials
Mail me