I was thinking about a way to bring some more variation into Quake.
So I looked for a way to vary their positions a little bit.
Of course, their positions are declared by the map makers and most of the time, the positions have a reason.
Usually the monster shall suprise you (from a hidden place)
I did not want to spoil this, but tweak it a bit. I stumbled over this very interesting function in Mission pack 1 source:
gremlin_split (); (it clones the gremlin)
So I had the idea to make every monster "clone-able" in Quake.
You now have the possibility to multiply the monsters inside a map via auto_cvar (if your engine supports it) and give it a random position around the original monster within a specific radius.
You can choose how many times the monster shall be cloned via auto_cvar. According to this, the total amount of monster per map will be doubeld, trippled, quadtrippeld, and so on.
If your engine does not support auto_cvars, you can still use the "free" original engine cvars, or use a fixed multiplier inside qc directly.
The new monster will only be placed if a suitable position is found in that specific radius. So it will not be stuck in a wall for example. The cloned monsters have the exact same properties like the orirginal. Map or ent file must not be edited.
I extended the gremlin_split (); function to be usable for all monsters, added crandom() vector positions for more positioning variation. It further also detects now if the random position is inside lava/slime. Also flying/swimming monster can now be cloned.
This short clip shows you some maps with doubled enemies:
Quake multiply enemies - YouTube
============================
This is the code:
Start the clone-ing with this call right after "walkmonster_start();" in the monsters .qc file:
For walking monsters:
Code: Select all
do_the_cloning();
Code: Select all
do_the_cloning__fly_swim ();
First declare a new .float:
Code: Select all
.float clone_me;
Code: Select all
/////// clone monsters:
var float autocvar_clonemonsters = 0; // set cvar 'clonemonsters' default to 0. 0= disabled sets how many times monsters shall be cloned. "1" = will have 2 times as much monsters as original. "2" = will have 3 times as much monsters as original. ...etc.
// code for walking monsters:
void() Clone =
{
local entity clone, head;
local float done, c, proceed;
local vector ang, pos;
done = 0;
c = 0;
ang = self.angles;
while (done == 0)
{
makevectors(ang);
pos = self.origin + (60 * v_forward) + (crandom() * 33 *v_right); // randomize the spawn position !!
head = findradius(pos, 35);
proceed = 1;
while (head)
{
if ((head.health > 0) && (head.flags & (FL_MONSTER | FL_CLIENT)))
proceed = 0;
head = head.chain;
}
traceline(self.origin,pos,FALSE,self);
if (trace_fraction == 1 && (proceed == 1))
{
traceline(self.origin,(pos-'40 40 0'),FALSE,self);
if (trace_fraction == 1)
{
traceline(self.origin,(pos+'40 40 0'),FALSE,self);
if (trace_fraction == 1)
{
traceline(self.origin,(pos + '0 0 64'),FALSE,self);
if (trace_fraction == 1)
{
traceline(self.origin,(pos - '0 0 64'),FALSE,self);
if (trace_fraction != 1)
{
traceline(pos,(pos - '0 0 64'),FALSE,self);
local vector checkforlavaslime;
checkforlavaslime = trace_endpos;
if ((pointcontents(checkforlavaslime) != CONTENT_SLIME) && (pointcontents(checkforlavaslime) != CONTENT_LAVA))
{
done = 1;
}
}
}
}
}
}
if (done == 0)
{
ang_y = ang_y + 36;
c = c + 1;
if (c==10)
{
return;
}
}
}
clone = spawn();
copyentity (self,clone);
total_monsters = total_monsters + 1;
setorigin(clone, pos);
};
void() do_the_cloning =
{
if (autocvar_clonemonsters <= self.clone_me)
return;
else
Clone();
self.clone_me = self.clone_me + 1;
do_the_cloning();
};
// ===================
// code for flying/swimming monsters:
void() Clone_fly_swim =
{
local entity clone, head;
local float done, c, proceed;
local vector ang, pos;
done = 0;
c = 0;
ang = self.angles;
while (done == 0)
{
makevectors(ang);
pos = self.origin + (60 * v_forward) + (crandom() * 33 *v_right) + (crandom() * 25 *v_up); // randomize the spawn position !!
head = findradius(pos, 35);
proceed = 1;
while (head)
{
if ((head.health > 0) && (head.flags & (FL_MONSTER | FL_CLIENT)))
proceed = 0;
head = head.chain;
}
traceline(self.origin,pos,FALSE,self);
if (trace_fraction == 1 && (proceed == 1))
{
traceline(self.origin,(pos-'40 40 0'),FALSE,self);
if (trace_fraction == 1)
{
traceline(self.origin,(pos+'40 40 0'),FALSE,self);
if (trace_fraction == 1)
{
done = 1;
}
}
}
if (done == 0)
{
ang_y = ang_y + 36;
c = c + 1;
if (c==10)
{
return;
}
}
}
clone = spawn();
copyentity (self,clone);
total_monsters = total_monsters + 1;
setorigin(clone, pos);
};
void() do_the_cloning__fly_swim =
{
if (autocvar_clonemonsters <= self.clone_me)
return;
else
Clone_fly_swim();
self.clone_me = self.clone_me + 1;
do_the_cloning__fly_swim ();
};
Seven.