Quake DeveLS - BFG -Suit

Author: Anozireth
Difficulty: Medium

Time for some carnage...

This patch will give the player a "BFG suit" when they pick up the power armor. Basically, it puts the player inside the BFG ball and beams emeinate from him to anything they can damage. The bfg_armor_think function is largely based on the original bfg_think. also, whem the player is inside the ball, their screen will be tinted green.

First, lets put the two main functions, bfg_armor_think and Use_BFGArmor in place. In g_weapon.c, at the end of the file add:

/*======================================================================
BFG Armor This will be a bitch....
======================================================================*/

//self is bfg ball
void bfg_armor_think (edict_t *self)
{
// This function is just bfg_think with some small changes
// after all, this is supposed to behave like da bfg
        edict_t *ent;
        edict_t *ignore;
        vec3_t  point;
        vec3_t  dir;
        vec3_t  start;
        vec3_t  end;
        int             dmg;
        trace_t tr;

        if(self->bfg_armor_done < level.time)
                {
                self->owner->client->bfg_blend = 0;
                G_FreeEdict(self);
                return;
                }

        dmg = 15;
        VectorCopy (self->owner->s.origin, self->s.origin);  // make it look like player is inside ball

        // Make owner screen a little green (hey, it rhymes!)
        //SV_AddBlend (0, 1, 0, 0.3, self->owner->client->ps.blend);
   self->owner->client->bfg_blend = 1;


        ent = NULL;
        while ((ent = findradius(ent, self->s.origin, 512)) != NULL) // Make sure we get plenty o bastards
        {
                if (ent == self)              // kant kill ourselves
                        continue;

                if (ent == self->owner)       // or our master
                        continue;

                if (!ent->takedamage)         // or powerups, medkits etc..
                        continue;

                if (!(ent->svflags & SVF_MONSTER) && (!ent->client) && (strcmp(ent->classname, "misc_explobox") != 0))   // If it wont blow up or die, ignore it
                        continue;

                VectorMA (ent->absmin, 0.5, ent->size, point);

                VectorSubtract (point, self->s.origin, dir);
                VectorNormalize (dir);

                ignore = self;
                VectorCopy (self->s.origin, start);
                VectorMA (start, 2048, dir, end);
                while(1)
                {
                        tr = gi.trace (start, NULL, NULL, end, ignore, CONTENTS_SOLID|CONTENTS_MONSTER|CONTENTS_DEADMONSTER);

                        if (!tr.ent)
                                break;

                        // hurt it if we can
                        if ((tr.ent->takedamage) && !(tr.ent->flags & FL_IMMUNE_LASER) && (tr.ent != self->owner))
                                T_Damage (tr.ent, self, self->owner, dir, tr.endpos, vec3_origin, dmg, 1, DAMAGE_ENERGY, MOD_BFG_LASER);

                        // if we hit something that's not a monster or player we're done
                        if (!(tr.ent->svflags & SVF_MONSTER) && (!tr.ent->client))
                        {
                                gi.WriteByte (svc_temp_entity);
                                gi.WriteByte (TE_LASER_SPARKS);
                                gi.WriteByte (4);
                                gi.WritePosition (tr.endpos);
                                gi.WriteDir (tr.plane.normal);
                                gi.WriteByte (self->s.skinnum);
                                gi.multicast (tr.endpos, MULTICAST_PVS);
                                break;
                        }

                        ignore = tr.ent;
                        VectorCopy (tr.endpos, start);
                }

                gi.WriteByte (svc_temp_entity);
                gi.WriteByte (TE_BFG_LASER);
                gi.WritePosition (self->s.origin);
                gi.WritePosition (tr.endpos);
                gi.multicast (self->s.origin, MULTICAST_PHS);
        }

        self->nextthink = level.time + FRAMETIME;
}

void Use_BFGArmor (edict_t *self)
{
// this function is fire_bfg simplifed
        edict_t *bfg;

        if(self->client->pers.inventory[ITEM_INDEX(FindItem("cells"))] < 50)
                return;

        bfg = G_Spawn();
        VectorCopy (self->s.origin, bfg->s.origin);
        bfg->movetype = MOVETYPE_NONE;
        bfg->solid = SOLID_NOT;
        bfg->s.effects |= EF_BFG | EF_ANIM_ALLFAST;
        bfg->s.modelindex = gi.modelindex ("sprites/s_bfg1.sp2");
        bfg->owner = self;
   bfg->bfg_armor_done = level.time + 1;
        bfg->nextthink = level.time + FRAMETIME;
        bfg->think = bfg_armor_think;
        bfg->classname = "bfg armor blast";
        //bfg->s.sound = gi.soundindex ("weapons/bfg__l1a.wav");
        gi.linkentity (bfg);

        self->client->pers.inventory[ITEM_INDEX(FindItem("cells"))] -=50;
}
OK, now that was a lot. Now, here's what they do: bfg_armor_think starts off by placing the ball at the player origin. It then blends the player's screen a little for effect. Then, it shoots out all the beams. Use_BFGArmor makes sure you have enough cells (50), then spawns the ball and subtracts 50 cells.

On to the next step, picking it up. In g_items.c, around line 19 add:

// BFG suit
void Use_BFGArmor (edict_t *self);

This is just the prototype for the function.  Now on line 724 of g_items.c in Pickup_PowerArmor add:

other->client->pers.inventory[ITEM_INDEX(FindItem("Bfg armor"))]++;
 
That will give it to us when we get the power armor.  Now, to make it an item, on  line 1243 add:

/*=================
BFG armor
=================*/
        {
                "item_bfg_armor",
                Pickup_PowerArmor,
                Use_BFGArmor,
                NULL,
                NULL,
                "misc/ar3_pkup.wav",
                "models/items/armor/shield/tris.md2", EF_ROTATE,
                NULL,
/* icon */              "i_powershield",
/* pickup */    "BFG Armor",
/* width */             60,
                50,
                "Cells",
                IT_WEAPON,
                NULL,
                0,
/* precache */ "misc/power2.wav misc/power1.wav"
        },
There, now it is officially an item. Now, we need a new variable in the gclient_s structure. so on line 862 of g_local.h insert:
int                     bfg_blend;
This is a reference for the screen blending function. Now we need another in the edict struct, so on line 985add:
float                   bfg_armor_done;
Now, about that blend function, in p_view.c at line 422 insert:
// BFG armor
        if(ent->client->bfg_blend)
      SV_AddBlend (0, 1, 0, 0.3, ent->client->ps.blend);

// BFG armor
This will give us the green screen so it looks like we're in the ball. OK, you're done! Go try it out, it practically vaporizes any smaller enemy or poorly armored foe(after all, it does do 150pts damage/sec.) You can select it like any other inventory item to use it.

This Tutorial was written by Tom Nicholson (aka. Anozireth). I am 15 and live in Seattle, WA.

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 their great help and support with hosting.
Best viewed with Netscape 4