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.
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
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.