Quake Devels-Command Detonated Rockets

Author: Caton Little
Difficulty: Medium

Pretty blue: God-Emperor Carmack's original code
Snot green: my heretical hackery

This entertaining weapon was designed primarily to scare the crap outa people who like hiding in hard to reach places and iritate innocent passers-by... you know who you are. It fires a big, slow rocket which putters along for as long as you hold the fire button down. When you release the button, it turns into a fast rocket which rips off towards the nearest target. Great for shooting around corners or into hidey holes.

First up, we need to keep track of the rocket so we know what to blow up if the player dies or releases the attack button. Crack open g_local.h (where the fun always starts) and stick a

edict_t *pet_rock;

line in the gclient_s struct. Initialise this field in MoveClientToIntermission (p_hud.c) by putting these lines at the end. (The first time the player enters the game, the function PutClientInServer zeros the whole client struct, so there's no need to set pet_rock to NULL there):

if(ent-client-pet_rock!=NULL)
    G_FreeEdict(ent-client-pet_rock);
ent-client-pet_rock=NULL;

We also want the rocket to blow up if the player dies (dead guys can't hold down fire buttons, right?), so put this in player_die (p_client.c):

if(self-client-pet_rock!=NULL)
    Pet_Rocket_Detonate (self-client-pet_rock);

OK. That'll make sure there are no rogue entities floating around. We also have to make sure it behaves well in other conditions... like if the player changes weapon or releases the button. Find these lines in Weapon_Generic, (p_weapon.c) and add the green bits:

if((ent-client-newweapon)&&(ent-client-weaponstate !=WEAPON_FIRING))
{
    ent-client-weaponstate=WEAPON_DROPPING;
    ent-client-ps.gunframe=FRAME_DEACTIVATE_FIRST;
    if(ent-client-pet_rock!=NULL)
        Pet_Rocket_Detonate(ent-client-pet_rock);
    return;
}
if((ent-client-pet_rock!=NULL)&&((ent-client-buttons & BUTTON_ATTACK)==0))
    Pet_Rocket_Detonate(ent-client-pet_rock);

Also, modify the line that reads

if((ent-client-latched_buttons|ent-client-buttons)& BUTTON_ATTACK)

to

if(((ent-client-latched_buttons|ent-client-buttons)& BUTTON_ATTACK)&&(ent-client-pet_rock==NULL))

This makes sure that only one slow rocket can exist at a time (and stops a stream of them coming out while the button is down). Okay, now the fun part... first, find Weapon_RocketLauncher_Fire (p_weapon.c). If game balance matters to you, you can change the damage and blast radius but the only line the really has to be changed is

ent-client-pet_rock=fire_pet_rocket(ent, start, forward, damage, 130, damage_radius, radius_damage);

We have to return the rocket entity back, so we'll need a new fire_pet_rocket function. Note also that the new rocket speed is only 130. This makes it a lot easier to time the detonation. Make a copy of fire_rocket (g_weapon.c) and call it fire_pet_rocket. (We ned to preserve the old fire_rocket for reasons that will soon become apparent) Note that it has to return a gedict_t *. Once again, there aren't many differences... just change

rocket-touch = rocket_touch;
rocket-think = G_FreeEdict;

to

rocket-touch = pet_rocket_touch
rocket-think = free_pet_rocket;

And put a

return rocket;

at the end. This gives something for client-pet_rock to do. The free_pet_rocket function is small, but necessary, otherwise Terrible Things Will Happen.

void free_pet_rocket(gedict_t *ent)
{
    if(ent-owner-client)
        ent-owner-client-pet_rock==NULL;
    G_FreeEdict(ent);
}

Right, we're nearly there. Find the rocket_touch function (g_weapon.c), copy it and mutate its sorry ass: Find the bit that handles sky collisions...

if (surf && (surf-flags & SURF_SKY))
{
    if(ent-owner-client)
        ent-owner-client-pet_rock=NULL;
    G_FreeEdict(ent);
    return;
}

and right at the end,

if(ent-owner-client)
    ent-owner-client-pet_rock=NULL;
G_FreeEdict(ent);

That just leaves one more function. Pet_Rocket_Detonate. This function blows the original rocket up, finds the nearest visible monster or player and sends a fast rockets off towards it.

void Pet_Rocket_Detonate(edict_t *ent)
{
    vec3_t origin, aim, dir;
    vec3_t forward, right, up;
    edict_t *targ, *realtarg;
    trace_t tr;
    float best, curr;
    int  damage;
    float damage_radius;
    int  radius_damage;

    // calculate position for the explosion entity
    VectorMA(ent-s.origin, -0.02, ent-velocity, origin);

    T_RadiusDamage(ent, ent-owner, ent-radius_dmg, NULL, ent-dmg_radius, MOD_R_SPLASH);

    targ=NULL;
    realtarg=NULL;
    best=10000;
    while((targ=findradius(targ, ent-s.origin, 2048))!=NULL)
    {
        if(targ==ent)
            continue;
        if(targ==ent-owner)
            continue;
        if(!((targ-svflags&SVF_MONSTER)&&(targ-health0))&&(strcmp(targ-classname, "player")!=0))
            continue;

        tr=gi.trace(ent-s.origin, NULL, NULL, targ-s.origin, ent, MASK_SHOT);
        if((tr.fraction<1.0)&&(tr.ent!=targ))
            continue;
        VectorSubtract(ent-s.origin, targ-s.origin, aim);
        curr=VectorLength(aim);
        if(curr<best)
        {
            best=curr;
            realtarg=targ;
        }
    }

    damage=75;
    damage_radius=120;
    radius_damage=75;
    if(ent-owner-client-quad_framenum level.framenum)
    {
        damage *=4;
        radius_damage *=4;
    }

    if(realtarg!=NULL)
        VectorCopy(realtarg-s.origin, origin); //If there is one
    else
        VectorMA(ent-s.origin, 4, ent-velocity, origin); //Otherwise, straight forward
    VectorSubtract(origin, ent-s.origin, aimdir);
    vectoangles(aimdir, dir);
    AngleVectors(dir, NULL, right, up);

//Straight at the target
    AngleVectors(dir, forward, NULL, NULL);
    fire_rocket(ent, ent-s.origin, forward, damage, 1300, damage_radius, radius_damage);

    ent-owner-client-pet_rock=NULL;
    G_FreeEdict(ent);
}

That's that. Fun things to do with this could involve turing it into a MIRV rocket, making it use more ammo and having a console command to switch it off.

 

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 3