Turrets:Part 5
Optional extras


Couple of extras here; A shielded turret where the shield recharges slowly, and a cloaking turret which uncloaks to fire. They both use a similar method, and both require changes to both the cgame module. For now they don't cost anything to use, that comes later.


GAME

Well, the stuff that goes in the game folder anyway.

Open up bg_public.h. Scroll down till you find the list below

typedef enum {
	ET_GENERAL,
	ET_PLAYER,
	ET_ITEM,
	ET_MISSILE,
	ET_MOVER,
	ET_BEAM,
	ET_PORTAL,
	ET_SPEAKER,
	ET_PUSH_TRIGGER,
	ET_TELEPORT_TRIGGER,
	ET_INVISIBLE,
	ET_GRAPPLE,				// grapple hooked on wall

	ET_EVENTS				// any of the EV_* events can be added freestanding
							// by setting eType to ET_EVENTS + eventNum
							// this avoids having to set eFlags and eventNum
} entityType_t;

We're wanting to add a new type of entity (the turret) so it can get it's own special effects. change the list so it looks like the one below (be careful of commas)

typedef enum {
	ET_GENERAL,
	ET_PLAYER,
	ET_ITEM,
	ET_MISSILE,
	ET_MOVER,
	ET_BEAM,
	ET_PORTAL,
	ET_SPEAKER,
	ET_PUSH_TRIGGER,
	ET_TELEPORT_TRIGGER,
	ET_INVISIBLE,
	ET_GRAPPLE,				// grapple hooked on wall
	ET_TURRET,				// New type of entity. turrets. FuzzySteve 2000.

	ET_EVENTS				// any of the EV_* events can be added freestanding
							// by setting eType to ET_EVENTS + eventNum
							// this avoids having to set eFlags and eventNum
} entityType_t;

That's us done with this file. This does change the cgame module so make sure that you recompile it if you want to test (I would leave it for now)

Next up is g_cmds.c. The changes here are fairly large, as I have to make the turrets be destroyable (give them health and the functions so they can be hurt and die) as well as adding the code so they either cloak and decloak, or regenerate. I've set the code to work from the base's think (as its unused) and the turret's type (normal, shielded, cloaking (cloaked) and cloaking(uncloaked)) is stored in s.time2 (an unused field thats transmitted to the client)

As the changes are so big, here's all the code. Just replace what you have for the turrets with this.

/*
=======================
Turret code. for spawning and seting the spawn location of turrets
=======================
*/
#define RANGE 500

#define HARC 90
#define DARC 10
#define UARC 45




qboolean checktarget(gentity_t *firer,gentity_t *target){
vec3_t 		distance,forward;
trace_t         trace;
int		angle;

/*
returns qfalse if the target is not valid. returns qtrue if it is
*/

if (!target) // Do we have a target?
	return qfalse;
if (!target->inuse) // Does the target still exist?
	return qfalse;
if (target==firer) // is the target us?
	return qfalse;
if(!target->client) // is the target a bot or player?
	return qfalse;
//if (target==firer->parent) // is the target the person that created the turret?
//	return qfalse;
//if (OnSameTeam(firer->parent, target)) // is the target one of us?
//	return qfalse;

if (target->health<0) // is the target still alive?
	return qfalse;

VectorSubtract(target->r.currentOrigin,firer->r.currentOrigin,distance);
if (VectorLength(distance)>RANGE) // is the target within range?
	return qfalse;

trap_Trace (&trace, firer->s.pos.trBase, NULL, NULL, target->s.pos.trBase, firer->s.number, MASK_SHOT );
if ( trace.contents & CONTENTS_SOLID ) // can we see the target?
	return qfalse;
/*
The last two checks are done last as they require more processing power than the others.
this order is just better from a proccesing load perspective
*/

	vectoangles (distance,distance);
	VectorSubtract(firer->centerpoint,distance,distance);
	angle=abs((int)distance[1]);
	while (angle>=360)
	{
	angle-=360;
	}
	if ((angle>=HARC) && (angle<=(360-HARC)))
		return qfalse;
	angle=abs((int)distance[0]);
	while (angle>=360)
	{
	angle-=360;
	}
	if ((angle>UARC) && (angle<(360-DARC)))
		return qfalse;

return qtrue;
}




