|
posted 09-17-98 11:33 PM CT (US)
Title: VIPER AIRSTRIKE 'AutoLauncher' II
Difficulty: Difficult to Write: Easy to Implement!
By: Philip (aka Maj.Bitch)
Email: peblair@gv.net // please report any bugs(??)
Date: 9-17-98
Note: Please give credit where credit is due.
=============================================================
In putting my bot detection stuff together I had to build
linked lists of players and a ghost entity. In the course
of putting all of this together I came across the idea of
trying to see if a ghost entity could initiate other
entities and cause things to happen. Well.. I awoke this
morning with the idea of having an entity autolaunch
random airstrikes at 1 minute intervals (just to keep'em
all hopping!!). I decided to take a much needed break
from the bot detection stuff and code this mod today.
Works great. Here's the post of my latest tutorial.
Everything from the
previous airstrike stuff still works
with the addition of the RailStrike!! (Like a lightning
bolt out of the sky straight down into the top of a player's
head!!) Then, they explode of course!!
To all who have
implemented my previous Viper Airstrike
Tutorial, I suggest that you put this entire code into your
mod and take out any other airstrike code that is already in
there from the other airstrike tutorials that I have posted.
The older coded tutorial will work with this one but you'll
have to chase down the changes yourself. Too many tweaks
to discuss them all!
Anyway,..
ABOUT NEW VIPER
AUTOLAUNCHER TUTORIAL:
Every player which
connects to the game is put onto a linked
list. This information is later used by an 'Auto-Launcher' as
discussed below.
There is an
AutoLauncher entity which is spawned immediately
after the first player connects and begins the deathmatch.
The AutoLauncher has its own timer. On a pre-set timed
basis, the launcher scans the list of players to find one
at random. If that player is out in the open, (ie, there
is sky above the player at that moment in time) then the
AutoLauncher 'steals' that player's origin. If that player
is not out in the open then another player is selected at
random.
Once it has obtained
an origin from an unsuspecting player, the
AutoLauncher then initiates a random airstrike of one of the
types (NUKE, CLUSTER, ROCKETS, RAILSTRIKE) onto that position
A message is sent to
the entity which got their coordinates
'borrowed' letting them know that there is incoming! They
then have 3 seconds to get out of the way!! Else KABOOM!!
After an
'AutoLaunched' airstrike has commenced, the auto
launcher shuts down for 60 seconds then starts looking for
players out in the open again. If one is found, then
the process repeats itself. Else, the AutoLauncher waits
patiently for 5 seconds and then starts checking player
positions once again. The process repeats until one is found.
Also, I also added
the RAILSTRIKE which fires a railslug into
the targeted position with radius damage at the point of
impact. This railslug will splatter a player on a direct
hit or kick them a country mile!!
Also, the cluster
bombs and rocket bombs have been given
a larger 'spread' so that they all don't hit the ground
at the same spot. In other words, you better clear the
targeted area because, anything which is around is most
likely going to end up completely shredded!!
Also, I added
obituaries to include kills from the airstrikes!
This was wholly absent from the previous tutorial on this topic.
This took quite a bit of re-engineering because you have to pass
around a MOD_ flag all the way down into the T_Damage() function
Sounds easy but it took alot just to get these little obits working.
Nice touch though!
Also, I found that
my use of the ents airstrike_targetdir was
actually not needed!! I achieved the same thing by setting the
targetdir vector in launch_airstrike_salvo() to {0,0,-1};...
Interesting..
All the original
Viper Airstrike features are still there so
airstrikes can be called by the players too!
Alot of minor tweaks
and bug fixes.
==========================================================
==========================================================
==========================================================
This is a heavily
modified version of the airstrike tutorial
posted on QDevels by Chris Hilton and another posted on QDevels
by Jonathan Benson. Many changes but they provided much
originality and inspiration.
About:
GREAT for getting
entrenched campers out of high up, well
defended and otherwise unreachable places.
In this
modification, a Strogg Viper airship is called in on a
targeted position. The player targets a position by pointing
their crosshair at a point (player, ground, building, wall, etc)
and then initiates the airstrike by hitting their airstrike
key (aliased in the player's autoexec.cfg: see below) which
radios the selected position to the Viper airship commander.
Once targeted, the
ship commander issues a voice command letting
the player know that the position has been targeted and that
the airship is on its way. Exactly 10 seconds later, the
ship appears in the sky and drops the desired airstrike
salvo (cluster or rocket bombs) directly onto the targeted
position. Afterwards, the ship flies off into the sunset.
If, for any reason,
the desired position cannot be targeted (ie,
the targeted position of the crosshair is pointing to someone or
something which is underneath something else and not directly
reachable from the sky) then the ship commander radios back to
the player to let them know that the target is not reachable from
the air and that a another position needs to be targeted.
The player must have
the prerequisite ammo amounts in their possession
before the airstrike is called. If not, then the airstrike cannot
be completed.
For the Cluster_Bombs a total of 10 Grenades is required
For the Rocket_Bombs a total of 6 Rockets is required
For the BFG_NUKE a total of 50 PowerCells is required.
For the Lightning a total of 50 PowerCells is required.
The player gets the
frag credits for all players killed in the
bombing raid either by direct hits or by radius damage.
GREAT for deathmatch
levesl 'THE EDGE' and 'TOKAYS TOWERS'.
Features:
: Original Strogg Viper airship model used from standard Q2 files.
: Ship voice commands and communications are fully enabled.
: All sounds are standard Q2 files so no client pak files needed.
: Any target with sky overhead is bombable (even inside structures!).
: Airstrikes are launched in 10 sec. and can be called off.
: Two types of bombing raids (cluster or rockets) enabled.
: Cluster bombing raid drops 10 single bombs (which bounce around).
: Cluster bombs have random vectors for maximum bouncing spread.
: Cluster bombs have random radius damage ranges.
: Rocket bombing raid drops 6 airial bombs.
: Rocket bombs have differing velocities and radius damages.
: Rocket bombs 'float' down rotating sideways as in real life..
: Players can be killed by their own bombing raids!!
: Rocket 'flyby' sound gives victim notice of incoming ship.
: Many more subtleties. Try it out for yourself.
: BFG Nukes also selectable..
: RailStrikes are also selectable.
========================================================
========================================================
========================================================
NOTE: You are
responsible for the forward declarations.
If you don't know what that means then put these at the
bottom of your q_shared.h file..
// EntList Routines
void G_AddToEntList(edict_t *ent);
void G_RemoveFromEntList(edict_t *ent);
void G_ScrubEntList(void);
edict_t *G_GetRandomEnt(void);
qboolean G_EntInGame(edict_t *ent);
edict_t *G_GetBestScoreEnt(void);
edict_t *G_GetNextEnt(void);
// Airstrike
Functions
void Get_Target_Position(edict_t *ent, vec3_t endpos);
void drop_clusterbomb(edict_t *shooter, vec3_t start, vec3_t aimdir);
void drop_rocket_bomb(edict_t *shooter, vec3_t start, vec3_t dir, int
damage,int speed);
void launch_airstrike_salvo(edict_t *ent, int strike_type);
qboolean Launch_Airstrike(edict_t *ent, int cmd);
void spawn_aircraft(edict_t *ent);
// Airstrike firing
Functions
void Fire_BFG(edict_t *self, vec3_t start, vec3_t dir, int damage, int speed,
float damage_radius);void Weapon_Grenade_Fire(edict_t *ent, qboolean held);
void Fire_BFG_Nuke(edict_t *shooter, vec3_t start, vec3_t dir, int damage,
int speed, float damage_radius);
void Fire_RailStrike_Bolt(edict_t *shooter, vec3_t start, vec3_t aimdir, int
damage, int kick);
void G_Spawn_Splash(int type, int count, int color, vec3_t start, vec3_t
movdir, vec3_t origin);
void G_Spawn_Trails(int type, vec3_t start, vec3_t endpos, vec3_t origin);
void G_Spawn_Sparks(int type, vec3_t start, vec3_t movdir, vec3_t origin);
void G_Spawn_Explosion(int type, vec3_t start, vec3_t origin);
==========================================================
Also: in some of my
standard Q2 functions, I've added
cap's to some of the function names which will cause your
compiler not to find the function. If my function names
don't match your function names then, by all means, change
may names to work for your code!
==========================================================
Okay, Let's get
started...
==========================================================
First, set up some global
definitions by placing the following
in q_shared.h
#define
MAX_WORLD_HEIGHT 8192
#define PRESENT_TIME
level.time
#define FRAMETIME 0.1
#define
MOD_ROCKET_BOMBS 0x00000023 // Rocket Airstrike
#define MOD_CLUSTER_BOMBS 0x00000024 // Cluster Airstrike
#define MOD_RAILSTRIKE 0x00000025 // BFG Nuke Airstrike
#define MOD_BFG_NUKE 0x00000026 // RailStrike
#define BOMB_MODEL
"models/objects/bomb/tris.md2"
#define ROCKET_MODEL "models/objects/rocket/tris.md2"
#define GRENADE_MODEL "models/objects/grenade/tris.md2"
#define VIPER_MODEL "models/ships/viper/tris.md2"
// Global sound
definitions.
#define PILOT1_SOUND gi.soundindex("world/pilot1.wav")
#define PILOT2_SOUND gi.soundindex("world/pilot2.wav")
#define PILOT3_SOUND gi.soundindex("world/pilot3.wav")
#define FLYBY1_SOUND gi.soundindex("world/flyby1.wav")
#define BFG_EXPLODE_SOUND gi.soundindex("weapons/bfg__x1b.wav")
// Type of airstrike
desired
#define ROCKET_BOMBS 1 // airstrike1
#define CLUSTER_BOMBS 2 // airstrike2
#define BFG_NUKE 3 // airstrike3
#define RAILSTRIKE 4 // airstrike4
#define
ENTS_VIEW_HEIGHT ent->viewheight
#define ENTS_V_ANGLE ent->client->v_angle
#define ENTS_S_ORIGIN ent->s.origin
#define ENTS_NETNAME ent->client->pers.netname
========================================================
========================================================
Add the following to
the bottom of edict_t struct.
// AirStrike
Variables
qboolean airstrike_called; // TRUE if Airstrike called
vec3_t airstrike_start; // Position of Targeted Entity
float airstrike_time; // Timer for incoming missiles
int airstrike_type; // 1=Rocket, 2=Cluster, etc.
========================================================
==========================================================
Okay, let's set up all the linked list stuff!!
==================
Put the following struct definition in your local.h
typedef struct
ent_list_s {
edict_t *entptr;
struct ent_list_s *next;
} entlist;
This is the
structure for the linked list routines...
==========================================================
Put this global
variable in your local.h
extern qboolean
launcher_spawned;
At declare it at the
top of g_main.c like this:
qboolean
launcher_spawned=false; // Default to Not Spawned.
==========================================================
==========================================================
==========================================================
At the bottom of
q_utils.c paste in all of the following
(from start to end as marked below)..
-------------- CUT
START -----------------
//======================================================
//=========== LINKED LIST ROUTINES =====================
//======================================================
entlist
*ptr1=NULL,*ptr2=NULL; // ptr1 ALWAYS points to first rec.
int num_players=0;
entlist *nextptr=NULL;
//=========================================================
// Returns next non-NULL Entity in Linked List.
//=========================================================
edict_t *G_GetNextEnt(void) {
entlist *tptr=NULL;
if (!nextptr)
nextptr=ptr1;
while
(nextptr!=NULL) {
tptr=nextptr; // one to return;
nextptr=nextptr->next; // move to next rec in list.
if (nextptr==NULL)
nextptr=ptr1;
if (tptr->entptr!=NULL)
break;
} // end while
return
tptr->entptr;
}
//=========================================================
// Returns a Random non-NULL Entity in Linked List.
//=========================================================
edict_t *G_GetRandomEnt(void) {
int i,rec;
double d;
float g;
d=10^(num_players
< 4 ? num_players : 4);
g=random()*(float)d+0.5;
rec=(((int)g)%num_players)+1;
//
gi.dprintf("int i = %d\n",i);
ptr2=ptr1; // Always
start at first rec #1
for (i=2; i<=rec;
i++)
ptr2=ptr2->next;
if (ptr2==NULL)
ptr2=ptr1; // Little Safeguarding...
// gi.dprintf("Returning
Random Ent: %s\n",ptr2->entptr->client->pers.netname);
return
ptr2->entptr;
}
//=========================================================
// Returns a non-NULL Entity with Highest Score
//=========================================================
edict_t *G_GetBestScoreEnt(void) {
edict_t *bestptr=NULL;
int bestscore=-999;
ptr2=ptr1; // ptr2
ALWAYS starts at ptr1
// scan thru entire
list..
while (ptr2!=NULL) {
if (ptr2->entptr->deadflag!=DEAD_DEAD)
if (ptr2->entptr->client->resp.score > bestscore) {
bestptr=ptr2->entptr; // found one. Can it be beat?
bestscore=ptr2->entptr->client->resp.score; }
ptr2=ptr2->next; }
return bestptr;
}
//=========================================================
// Entity is added to List in ClientBeginDeathmatch()
//=========================================================
void G_AddToEntlist(edict_t *ent) {
entlist *newptr;
// create the next
rec in list
newptr = malloc(sizeof(entlist));
newptr->entptr = ent; // make the assignment
newptr->next = NULL; // tie off end pointer
if (ptr1==NULL) //
if this is first rec then
ptr1 = newptr; // set ptr1 to point to rec
else {
ptr2=ptr1; // ptr2 ALWAYS starts at ptr1
while (ptr2->next!=NULL)
ptr2=ptr2->next; // move quickly to last rec.
ptr2->next=newptr;} // connect the dots..
num_players+=1; //
add another player to list.
}
//=========================================================
// Entity is removed from List in ClientDisconnect()
//=========================================================
void G_RemoveFromEntList(edict_t *ent) {
entlist *tptr,*ptr3;
ptr3=ptr2=ptr1; //
ptr2 ALWAYS starts at ptr1
// scan all links in
list.
while (ptr2 != NULL) {
if (ptr2->entptr==ent) {
tptr=ptr2; // we're going to free() this record.
ptr2=ptr2->next; // bump to next rect
ptr3->next=ptr2; // connect the dots..
free(tptr); // put for garbage collection..
num_players-=1; // one less player on list.
return; } // Okay, we're done, so Exit
ptr3=ptr2;
ptr2=ptr2->next; // move to next rec in list
} // end while
}
//======================================================
// Returns true if ent is in the game and not DEAD
//======================================================
qboolean G_EntInGame(edict_t *ent) {
return
((ent->deadflag != DEAD_DEAD)
&& (ent->client->respawn_time + 10.0 < level.time));
}
//=========================================================
// Removes all Entities from EntList in EndDMLevel()
//=========================================================
void G_ScrubEntList(void) {
entlist *tptr;
ptr2=ptr1; // ptr2
ALWAYS starts at ptr1
// step across
entire list.
while (ptr2 != NULL) {
tptr=ptr2;
ptr2=ptr2->next;
free(tptr); }
ptr1=NULL; // reset
ptr to start of list.
num_players=0; // nobody on list anymore.
}
---------------- CUT
STOP -------------------
========================================================
==========================================================
==========================================================
==========================================================
Now, let's setup of
all the linked list pieces of code that
need to be inserted here and there:
==========================================================
At the top of
EndDMLevel() put the following line of code:
G_ScrubEntList(); //
Entity List Cleanup
This will clean up the linked list at the end of each
deathmatch level.
========================================================
In
ClientBeginDeathmatch(), right after PutClientInServer();
put the following:
G_AddToEntlist(ent);
// Add Entity to EntList
This will add each new player to the linked list as they
enter the deathmatch.
========================================================
Put this in
ClientDisconnect() before the printf as shown:
G_RemoveFromEntList(ent);
// Remove from EntList
---- put it right
before this line ---------
gi.bprintf(PRINT_HIGH,
"%s disconnected\n", ENTS_NETNAME);
This will remove a player from the linked list when they
disconnect from the server.
========================================================
Okay, that's all the
linked list setup stuff..
Now, lets do the
"AUTOLAUNCHER' routines..
========================================================
Create a new file
called g_airstrike.c and paste in ALL the
following from start to end as indicated..
Make sure that your
#include's are correct for your
particular setup!!
------------ Start
here ------------------
#include
"xxxxxxx.h"
#include "xxxxxxx.h"
//======================================================
//============ Airstrike Targeting Routine =============
//======================================================
void Get_Target_Position(edict_t *ent, vec3_t endpos) {
vec3_t start={0,0,0}, forward={0,0,0},
endpt={0,0,0}, targetdir={0,0,0};
trace_t tr, tr_2;
// find the target's
end point
VectorCopy(ENTS_S_ORIGIN, start);
start[2] += ENTS_VIEW_HEIGHT;
AngleVectors(ENTS_V_ANGLE, forward, NULL, NULL);
VectorMA(start, MAX_WORLD_HEIGHT, forward, endpt);
tr=gi.trace(start, NULL, NULL, endpt, ent, MASK_SHOT|MASK_LIQUID);
// find the
direction from the entry point to the target
VectorSubtract(tr.endpos, endpos, targetdir);
VectorNormalize(targetdir);
VectorAdd(endpos, targetdir, start);
tr_2=gi.trace(start,
NULL, NULL, tr.endpos, ent, MASK_SHOT|MASK_LIQUID);
// Do we have a
clear line of fire?
if (CONTENTS_AT_POINT(start) == CONTENTS_SOLID || tr_2.fraction < 1.0) {
// Clear out the airstrike positioning vectors.
ent->airstrike_called=false; // Call off airstrike..
gi.cprintf(ent, PRINT_HIGH, "No Line-Of-Fire to Target!!\n");
gi.sound(ent, CHAN_ITEM, PILOT1_SOUND, 0.8, ATTN_NORM, 0);
return; }
VectorCopy(start,
ent->airstrike_start);
}
//=====================================================
//=========== Airstrike 'Salvo' Routine ===============
//=====================================================
void launch_airstrike_salvo(edict_t *ent, int strike_type) {
vec3_t start={0,0,0},targetdir={0,0,-1};
int i;
VectorCopy(ent->airstrike_start,
start);
switch (strike_type)
{
case CLUSTER_BOMBS:
for (i=1;i<=10;i++) {
drop_clusterbomb(ent, start, targetdir);
start[0]+=i*20*random();
start[1]+=i*30*random(); }
break;
case ROCKET_BOMBS:
drop_rocket_bomb(ent, start, targetdir, 400, 250);
start[0]+=200*random();
start[1]+=200*random();
drop_rocket_bomb(ent, start, targetdir, 500, 450);
start[0]-=500*random();
start[1]+=500*random();
drop_rocket_bomb(ent, start, targetdir, 600, 150);
start[0]+=400*random();
start[1]-=400*random();
drop_rocket_bomb(ent, start, targetdir, 600, 210);
start[0]+=250*random();
start[1]+=250*random();
drop_rocket_bomb(ent, start, targetdir, 600, 430);
start[0]-=425*random();
start[1]+=425*random();
drop_rocket_bomb(ent, start, targetdir, 500, 330);
break;
case BFG_NUKE:
Fire_BFG_Nuke(ent, start, targetdir, 200, 800, 1000);
break;
case RAILSTRIKE:
Fire_RailStrike_Bolt(ent, start, targetdir, 50, 400);
break;
} // end switch
// Clear out the
airstrike vector.
VectorClear(ent->airstrike_start);
ent->airstrike_called=false;
// Airstrike Reset
}
//=====================================================
void AirCraft_Touch(edict_t *craft, edict_t *other, cplane_t *plane, csurface_t
*surf){
G_FreeEdict(craft);
}
//======================================================
//============ Airstrike 'AirCraft' Routine ============
//======================================================
void spawn_aircraft(edict_t *ent) {
vec3_t start={0,0,0}, dir={0,1,0};
edict_t *craft=NULL;
VectorCopy(ent->airstrike_start,
start);
craft=G_Spawn(); //
Spawn Craft Entity
craft->classname="aircraft";
VectorCopy(start, craft->s.origin);
VectorCopy(dir, craft->movedir); // Craft Move direction
vectoangles(dir, craft->s.angles); // Vector angle of direction
craft->velocity[0]=0;
craft->velocity[1]=120; // pretty slow velocity...
craft->velocity[2]=0;
craft->clipmask=MASK_SHOT;
craft->movetype=MOVETYPE_FLYMISSILE;// Movetype = FLY
craft->solid=SOLID_BBOX; // Craft Body Box
VectorClear(craft->mins); // Must Clear these out
VectorClear(craft->maxs); // Must Clear these out.
craft->s.modelindex=gi.modelindex(VIPER_MODEL);
craft->owner=ent;
craft->takedamage=DAMAGE_NO;
craft->touch=AirCraft_Touch;
craft->think=G_FreeEdict;
craft->nextthink=level.time+30;
gi.linkentity(craft);
gi.sound(ent,
CHAN_AUTO, FLYBY1_SOUND, 0.7, ATTN_NORM, 0);
launch_airstrike_salvo(ent,
ent->airstrike_type);
}
//======================================================
void Viper_Think(edict_t *Viper) {
spawn_aircraft(Viper);
// Launch the Viper's Craft!!
// After 50 Seconds,
release Viper Entity.
Viper->think=G_FreeEdict;
Viper->nextthink=level.time + 50.0; // Keep Alive for Client Obits!!
}
//======================================================
edict_t *G_Spawn_Viper(edict_t *ent) {
edict_t *Viper;
int clientnum;
Viper=G_Spawn();
Viper->movetype= MOVETYPE_NONE;
Viper->classname="Viper";
clientnum=Viper-g_edicts-1;
Viper->client=&game.clients[clientnum];
sprintf(Viper->client->pers.netname, "Viper");
Viper->takedamage=DAMAGE_NO;
Viper->solid = SOLID_NOT;
Viper->clipmask = MASK_ALL;
Viper->model = "";
Viper->s.modelindex = 0;
Viper->s.modelindex2 = 0;
Viper->s.frame = 0;
Viper->s.effects = 0;
Viper->s.renderfx = 0;
VectorCopy(ent->s.origin, Viper->s.origin);
VectorClear(Viper->velocity);
Viper->use =
NULL;
Viper->die = NULL;
Viper->touch = NULL;
Viper->think = NULL;
Viper->nextthink = 0;
return Viper;
}
//======================================================
qboolean Launch_Airstrike(edict_t *ent, int cmd) {
edict_t *Viper;
vec3_t start={0,0,0},
world_up={0,0,0},
endpt={0,0,0},
targetdir={0,0,0};
trace_t tr;
Viper=G_Spawn_Viper(ent);
gi.linkentity(Viper); // Must be here for some reason..
switch (cmd) {
case 4: Viper->airstrike_type=RAILSTRIKE; break;
case 3: Viper->airstrike_type=BFG_NUKE; break;
case 2: Viper->airstrike_type=CLUSTER_BOMBS; break;
case 1:
default:Viper->airstrike_type=ROCKET_BOMBS;
} // end switch
VectorClear(Viper->airstrike_start);
VectorCopy(ent->s.origin, start); // start at ent's position.
start[2] += ent->viewheight;
VectorSet(world_up, 0, 0, 1);
VectorMA(start, MAX_WORLD_HEIGHT, world_up, endpt);
tr=gi.trace(start, NULL, NULL, endpt, ent, MASK_SHOT|MASK_LIQUID);
if (tr.surface && !(tr.surface->flags & SURF_SKY)) {
G_FreeEdict(Viper);
return false;} // No direct path to ent->s.origin.
VectorSubtract(tr.endpos,
start, targetdir);
VectorNormalize(targetdir);
VectorAdd(endpt,targetdir,start);
//-----------------------------------------------
// Direct path to target so go ahead with strike!
//-----------------------------------------------
VectorCopy(tr.endpos,
Viper->airstrike_start);
// Warn of impending
doom..
switch (cmd) {
case 4: gi.centerprintf(ent, "InComing RailStrike!!\n"); break;
case 3: gi.centerprintf(ent, "InComing BFG NUKE!!\n"); break;
case 2: gi.centerprintf(ent, "InComing Cluster Bombs!!\n"); break;
case 1:
default:gi.centerprintf(ent, "InComing Rocket Bombs!!\n");
} // end switch
Viper->think =
Viper_Think;
Viper->nextthink =level.time + 5.0; // 5 Seconds 'till KABOOM!
return true;
}
//======================================================
void Launcher_Think(edict_t *Launcher) {
int strike_type;
edict_t *ent;
ent=G_GetRandomEnt();
while (!G_EntInGame(ent))
ent=G_GetRandomEnt();
// Randomly select: CLUSTER,
ROCKET, BFG_NUKE, RAILSTRIKE
strike_type=(((int)(random()*10+0.5))%4)+1;
if
(!Launch_Airstrike(ent, strike_type)) // Couldn't be launched?
Launcher->nextthink=level.time + 5.0; // Try again in 5 seconds.
else
Launcher->nextthink=level.time + 60.0; // Wait for 60 secs..
}
//=========================================================
void CreateLauncher(void) {
edict_t *Launcher;
Launcher=G_Spawn();
Launcher->movetype=
MOVETYPE_NONE;
Launcher->classname="Launcher";
Launcher->takedamage=DAMAGE_NO;
Launcher->solid = SOLID_NOT;
Launcher->clipmask = MASK_ALL;
Launcher->model = "";
Launcher->s.modelindex = 0;
Launcher->s.modelindex2 = 0;
Launcher->s.frame = 0;
Launcher->s.effects = 0;
Launcher->s.renderfx = 0;
VectorClear(Launcher->s.origin);
VectorClear(Launcher->velocity);
Launcher->use =
NULL;
Launcher->die = NULL;
Launcher->touch = NULL;
Launcher->think =
Launcher_Think;
Launcher->nextthink =level.time + 10;
gi.linkentity(Launcher);
launcher_spawned=true;
// only spawn once!
}
------------ End
here --------------------
=============================================
Okay, now let's get
the AutoLauncher spawned!
=============================================
Inside of
PutClientInServer() add this single
line to the very bottom of the function:
if (!launcher_spawned)
CreateLauncher();
This will spawn the
AutoLauncher when the first
player connects.. That's it for the AutoLauncher.
=============================================
Next, let's setup
the player's ability to call
the airstrike themselves..
========================================================
============== G_CMDS.C MODIFICATINOS ==================
========================================================
Add the following
function to g_cmds.c at a point above
the ClientCommand() function..
//======================================================
void Cmd_Airstrike_f(edict_t *ent) {
int index;
vec3_t start={0,0,0}, forward={0,0,0},
world_up={0,0,0}, end={0,0,0};
trace_t tr;
// Deduct proper
ammo amounts?
if (!((int)dmflags->value & DF_INFINITE_AMMO))
switch (ent->airstrike_type) {
case CLUSTER_BOMBS:
index = ITEM_INDEX(FindItem("grenades"));
if (ent->client->pers.inventory[index] >= 10)
ent->client->pers.inventory[index] -= 10;
else {
gi.cprintf(ent, PRINT_HIGH, "Cluster strike requires 10
Grenades!!\n");
return; }
break;
case ROCKET_BOMBS:
index = ITEM_INDEX(FindItem("rockets"));
if (ent->client->pers.inventory[index] >= 6)
ent->client->pers.inventory[index] -= 6;
else {
gi.cprintf(ent, PRINT_HIGH, "Rocket strike requires 6
Rockets!!\n");
return; }
break;
case BFG_NUKE:
index = ITEM_INDEX(FindItem("cells"));
if (ent->client->pers.inventory[index] >= 50)
ent->client->pers.inventory[index] -= 50;
else {
gi.cprintf(ent, PRINT_HIGH, "BFG Nuke strike requires 50
PowerCells!!\n");
return; }
break;
case RAILSTRIKE:
index = ITEM_INDEX(FindItem("cells"));
if (ent->client->pers.inventory[index] >= 50)
ent->client->pers.inventory[index] -= 50;
else {
gi.cprintf(ent, PRINT_HIGH, "RailStrike requires 50
PowerCells!!\n");
return; }
break;
} // end switch
// Zero out the
airstrike positioning vector.
VectorClear(ent->airstrike_start);
------------- INSERT PART II HERE ------------
|