
Quake DeveLS - AirStrike
Author: Chris Hilton
Difficulty: Hard
Ever find yourself in a
cozy observing spot, watching the enemy stand around like lemmings, and wishing
you could call in an airstrike? Now you can!
Client
Variables
First, we'll need to keep
track of some new information for the player. This data won't need to be
persistant across levels or anything, so we simply add the variables to the end
of the gclient_t structure at approx. line 829 of g_local.h like so ('+' signs
indicate lines added)
float pickup_msg_time; float respawn_time; // can respawn when time > this++ // CCH: new variables for airstrikes+ int airstrike_called;+ vec3_t airstrike_entry;+ float airstrike_time;+ } gclient_t;
Now we can
keep track of when an airstrike has been called, where it should enter, and
what time it should arrive.
Entry
Point
'Where it should enter?'
you might be thinking. You can't have airstrikes just anywhere, they have to
come from the sky. So when an airstrike is called in, we want to make sure
there's a bit of sky for it to enter through. We'll talk about this more in
just a minute.
Let's add our command for
killing in the airstrike. To the ClientCommand() function in g_cmds.c we add
the following
Cmd_PutAway_f (ent); else if (Q_stricmp (cmd, "wave") == 0) Cmd_Wave_f (ent);++ // CCH: new command for calling airstrikes+ else if (Q_stricmp (cmd, "airstrike") == 0)+ Cmd_Airstrike_f (ent);+ else if (Q_stricmp (cmd, "gameversion") == 0) { gi.cprintf (ent, PRINT_HIGH, "%s : %s\n", GAMEVERSION, __DATE__);
This
enables us to type "cmd airstrike" at the console to call in an
airstrike (you'll probably want to bind this command to a key). Using this
command calls the function Cmd_Airstrike_f(), which I've added just before
ClientCommand() in g_cmds.c like so
+/*+=================+Cmd_Airstrike_f+CCH: new function to call in airstrikes+=================+*/+void Cmd_Airstrike_f (edict_t *ent)+{+ vec3_t start;+ vec3_t forward;+ vec3_t end;+ trace_t tr;++ // make sure an airstrike hasn't already been called+ if ( ent->client->airstrike_called )+ {+ gi.cprintf(ent, PRINT_HIGH, "The airstrike is already on its way.\n");+ return;+ }++ // make sure we're pointed at the sky+ VectorCopy(ent->s.origin, start);+ start[2] += ent->viewheight;+ AngleVectors(ent->client->v_angle, forward, NULL, NULL);+ VectorMA(start, 8192, forward, end);+ tr = gi.trace(start, NULL, NULL, end, ent, MASK_SHOT|CONTENTS_SLIME|CONTENTS_LAVA);+ if ( tr.surface && !(tr.surface->flags & SURF_SKY) )+ {+ gi.cprintf(ent, PRINT_HIGH, "Airstrikes have to come through the sky!\n");+ return;+ }++ // set up for the airstrike+ VectorCopy(tr.endpos, ent->client->airstrike_entry);+ ent->client->airstrike_called = 1;+ ent->client->airstrike_time = level.time + 30;+ gi.cprintf(ent, PRINT_HIGH, "Airstrike en route, ETA 30 seconds. Light up target.\n");+}+ /* ================= ClientCommand ================= */
In this
function, we first make sure that an airstrike hasn't already been called. If
one has, we let the player know and return.
Next, we make sure the
player is pointed at the sky. This will be the entry point of the airstrike
into the game world. To do this, we'll trace a line from the player's head to a
point 8192 units ahead of it (this should be more than long enough to find a
surface to run into) and check to see what we find. We use the vector start to
define the start point of this line, which is the player's origin plus his
viewheight. Then we use the AngleVectors() function to determine the forward
vector. Using VectorMA() gives us our end point, which is 8192 units from start
in the direction forward. Then we use gi.trace() to trace the line from start
to end, stopping wherever a shot would stop or if we run into slime or lava.
This will fill tr with data about what the trace ran into, but what we're
concerned about here is the surface we ran into. If that surface is not sky, we
let the player know what the 'air' in airstrike means and return.
When we do find sky, we
copy the trace's endpoint into our airstrike_entry variable, set our
airstrike_called variable, and set the airstrike_time to 30 seconds in the
future. Finally, let the player know that the airstrike is on its way, it's
time to target this thing!
So, how will we know when
the airstrike arrives? We'll just modify the ClientThink() function in
p_client.c, which is called every client frame, and add a little code at the
end to check if the airstrike has arrived.
} } + // CCH: Check to see if an airstrike has arrived+ if ( client->airstrike_called && level.time > client->airstrike_time )+ {+ client->airstrike_called = 0;+ Think_Airstrike (ent);+ } }
When the
time comes, we set airstrike_called back to off and call our Think_Airstrike()
function which will deliver the payload to the game world.
Think_Airstrike
I've added this function
to just after Think_Weapon() in p_weapon.c.
+/*+=================+Think_Airstrike+CCH: This will bring the airstrike ordinance into existence in the game+Called by ClientThink+=================+*/+void Think_Airstrike (edict_t *ent)+{+ vec3_t start;+ vec3_t forward;+ vec3_t end;+ vec3_t targetdir;+ trace_t tr;++ // find the target point+ VectorCopy(ent->s.origin, start);+ start[2] += ent->viewheight;+ AngleVectors(ent->client->v_angle, forward, NULL, NULL);+ VectorMA(start, 8192, forward, end);+ tr = gi.trace(start, NULL, NULL, end, ent, MASK_SHOT|CONTENTS_SLIME|CONTENTS_LAVA);++ // find the direction from the entry point to the target+ VectorSubtract(tr.endpos, ent->client->airstrike_entry, targetdir);+ VectorNormalize(targetdir);+ VectorAdd(ent->client->airstrike_entry, targetdir, start);++ // check to make sure we're not materializing in a solid+ if ( gi.pointcontents(start) == CONTENTS_SOLID )+ {+ gi.cprintf(ent, PRINT_HIGH, "Airstrike intercepted en route.\n");+ return;+ }++ // fire away!+ fire_rocket(ent, start, targetdir, 600, 550, 600, 600);+ gi.cprintf(ent, PRINT_HIGH, "Airstrike has arrived.\n");+}
We know
where the payload will enter from, but we need to know what direction it will
travel. The player will act as a spotter for targeting the payload, so wherever
the player is pointing will be the destination. We use gi.trace() just like we
did when we called in the airstrike to find the destination point.
Next, we subtract the
entry and destination points to find the direction the payload should travel.
Directions should nearly always be normalized (reduced to 1 unit in length) and
we even add this normalized vector to the airstrike_entry point so that the
payload will spawn into a point that is just a little bit off of the surface.
Finally, we use
gi.pointcontents() to check the start point and make sure we're not going to
spawn in a solid (you never know where the player has set the target point
after 30 seconds). If it's solid, we just let the player know that the payload
didn't make it. Otherwise, we fire a rocket using the fire_rocket() function
but with 5 times the normal damage and let the player know the payload has
arrived.
Odds and
Ends
All that's left now is a
few bits of code to wrap everything up. To g_local.h at approx. line 522 we
need to add our Think_Airstrike() function prototype.
void ChangeWeapon (edict_t *ent); void SpawnItem (edict_t *ent, gitem_t *item); void Think_Weapon (edict_t *ent);++// CCH: new prototype for function called when airstrike arrives+void Think_Airstrike (edict_t *ent);+ int ArmorIndex (edict_t *ent); int PowerArmorType (edict_t *ent); gitem_t *GetItemByIndex (int index);
Also, we
shouldn't really allow dead players to act as spotters, so we'll add the
folliwing to the player_die() function at line 219 of p_client.c.
} } + // CCH: Call off the airstrike+ self->client->airstrike_called = 0;+ // FIXME once we have death frames // self->deadflag = DEAD_DYING; self->deadflag = DEAD_DEAD;
That's it
for now. Some ways this might be improved would be having an actual sighting
laser, ability to select different payloads (cluster bombs, napalm, homing
missile), perhaps randomizing the airstrike delivery (never know when those pilots
will show up), and adding a flyby sound, like the one for the Strogg ship.
[Ed's note... there's an
exercise for the reader ! If anyone would like to implement some of those
improvements, send it in as a tutorial ! - SumFuka]
Have fun. Full source and
patch file at http://www.jump.net/~dctank.
|
This site, and all
content and graphics displayed on it, |