Quake DeveLS - Advanced Thinking

Author: Kieren Johnstone a.k.a. -X'BuG-
Difficulty:
Easy

Hi, it's me again...I recently thought up a way of making Quake2 programming much easier! Using this tutorial, we will be able to have two different "think" and "nextthink" functions!
This means that, for example, you could have a rocket that dropped a grenade every second, but exploded after 3 seconds!
Hey - that's a great idea! I'll show you the grenade rocket trick!

First, we need to implement the new think function. Go to g_local.h at the very bottom of the file, and add this:

 
        // add for advanced thinking:
        float          nextthink2;
        void           (*think2)(edict_t *self);
        // end of add
}


Then we have to make it so that the game recognises this and runs think2 when the time is right. Open g_phys.c, and go to the function called SV_RunThink. Replace it with this:

 
/*
=============
SV_RunThink
 
Runs thinking code for this frame if necessary
=============
*/
qboolean SV_RunThink (edict_t *ent)
{
        float   thinktime;
        float   thinktime2;
 
        thinktime = ent->nextthink;
        if (thinktime <= 0)
               return true;
        if (thinktime > level.time+0.001)
               return true;
        
        ent->nextthink = 0;
        if (!ent->think)
               gi.error ("NULL ent->think");
        ent->think (ent);
 
        // added for advanced thinking:
 
        thinktime2 = ent->nextthink2;
        if (thinktime2 <= 0)
               return true;
        if (thinktime2 > level.time+0.001)
               return true;
        
        ent->nextthink2 = 0;
        if (!ent->think2)
               gi.error ("NULL ent->think2");
        ent->think2 (ent);
 
        // end of add
 
        return false;
}


As I said, this basically checks if it's time to run the think2 function (i.e. if the time is nextthink2).
Right, that's that done - now you can do whatever the hell you want with your new think2 ideas! Now to explore my idea.
We want a rocket to drop a grenade every second, but explode after 3 seconds! Let's go!
Open g_weapon.c and look for the fire_rocket function. Replace it with this:

 
void fire_rocket (edict_t *self, vec3_t start, vec3_t dir, int damage, int speed, float damage_radius, int radius_damage)
{
        edict_t *rocket;
 
        speed /= 2; // go slower
        rocket = G_Spawn();
        VectorCopy (start, rocket->s.origin);
        VectorCopy (dir, rocket->movedir);
        vectoangles (dir, rocket->s.angles);
        VectorScale (dir, speed, rocket->velocity);
        rocket->movetype = MOVETYPE_FLYMISSILE;
        rocket->clipmask = MASK_SHOT;
        rocket->solid = SOLID_BBOX;
        rocket->s.effects |= EF_ROCKET;
        VectorClear (rocket->mins);
        VectorClear (rocket->maxs);
        rocket->s.modelindex = gi.modelindex ("models/objects/rocket/tris.md2");
        rocket->owner = self;
        rocket->touch = rocket_touch;
        rocket->nextthink = level.time + 3; // changed to 3
        rocket->think = G_FreeEdict;
        // added:
        rocket->nextthink2 = level.time + 1;   // every second,
        rocket->think2 = rocket_drop_grenade;  // drop a grenade
        // end of add
        rocket->dmg = damage;
        rocket->radius_dmg = radius_damage;
        rocket->dmg_radius = damage_radius;
        rocket->s.sound = gi.soundindex ("weapons/rockfly.wav");
        rocket->classname = "rocket";
 
        if (self->client)
               check_dodge (self, rocket->s.origin, dir, speed);
 
        gi.linkentity (rocket);
}

And to finish it off, we need a function to drop those grenades! Put it before fire_rocket, or you'll have to prototype it (put void rocket_drop_grenade(edict_t *ent) before fire_rocket).
Here it is:

 
void rocket_drop_grenade (edict_t *ent)
{
        // new function to
        // drop a grenade from a rocket
        //
        // (similar to fire_grenade)
        //
        edict_t *grenade;
 
        grenade = G_Spawn();
        VectorCopy (ent->s.origin,grenade->s.origin);
        grenade->s.origin[2] -= ent->maxs[2] + 1;
        grenade->velocity[2] += 20;
        VectorSet (grenade->avelocity, 300, 300, 300);
        grenade->movetype = MOVETYPE_BOUNCE;
        grenade->clipmask = MASK_SHOT;
        grenade->solid = SOLID_BBOX;
        grenade->s.effects |= EF_GRENADE;
        VectorClear (grenade->mins);
        VectorClear (grenade->maxs);
        grenade->s.modelindex = gi.modelindex ("models/objects/grenade2/tris.md2");
        grenade->owner = ent->owner;
        grenade->touch = Grenade_Touch;
        grenade->nextthink = level.time + 1;
        grenade->think = Grenade_Explode;
        grenade->dmg = 30;
        grenade->dmg_radius = 200;
        grenade->classname = "hgrenade";
        if (held)
               grenade->spawnflags = 3;
        else
               grenade->spawnflags = 1;
        grenade->s.sound = gi.soundindex("weapons/hgrenc1b.wav");
 
        gi.sound (self, CHAN_WEAPON, gi.soundindex ("weapons/hgrent1a.wav"), 1, ATTN_NORM, 0);
        gi.linkentity (grenade);
}


Tada! All done, it should work fine! If it doesn't please e-mail and tell me, I don't like to give a bad impression! Although slightly unrealistic, the grenade rocket launcher is a good example. Think of something good and maybe send it in as a tutorial!


Tutorial by Kieren Johnstone, author of forthcoming mod CLOT.

This site, and all content and graphics displayed on it,
are ©opyrighted to the Quake DeveLS team. All rights received.
Got a suggestion? Comment? Question? Hate mail? Send it to us!
Oh yeah, this site is best viewed in 16 Bit or higher, with the resolution on 800*600.
Thanks to Planet Quake for there great help and support with hosting.
Best viewed with Netscape 4 or IE 5