void turret_findenemy( gentity_t *ent){
	gentity_t *target;

	target = g_entities;

	for (; target < &g_entities[level.num_entities]; target++)
	{
		if(!checktarget(ent,target))
			continue;
		ent->enemy=target;
		return;
	}

	ent->enemy=NULL;
}



void turret_trackenemy( gentity_t *ent){
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.angles);
VectorCopy(dir,ent->r.currentAngles);
VectorCopy( dir,ent->s.apos.trBase );
trap_LinkEntity (ent);

}

void turret_fireonenemy( gentity_t *ent){
fire_plasma( ent->activator, ent->r.currentOrigin, ent->turloc );
G_AddEvent( ent, EV_FIRE_WEAPON, 0 );
ent->count=level.time+200;
// decloaks a cloaked turret when firing.
if (ent->s.time2==2){
	ent->s.time2=3;
	ent->chain->s.time2=3;
	}
}




void turret_think( gentity_t *ent){

ent->nextthink=level.time+10;


if (!checktarget(ent,ent->enemy))
	turret_findenemy(ent);
if(!ent->enemy)
	return;
turret_trackenemy(ent);
if (ent->count<level.time)
	turret_fireonenemy(ent);
}

void Base_think(gentity_t *ent){

// for shielded turrets. regenerates health to 400 at 10 health a second
if ((ent->s.time2==1)&(ent->health<400)){
	ent->health+=1;
	ent->nextthink=level.time+100;
}

// recloaks a turret without a valid target. checks once a second
if ((ent->s.time2==3)&(!ent->chain->enemy))
	{
	ent->s.time2=2;
	ent->chain->s.time2=2;
	ent->nextthink=level.time+1000;
	}


}

void createturretgun(gentity_t *ent){
	gentity_t *turret; 	// The object to hold the turrets details.
	int			num;
	int			touch[MAX_GENTITIES];

// code to check there is noone within the base before making it solid
	vec3_t		mins, maxs;

	VectorAdd( ent->r.currentOrigin, ent->r.mins, mins );
	VectorAdd( ent->r.currentOrigin, ent->r.maxs, maxs );
	num = trap_EntitiesInBox( mins, maxs, touch, MAX_GENTITIES );
	if (num>1)
	{
	ent->nextthink=level.time+1000;
	return;
	}

	ent->nextthink=level.time+100; // sets up the thinking for the cloaking or regeneration/
	ent->think=Base_think; // handles cloaking or regeneration
	ent->clipmask = CONTENTS_SOLID | CONTENTS_PLAYERCLIP;
	ent->r.contents = CONTENTS_SOLID;
	turret=G_Spawn();
	turret->parent=ent->parent;
	turret->chain=ent;
	ent->chain=turret;
	turret->s.eType=ET_TURRET;
	turret->s.time2=0;
	turret->eventTime=200;
	turret->s.number = turret - g_entities;
	turret->s.weapon=WP_PLASMAGUN;;
	turret->classname="turret";	
	turret->s.modelindex = G_ModelIndex("models/objects/turret/gun1.md3");
	turret->model = "models/objects/turret/gun1.md3";
	turret->s.modelindex2 = G_ModelIndex("models/objects/turret/gun1.md3");
	turret->think=turret_think;
	turret->nextthink=level.time+100;
	G_SetOrigin( turret, ent->r.currentOrigin );
	VectorCopy(ent->s.apos.trBase,turret->s.apos.trBase);
	VectorCopy(turret->s.apos.trBase,turret->centerpoint);
	trap_LinkEntity (turret);
}

void turret_retaliate(gentity_t *self, gentity_t *attacker, int damage){
// set the guns enemy to the person that shot it. (does not override targeting rules)
if (self->chain)
	self->chain->enemy=attacker;

// this is here to casue the turret to unshield when its taken a certain amount of damage. (enough to reduce health to below 100)
if (self->s.time2==1)
{
if (self->health<100){
	self->s.time2=0;
	if (self->chain)
		self->chain->s.time2=0;
}
}
}



