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