
Quake DeveLS - Decoy 2
Author: John Rittenhouse
Difficulty: Medium
Let's Do
It...
The original decoy
tutorial posted on QDeveLS is written so as you might as well write a new
monster so here is a easier one that does it in a different way. Remember +'s
are lines to add in!
Go to the bottom of the
edict_t defination and add the code below needed at the bottom.
gitem_t *item; // for bonus items // common data blocks moveinfo_t moveinfo; monsterinfo_t monsterinfo; +edict_t *decoy; //JR Decoy Edict
What we
have just done is create the decoy's edict so we can have decoy. Now lets work
on the commands for activating him and other stuff. Add a new file to your
project called "decoy.c". And add this code into it at the top of the
new file.
+#include "g_local.h" +#include "m_player.h" +#define newDecoy self->decoy +int i; +int huh; +void SP_Decoy (edict_t *self); +void func_explosive_explode (edict_t *self, edict_t *inflictor, edict_t *attacker, int damage, vec3_t point);
What we
have done is declare the declarations for this file not too hard. Now we will
add the spawning code, which is the most important part. We are going to do
something not too hard here now,
+void SP_Decoy (edict_t *self)+{+ if ( newDecoy ) + { + G_FreeEdict(newDecoy); + newDecoy = NULL;+ gi.centerprintf (self,"Holo decoy off!\n"); + + return; + } + gi.bprintf (PRINT_HIGH, "Holo Decoy on.\n");+ newDecoy = G_Spawn ();+ VectorCopy(self->s.origin,newDecoy->s.origin); + newDecoy->classname="decoy";+ newDecoy->takedamage=DAMAGE_AIM; + newDecoy->movetype= MOVETYPE_TOSS;+ newDecoy->mass = 200; + newDecoy->solid = SOLID_BBOX;+ newDecoy->deadflag =DEAD_NO; + newDecoy->clipmask = MASK_PLAYERSOLID;+ newDecoy->model = self->model; + newDecoy->s.modelindex = self->s.modelindex;+ newDecoy->s.modelindex2 = self->s.modelindex2;+ newDecoy->s.frame =0; + newDecoy->s.renderfx |= RF_TRANSLUCENT;+ newDecoy->waterlevel = 0;+ newDecoy->watertype=0;+ newDecoy->health= 20; + newDecoy->max_health =20;+ newDecoy->gib_health = -80; + newDecoy->pain= decoy_pain;+ newDecoy->think = Decoy_Think; + newDecoy->nextthink =level.time + .1;+ newDecoy->delay = level.time + 300;+ newDecoy->die = decoy_die; + newDecoy->owner = self;+ newDecoy->dmg = 100; + newDecoy->dmg_radius = 100; + + VectorSet (newDecoy->mins, -16, -16, -24); + VectorSet (newDecoy->maxs, 16, 16, 32);+ VectorClear (newDecoy->velocity);+ gi.linkentity (newDecoy);+ gi.centerprintf (self,"New decoy set!\n");+}
This code
here starts the new decoy. THe next is the rest of the code its fairly simple
also
+void decoyAI_RunFrames(edict_t *self, int start, int end) +{ + if(self->s.frame s.frame++; + } + else + { + self->s.frame = start; + } +} +void decoy_pain (edict_t *self, edict_t *other, float kick, int damage) +{ + decoyAI_RunFrames(self, FRAME_pain101, FRAME_pain104); +} + +void decoy_explode (edict_t *ent) +{ + + vec3_t origin; + + + + //FIXME: if we are onground then raise our Z just a bit since we are a point? + T_RadiusDamage(ent, ent->owner, ent->dmg, NULL, ent->dmg_radius,0); + VectorMA (ent->s.origin, -.02, ent->velocity, origin); + gi.WriteByte (svc_temp_entity); + if (ent->waterlevel) + { + if (ent->groundentity) + gi.WriteByte (TE_GRENADE_EXPLOSION_WATER); + else + gi.WriteByte (TE_ROCKET_EXPLOSION_WATER); + } + else + { + if (ent->groundentity) + gi.WriteByte (TE_GRENADE_EXPLOSION); + else + gi.WriteByte (TE_ROCKET_EXPLOSION); + } + gi.WritePosition (ent->s.origin); + gi.multicast (ent->s.origin, MULTICAST_PVS); + + +} +void Decoy_Think (edict_t *ent) +{ + edict_t *blip = NULL; + + if (level.time >ent->delay) + { + decoy_explode(ent); + return; + } + ent->think = Decoy_Think; + while ((blip = findradius (blip, ent->s.origin, 100)) != NULL) + { + + if (!(blip->svflags & SVF_MONSTER) && !blip->client) + continue; + if (blip == ent->owner) + continue; + if (blip->health <= 0) + continue; + if (!visible(ent, blip)) + continue; + ent->think = decoy_explode; + break; + } + switch (i) + { + case 0: + decoyAI_RunFrames(ent,FRAME_flip01-1,FRAME_flip12); + break; + case 1: + decoyAI_RunFrames(ent,FRAME_salute01-1,FRAME_salute11); + break; + case 2: + decoyAI_RunFrames(ent, FRAME_taunt01-1, FRAME_taunt17); + break; + +case 3: + decoyAI_RunFrames(ent,FRAME_wave01-1,FRAME_wave11); + break; + case 4: + default: + decoyAI_RunFrames(ent,FRAME_point01-1,FRAME_point12); + break; + } + if (huh == 20) + { + i = random()*5; + huh = 0; + } + huh = huh + 1; + + ent->nextthink = level.time + .1; +}
This is
all the code for frames and stuff. Now for the death function. Its an another
simple thing.
+void decoy_die (edict_t *self, edict_t *inflictor, edict_t *attacker, int damage, vec3_t point) +{ + int i; + gi.sound (self, CHAN_BODY, gi.soundindex ("misc/udeath.wav"), 1, ATTN_NORM, 0); + for(i=0; i<4; i++) + ThrowGib (self, "models/objects/gibs/sm_meat/tris.md2", damage, GIB_ORGANIC); + + self->takedamage = DAMAGE_NO; + + gi.WriteByte (svc_temp_entity); + gi.WriteByte (TE_BFG_EXPLOSION); + gi.WritePosition (self->s.origin); + gi.multicast (self->s.origin, MULTICAST_PVS); + + SP_Decoy (self->owner); + G_FreeEdict(self); +}
Alright
now so you can turn it on and off. put this code into the command part of
cmd_c.
else if (Q_stricmp (cmd, "weapprev") == 0) Cmd_WeapPrev_f (ent); else if (Q_stricmp (cmd, "weapnext") == 0) Cmd_WeapNext_f (ent); else if (Q_stricmp (cmd, "weaplast") == 0) Cmd_WeapLast_f (ent); +else if (Q_stricmp(cmd, "decoy") == 0 ) + SP_Decoy (ent);
Put at the
top of the file this code.
+void SP_Decoy(edict_t *self);
That wasn't
too hard now was it. I used this code for my mod Star Troopers at
http//www.captured.com/startc/
|
This site, and all
content and graphics displayed on it, |