void turret_explode(gentity_t *self, gentity_t *inflictor, gentity_t *attacker, int damage, int mod){
/* This just blows up the base when its destroyed. took me some time to work it out */
	vec3_t dir; // needed by the event being added

	dir[0] = dir[1] = 0;
	dir[2] = 1;
if (self->chain)
	G_FreeEntity(self->chain); // get rid of the gun. // the gun just vanishes
self->s.weapon=WP_ROCKET_LAUNCHER; // to tell it what kind of explosion to use
G_AddEvent( self, EV_MISSILE_MISS, DirToByte( dir ) ); // to tell it to spawn an explosion here
self->freeAfterEvent = qtrue; // so the base goes away after the explosion
}



void Cmd_SpawnTurret_f( gentity_t *ent ){


	gentity_t	*base;
	vec3_t 		forward,up;

	base=G_Spawn();
	base->parent=ent;
	base->s.modelindex = G_ModelIndex("models/objects/turret/base.md3");
	base->model = "models/objects/turret/base.md3";
	base->s.modelindex2 = G_ModelIndex("models/objects/turret/base.md3");
	G_SetOrigin(base,ent->r.currentOrigin);
	VectorSet(base->s.apos.trBase,0,ent->s.apos.trBase[1],0);
	base->think=createturretgun;
	base->health=300; // change this to make the turrets tougher or weaker.
	base->s.eType=ET_TURRET;
	base->s.time2=0;
	base->takedamage=qtrue; // so they can be destoryed
	base->die=turret_explode; // so they actually explode when destroyed
	base->pain=turret_retaliate; // if they are damaged they switch target to the person attacking (if its a valid target)
	base->nextthink=level.time+5000;
	VectorSet( base->r.mins, -15, -15, -20 );
	VectorSet( base->r.maxs, 35, 15, -5);
	trap_LinkEntity (base);

}

s.time2 stores the type of turret. 0 is a normal turret, 1 is a shielded turret, 2 is a cloaked turret, 3 is a cloaked turret thats firing (to let it know to recloak).

For now theres no way, other than to change the code and recompile it, to set it to spawn a different turret. However all you need to do is add a new command and a new variable that control what type gets spawned. Consider it homework (I'm lazy.)

That's us finished with the game module, on to the cgame module


CGAME

Our first change to this has already been made as bg_public gets included in it.

The changes here are restriced to cg_ents.c, so opening it up is a good pace to start

add the code below to that file, just below the #include line.

// fuzzysteve turret effects
static void CG_TURRET(centity_t *cent)
{
	refEntity_t			ent;
	entityState_t		*s1;
	

	s1 = ->currentState;

	memset (&ent, 0, sizeof(ent));

	VectorCopy( cent->lerpOrigin, ent.origin);
	VectorCopy( cent->lerpOrigin, ent.oldorigin);


	ent.frame = s1->frame;
	ent.oldframe = ent.frame;
	ent.backlerp = 0;
	// convert angles to axis
	AnglesToAxis( cent->lerpAngles, ent.axis );
	
	ent.hModel=cgs.gameModels[s1->modelindex];
	// add to refresh list
	switch (s1->time2){
	case 1:
		trap_R_AddRefEntityToScene (&ent); // make the model show up
		ent.customShader=cgs.media.battleSuitShader;
		trap_R_AddRefEntityToScene (&ent); // make the shader show up
		break;	
	case 2:
		ent.customShader=cgs.media.invisShader;
		trap_R_AddRefEntityToScene (&ent); // make the shader show up. no model
		break;
	case 3:
		trap_R_AddRefEntityToScene (&ent); // just add the model (uncloaked cloaking turret)
		break;
	default:
		trap_R_AddRefEntityToScene (&ent); // if something else has happened
	}
}

This is mostly a copy of CG_GENERAL. Its fairly easy to follow

Last change coming up. find the code below

	
	case ET_PORTAL:
		CG_Portal( cent );
		break;
	case ET_SPEAKER:
		CG_Speaker( cent );
		break;
	case ET_GRAPPLE:
		CG_Grapple( cent );
		break;
	}
}

and modify it to

	case ET_PORTAL:
		CG_Portal( cent );
		break;
	case ET_SPEAKER:
		CG_Speaker( cent );
		break;
	case ET_GRAPPLE:
		CG_Grapple( cent );
		break;
	case ET_TURRET:
		CG_TURRET(cent);
		break;
	}
}

That should be us. Compile both and try them out. you may want the shieleded turret to be a lot tougher than the normal and cloaked, just set the health higher.


Back to the tutorials
Mail me