|
I
stripped this from my private source and pasted this tutorial together
because I couldn't find a copy of the original tute. Also, the way this is
currently implemented is that the person who spawns the monster gets awarded
the frags the monster gets for you. You can change it around to fit your own
mod. And, it currently spawns the monster near your position. It should be
pretty easy to change that to make it spawn random monsters at deathmatch
spawn points. It will work just fine in either case because the basic stuff
is all here. If I left anything out or you have problems, please let me know.
Maj.Bitch
Add this to your g_local.h file
// NEW - (Maj.Bitch) for ent->mtype variable
// TYPE OF MONSTER ---- DEFAULT HEALTH
#define M_NOMONSTER 0 // 0
#define M_SOLDIERLT 1 // 20
#define M_SOLDIER 2 // 30
#define M_SOLDIERSS 3 // 40
#define M_FLIPPER 4 // 50 - Shark
#define M_FLYER 5 // 50 - Flyer
#define M_INFANTRY 6 // 100
#define M_INSANE 7 // 100 - Crazy Marine
#define M_GUNNER 8 // 175
#define M_CHICK 9 // 175
#define M_PARASITE 10 // 175
#define M_FLOATER 11 // 200
#define M_HOVER 12 // 240 - Icarus
#define M_BERSERK 13 // 240
#define M_MEDIC 14 // 300 - unused
#define M_MUTANT 15 // 300
#define M_BRAIN 16 // 300
#define M_GLADIATOR 17 // 400
#define M_TANK 18 // 750
#define M_SUPERTANK 19 // 1500
#define M_BOSS2 20 // 2000
#define M_JORG 21 // 3000
#define M_MAKRON 22 // 3000
===============================================
===============================================
Add this variable to edict_t struct
int mtype;
===============================================
===============================================
Put this line at the very top of ClientObituary()
// Was this a monster that did the killing?
if (Monster_Obits(self, attacker)) return;
===============================================
===============================================
Paste all this stuff at the bottom of g_monster.c
//======================================================
//======== GENERIC MONSTER SPAWNING ROUTINES ===========
//======================================================
//======================================================
// Frag award and Obits for Victims of Monster attacks!
// Attacker=Monster
// Attacker->activator=Monster's Owner
//======================================================
qboolean Monster_Obits(edict_t *victim, edict_t *attacker) {
char *message1="";
char *message2="";
// No Monster obits in single-player mode.
if (!deathmatch->value) return false;
// No message if killed by your own Monster!
if (victim==attacker->activator) return false;
message1="was killed by";
// What type of monster was this?
switch (attacker->mtype) {
case M_BERSERK:
message2="'s Berserker";
break;
case M_BOSS2:
message2="'s Boss";
break;
case M_SOLDIERSS:
message2="'s SoldierSS";
break;
case M_JORG:
message2="'s Jorg";
break;
case M_BRAIN:
message2="'s Brain";
break;
case M_CHICK:
message2="'s Chick";
break;
case M_FLIPPER:
message2="'s Shark";
break;
case M_FLOATER:
message2="'s Floater";
break;
case M_FLYER:
message2="'s Flyer";
break;
case M_INSANE:
message2="'s Insane";
break;
case M_GLADIATOR:
message2="'s Gladiator";
break;
case M_HOVER:
message2="'s Icarus";
break;
case M_INFANTRY:
message2="'s Infantry";
break;
case M_SOLDIERLT:
message2="'s SoldierLT";
break;
case M_SOLDIER:
message2="'s Soldier";
break;
case M_MEDIC:
message2="'s Medic";
break;
case M_MUTANT:
message2="'s Mutant";
break;
case M_PARASITE:
message2="'s Parasite";
break;
case M_TANK:
message2="'s Tank";
break;
case M_MAKRON:
message2="'s Makron";
break;
case M_GUNNER:
message2="'s Gunner";
break;
case M_SUPERTANK:
message2="'s Supertank";
break;
default:
return false; }
// Print the obituary message..
if (victim->client && attacker->activator->client) {
gi.bprintf(PRINT_MEDIUM,"%s %s %s %s!\n",
victim->client->pers.netname,
message1,
attacker->activator->client->pers.netname,
message2);
// Give Monster's activator/owner this frag!! - CHANGE THIS TO FIT YOUR MOD
attacker->activator->client->resp.score++; }
return true;
}
//======================================================
// Timer entity initiates the Monsters monster-Detonation.
//======================================================
void Kill_Monster(edict_t *timer) {
// Is Monster still alive?
if (timer->activator!=NULL)
// Maximally damage this monster (5000 dmg units ought to do it!!)
T_Damage(timer->activator, timer, timer, zvec, timer->activator->s.origin, NULL, 5000, 1, 0, MOD_SPLASH);
G_FreeEdict(timer);
}
//=========================================================
// Spawn a Monster of type 'mtype' at 'torigin' and timer
// will monster-destruct monster in number of 'secs'.
//=========================================================
qboolean G_Spawn_Monster(edict_t *ent, vec3_t torigin, int mtype, float secs) {
edict_t *monster;
edict_t *timer;
int temp;
if (secs<=0) {
gi.cprintf(ent, PRINT_HIGH,"G_Spawn_Monster called with 0 secs??\n");
return false; }
if (mtype!=M_FLIPPER)
if (gi.pointcontents(torigin) & CONTENTS_SOLID) {
gi.cprintf(ent,PRINT_HIGH,"Monster materialized in solid matter!\n");
return false; }
// Create basic entity stuff...
monster = G_Spawn();
gi.linkentity(monster);
// Force skill to NIGHTMARE MODE - hehe!!
gi.cvar_forceset("skill", va("%f","3"));
temp=deathmatch->value;
deathmatch->value=0; // Temporarily disable this!
// Create this monster type
switch (mtype) {
case M_BERSERK: SP_monster_berserk(monster);
monster->classname = "XBerserk";break;
case M_BOSS2: SP_monster_boss2(monster);
monster->classname = "XBoss2";break;
case M_SOLDIERSS: SP_monster_soldier_ss(monster);
monster->classname = "XSoldierSS";break;
case M_JORG: SP_monster_jorg(monster);
monster->classname = "XJorg";break;
case M_BRAIN: SP_monster_brain(monster);
monster->classname = "XBrain";break;
case M_CHICK: SP_monster_chick(monster);
monster->classname = "XChick";break;
case M_FLIPPER: SP_monster_flipper(monster);
monster->classname = "XShark";break;
case M_FLOATER: SP_monster_floater(monster);
monster->classname = "XFloater";break;
case M_FLYER: SP_monster_flyer(monster);
monster->classname = "XFlyer";break;
case M_INSANE: SP_misc_insane(monster);
monster->classname = "XInsane";break;
case M_GLADIATOR: SP_monster_gladiator(monster);
monster->classname = "XGladiator";break;
case M_HOVER: SP_monster_hover(monster);
monster->classname = "XIcarus";break;
case M_INFANTRY: SP_monster_infantry(monster);
monster->classname = "XInfantry";break;
case M_SOLDIERLT: SP_monster_soldier_light(monster);
monster->classname = "XSoldierLT";break;
case M_SOLDIER: SP_monster_soldier(monster);
monster->classname = "XSoldier";break;
case M_MEDIC: SP_monster_medic(monster);
monster->classname = "XMedic";break;
case M_MUTANT: SP_monster_mutant(monster);
monster->classname = "XMutant";break;
case M_PARASITE: SP_monster_parasite(monster);
monster->classname = "XParasite";break;
case M_TANK: SP_monster_tank(monster);
monster->classname = "XTank";break;
case M_MAKRON: SP_monster_makron(monster);
monster->classname = "XMakron";break;
case M_GUNNER: SP_monster_gunner(monster);
monster->classname = "XGunner";break;
case M_SUPERTANK: SP_monster_supertank(monster);
monster->classname = "XSuperTank"; }
deathmatch->value=temp; // Restore to previous value!
monster->owner=world; // Creator can be killed too.
monster->activator=ent; // Link to creator for frags..
monster->mtype=mtype; // Type of monster this is (for obits)..
VectorCopy(torigin, monster->s.origin);
// Set ai for Brutal Mode and to start pursuit ASAP!!
monster->monsterinfo.aiflags |= (AI_BRUTAL&AI_PURSUE_NEXT));
// Play a powerup sound..
gi.sound(monster, CHAN_VOICE, gi.soundindex("misc/power1.wav"), 1.0, ATTN_IDLE, OFF);
// - DELETE THE TIMER IF YOU DON'T WANT THEM TO SELF-DESTRUCT AUTOMATICALLY
// -------------------------------------
// Timer destroys monster after # secs.
// -------------------------------------
timer=G_Spawn();
timer->owner=ent;
timer->activator=monster; // Link to Monster
timer->takedamage=DAMAGE_NO;
timer->movetype=MOVETYPE_NONE;
timer->solid = SOLID_NOT;
VectorClear(timer->s.origin);
VectorClear(timer->mins);
VectorClear(timer->maxs);
timer->think=Kill_Monster;
timer->nextthink=level.time + secs;
gi.linkentity(timer);
// Link back to Owner
ent->goalentity=timer;
return true; // Spawn successful
}
// SET A COMMAND TO ACTIVATE THIS BY SERVER COMMAND
// AND CHANGE THIS AROUND TO FIT YOUR MOD...
//====================================================
// Spawn a monster from command line..
//====================================================
void Cmd_Monsters_f(edict_t *ent, int mtype) {
vec3_t forward, torigin;
// Not available to dead or respawning players!
if (!ent || !ent->inuse || !ent->client) return;
// Already spawned? Then detonate
if (ent->goalentity) {
Kill_Monster(ent->goalentity);
ent->goalentity=NULL;
gi.centerprintf(ent, "MONSTER DESTROYED!\n");
return; }
// See if we can project origin forward 50 units...
AngleVectors(ent->s.angles, forward, NULL, NULL);
VectorMA(ent->s.origin, 50, forward, torigin);
if (G_Spawn_Monster(ent, torigin, mtype, 180))
gi.centerprintf(ent, "MOVE AWAY NOW!\n");
}
|