|
posted 11-20-98 11:43 AM CT (US)
Title: The Depth Charge Weapon
Difficulty: Moderate
By: Philip (aka Maj.Bitch)
Email: peblair@gv.net
Date: 11-14-98
Note: Please give credit where credit is due.
======================================================
You've heard of
depth charges before, right? Those are
those barrel-type things that Navy Destroyers toss over
the sides and they blow up at a certain depth. Well, have
you ever had to deal with a camper hiding out under the
water (coming up for air as needed) and railing every poor
bastard who ventures into the water before they've had a
chance to do anything? Well, throw in a Depth Charge!!
For the cost of 5
Grenades, you toss out what looks like
a barrel (standard issue Q2 model) and the barrel bounces
around before coming to a stop somewhere. Now, two things
are going to happen. If the barrel is NOT IN WATER, then
it just explodes in 5 seconds with a small fireball and
giving off the usual radius damage. If the barrel detects
that it is in water then, after the 5 sec timer, the barrel
explodes into 4 BFG fireballs radiating outward in the direction
of the (N,S,E,W) points of the compass and another fireball
blasts straight upward (GREAT for Tokay's Towers!!). The
fire_bfg() is used and is standard Q2 so all the Q2 animations
and damaging etc associated with the bfg are still intact.
Along with the bfg
fireballs, anybody else within 1000 unit
radius who is in the water also, gets blown upward and a bit
in their forward direction (clear out of the water)!!! So,
if the bfg didn't get'em the shockwave will surely relocate
them out of their watery cubby-hole! Watch other players get
blown clear out of the water after your depth charge has
detonated..
Okay, let's get
started..
============================================================
We need to add
another MOD_* obituary flag. So, inside of
your g_local.h add the MOD_DEPTHCHARGE flag as the next integer
in the numerical sequence as shown by example:
#define
MOD_CLUSTER_BOMBS 24 // Cluster Airstrike
#define MOD_ZYLON_GAS 25 // Gas Grenade
#define MOD_RAILSTRIKE 26 // BFG Nuke Airstrike
#define MOD_BFG_NUKE 27 // Railgun Airstrike
#define MOD_DECOY 28 // Decoy Blast
#define MOD_LASERDRONE 29 // LaserDrone Weapon
#define MOD_DEPTHCHARGE 30 // NEW FLAG HERE
============================================================
Okay, let's make
some obituaries for your gameplay. Find your
ClientObituary() function in your p_client.c file and add the
following:
---------- FIND
THESE LINES HERE -------------
case
MOD_TARGET_BLASTER:
message="got blasted";
break;
------------ ADD THESE LINES HERE ------------
case MOD_DEPTHCHARGE:
message="got obliterated";
break;
Further down in this
same function:
-------------- FIND
THESE LINES -----------
case MOD_G_SPLASH:
if (IsFemale(victim))
message="tripped on her own grenade";
else
message="tripped on his own grenade";
break;
----------- ADD THESE
LINES HERE -------------
case MOD_DEPTHCHARGE:
message="got depth charged";
break;
Still further down
in this same function:
---------- FIND
THESE LINES HERE -------------
case MOD_TELEFRAG:
message="tried to invade";
message2="'s personal space";
break;
--------- ADD THESE
LINES HERE ---------------
case
MOD_DEPTHCHARGE:
message="was killed by";
message2="'s depth charge";
break;
Remember: You should
feel free to change these client
obituaries to whatever you think is more appropriate for
your particular mod..
============================================================
============================================================
============================================================
Open up your
g_cmds.c file and go near the bottom of your
ClientCommand() function and add the following as shown
by example..
---------- IF-ELSE
STATEMENTS --------
else if (Q_stricmp(cmd, "decoy") == 0 )
SP_Decoy(ent);
else if (Q_stricmp(cmd, "drone") == 0 )
Cmd_LaserDrone_f(ent);
else if (Q_stricmp(cmd, "teleport") == 0)
Cmd_Teleport_f(ent);
----------- ADD THESE LINES ------
else if (Q_stricmp(cmd, "dcharge") == 0)
Cmd_DepthCharge_f(ent);
else if .....
This will shoot out
a Depth Charged with a single stroke of
the player's aliased key.
============================================================
Also, near the top
of you g_cmds.c file add this forward
declaration:
void
Cmd_DepthCharge_f(edict_t *ent);
============================================================
Lets open you g_weapons.c file and add these helper routines
and the source code somewhere near the top of the file.
NOTE: I've used
these helper routines in many of my previous
tutorials so you may already have them someplace in your source.
If you already do have them, then you don't need them anymore.
Your compiler should warn you that you've already got these
functions previously defined..
Okay, add the
following text to the top of your g_weapons.c file.
------------------
START HERE ------------------------
//======================================================
// True if start and end are within radius distance.
//======================================================
qboolean G_Within_Radius(vec3_t start, vec3_t end, float rad) {
vec3_t eorg={0,0,0};
int j;
for (j=0; j<3; j++)>
eorg[j]=abs(start[j]-end[j]);
return (VectorLength(eorg) < rad);
}
//======================================================
// True if Ent is valid, has client, and edict_t inuse.
//======================================================
qboolean G_EntExists(edict_t *ent) {
return ((ent) && (ent->client) && (ent->inuse));
}
//======================================================
// True if ent is not DEAD or DEAD or DEAD (and BURIED!)
//======================================================
qboolean G_ClientNotDead(edict_t *ent) {
qboolean b1=ent->client->ps.pmove.pm_type!=PM_DEAD;
qboolean b2=ent->deadflag != DEAD_DEAD;
qboolean b3=ent->health > 0;
return (b3 || b2 || b1);
}
//======================================================
// True if ent is not DEAD and not just did a Respawn.
//======================================================
qboolean G_ClientInGame(edict_t *ent) {
if (!G_EntExists(ent)) return false;
if (!G_ClientNotDead(ent)) return false;
return (ent->client->respawn_time + 5.0 < level.time);
}
//======================================================
//======================================================
//======================================================
/*
Spawns a (type) explosion at (start} and Broadcasts to all Potentially
Visible Sets from {origin}
TE_BFG_BIGEXPLOSION
- Spawns a BFG particle explosion
TE_BFG_EXPLOSION - Spawns a BFG explosion sprite
TE_BOSSTPORT - Spawns a mushroom-cloud particle effect
TE_EXPLOSION1 - Spawns a mid-air-style explosion
TE_EXPLOSION2 - Spawns a nuclear-style explosion
TE_GRENADE_EXPLOSION - Spawns a grenade explosion
TE_GRENADE_EXPLOSION_WATER - Spawns an underwater grenade explosion
TE_ROCKET_EXPLOSION - Spawns a rocket explosion
TE_ROCKET_EXPLOSION_WATER - Spawns an underwater rocket explosion
Note: The last four
EXPLOSION entries overlap to some degree.
TE_GRENADE_EXPLOSION is the same as TE_EXPLOSION2,
TE_ROCKET_EXPLOSION is the same as TE_EXPLOSION1,
and both of the EXPLOSION_WATER entries are the same, visually.
*/
//======================================================
void G_Spawn_Explosion(int type, vec3_t start, vec3_t origin ) {
gi.WriteByte(svc_temp_entity);
gi.WriteByte(type);
gi.WritePosition(start);
gi.multicast(origin, MULTICAST_PVS);
}
//======================================================
//============== DEPTHCHARGE ROUTINES ==================
//======================================================
#define
ITEM_IN_ENTS_INVENTORY ent->client->pers.inventory[index]
//======================================================
void DepthCharge_Shockwave(edict_t *dcharge) {
edict_t *ent=NULL;
vec3_t forward,right,up;
int i;
// Blow backward ALL
ents IN WATER within 1000 units...
for(i=0;i < game.maxclients;i++) {
ent=g_edicts+i+1;
if (!G_ClientInGame(ent)) continue;
if (ent==dcharge) continue;
if (!G_Within_Radius(dcharge->s.origin, ent->s.origin, 1000)) continue;
if (!(gi.pointcontents(ent->s.origin) & CONTENTS_WATER)) continue;
AngleVectors(ent->s.angles, forward, right, up);
VectorMA(ent->velocity, 200, forward, ent->velocity);
VectorMA(ent->velocity, 2000, up, ent->velocity);
} // end for
}
//======================================================
void D_Dummy_Touch(edict_t *a, edict_t *b, cplane_t *c, csurface_t *d)
{}
//======================================================
void Secondary_Explosion(edict_t *dcharge) {
vec3_t forward,backward,right,left,up;
VectorSet(up,0,0,1);
VectorSet(forward,0,1,0);
VectorSet(right,1,0,0);
VectorSet(backward,0,-1,0);
VectorSet(left,-1,0,0);
// Fire BFG fireball
with dmg=200; speed=400; radius=1000!!
fire_bfg(dcharge->activator, dcharge->s.origin, up, 200, 400, 1000);
fire_bfg(dcharge->activator, dcharge->s.origin, forward, 200, 400,
1000);
fire_bfg(dcharge->activator, dcharge->s.origin, right, 200, 400, 1000);
fire_bfg(dcharge->activator, dcharge->s.origin, backward, 200, 400,
1000);
fire_bfg(dcharge->activator, dcharge->s.origin, left, 200, 400, 1000);
// Create a big
shockwave
DepthCharge_Shockwave(dcharge);
G_FreeEdict(dcharge);
}
//======================================================
void Primary_Explosion(edict_t *dcharge) {
// Not touchable
until stopped moving.
if ((VectorLength(dcharge->velocity) > 1)
|| (level.time < dcharge->delay)) {
dcharge->nextthink = level.time + 5.0;
return; }
// If in water then
big secondary explosion!!
if (gi.pointcontents(dcharge->s.origin) & CONTENTS_WATER) {
dcharge->think=Secondary_Explosion;
dcharge->nextthink = level.time + 1.0; }
else {
// Else, small explosion
G_Spawn_Explosion(TE_EXPLOSION2, dcharge->s.origin, dcharge->s.origin);
T_RadiusDamage(dcharge, dcharge->activator, 40, NULL, 300,
MOD_DEPTHCHARGE);
G_FreeEdict(dcharge); }
}
//======================================================
void Spawn_DepthCharge(edict_t *ent) {
edict_t *dcharge=NULL;
vec3_t torigin;
vec3_t forward=(0,0,0);
vec3_t right=(0,0,0);
vec3_t up=(0,0,0);
gitem_t *item=NULL;
VectorCopy(ent->s.origin,torigin);
AngleVectors(ent->client->v_angle, forward, right, up);
VectorMA(torigin, 50, forward, torigin);
if
(gi.pointcontents(torigin) & MASK_SHOT) {
gi.cprintf(ent,PRINT_HIGH,"Cannot project into Solid!\n");
return; }
dcharge = G_Spawn();
dcharge->owner=NULL;
dcharge->activator=ent;
dcharge->movetype=MOVETYPE_BOUNCE;
dcharge->solid = SOLID_BBOX;
dcharge->s.renderfx = NONE;
VectorCopy(up, dcharge->s.angles);
dcharge->s.effects = EF_NONE;
VectorSet(dcharge->mins,
-10, -10, 0);
VectorSet(dcharge->maxs, 10, 20, 30);
VectorClear(dcharge->velocity);
VectorScale(forward, 600, dcharge->velocity);
VectorMA(dcharge->velocity, 200+crandom()*10.0, up, dcharge->velocity);
VectorMA(dcharge->velocity, crandom()*10.0, right, dcharge->velocity);
VectorClear(dcharge->avelocity);
VectorCopy(torigin,dcharge->s.origin);
dcharge->model = "models/objects/barrels/tris.md2";
gi.setmodel(dcharge, dcharge->model);
dcharge->delay=5.0;
dcharge->touch=D_Dummy_Touch;
dcharge->think=Primary_Explosion;
dcharge->nextthink = level.time + 1.0;
gi.linkentity(dcharge);
}
//======================================================
// Pay the Piper for the Depth Charge
//======================================================
void Cmd_DepthCharge_f(edict_t *ent) {
int index;
// Don't allow
dead/respawning players to launch Depth Charges!
if (!G_ClientInGame(ent)) return;
index =
ITEM_INDEX(FindItem("grenades"));
if (ITEM_IN_ENTS_INVENTORY < 5){
gi.centerprintf(ent, "Depth Charge costs 5 grenades!\n");
return; }
else {
ITEM_IN_ENTS_INVENTORY -= 5;
Spawn_DepthCharge(ent); }
}
===============================================================
Lastly..
Be sure to add the
following to your Autoexec.cfg file as
shown by example..
bind d
"dcharge"
That's it!! Now go
KILL ALL THOSE BASTARDS!
Have Fun!!
philip
|