monster_spawn function?
monster_spawn function?
yes im making a wave based mod for my own use, but the problem iv come across is this. i cant get it to work! i tried doing if (wave == x) {monster_army} this obviously doesn't work. so iv been trying to use e = spawn(); and in monster_army i set entity = soldier; . but i think this just gave me an error when trying to call soldier = spawn(); from my monster_spawn function. what am i doing wrong? what do i need to do to fix it?
Re: monster_spawn function?
DMSP is a mod that spawns monsters in deathmatch maps, which is why it is called DMSP. (Deathmatch single player). http://strlen.com/maps/dmsp/index.html
Back story done, continuing ... this mod spawns monsters out of thin air.
1) dmsp.qc thread: http://forums.inside3d.com/viewtopic.php?t=2587 where hondobondo shares dmsp.qc which I guess he got from Aardappel.
2) This .qc file is in the download here: http://www.moddb.com/members/hondobondo ... collection
Back story done, continuing ... this mod spawns monsters out of thin air.
1) dmsp.qc thread: http://forums.inside3d.com/viewtopic.php?t=2587 where hondobondo shares dmsp.qc which I guess he got from Aardappel.
2) This .qc file is in the download here: http://www.moddb.com/members/hondobondo ... collection
The night is young. How else can I annoy the world before sunsrise? Inquisitive minds want to know ! And if they don't -- well like that ever has stopped me before ..
Re: monster_spawn function?
I think it would go something like this:
Code: Select all
self = spawn();
self.origin = '0 0 0'; // put some calculation here!
monster_army();
Re: monster_spawn function?
I wrote some code that semi randomizes the monster spawns for soldiers, dogs, and enforcers, in single player maps. Its in this thread: http://forums.inside3d.com/viewtopic.php?f=2&t=4776
Essentially I used some logic to determine where near an existing spawn I could spawn a monster, and randomly picked how many to spawn. It is straight forward enough, and effective.
Essentially I used some logic to determine where near an existing spawn I could spawn a monster, and randomly picked how many to spawn. It is straight forward enough, and effective.
Re: monster_spawn function?
thanks for the info guys ill be sure to look into it all.
Re: monster_spawn function?
so basically i have to make a selectspawnpoint for my monsters. similar to the one for the player. then dependent on the wave tell it to spawn one? thats okay, but what if i wanted to spawn 3 or 4 of one monster or 2 of one and 3 of another?
Re: monster_spawn function?
The trick is to spawn the monster you want.
So in this code which is called by the entity in the map, I create two entities, the new entity I am setting up, and the tmp one to hold onto self while I use it to pass off to the monster spawn code. So, in order:
1) Create the two new entities
2) Spawn the new entity
3) Set location, rotation, and importantly, classname.
4) Preserve self entity in the tmp entity
5) Set self to the new entity
6) Call the monster spawn function to complete setting up the monster and spin him off.
7) reset self back from the tmp entity.
At this point, rinse, reuse, repeat. This method should work in a loop with any monster, the trick is of course to spawn the correct monster type you want. So you might do something like this:
You would still need code to determine where to spawn the entities, and if you even can. So check for other monsters there, walls, etc...
Code: Select all
local entity new, tmp;
new = spawn();
new.classname = "monster_army";
new.angles = self.angles;
setorigin(new, neworigin);
tmp = self;
self = new;
monster_army_spawn();
new = self;
self = tmp;
spawncount = spawncount + 1;
1) Create the two new entities
2) Spawn the new entity
3) Set location, rotation, and importantly, classname.
4) Preserve self entity in the tmp entity
5) Set self to the new entity
6) Call the monster spawn function to complete setting up the monster and spin him off.
7) reset self back from the tmp entity.
At this point, rinse, reuse, repeat. This method should work in a loop with any monster, the trick is of course to spawn the correct monster type you want. So you might do something like this:
Code: Select all
local float randint, soldiers, dogs, ogres;
soldiers = 0;
dogs = 0;
ogres = 0;
randint = rint((random() * 3) + 1);
while (soldiers < randint)
{
//Spawn Soldier Code here
soldiers = soldiers + 1;
}
randint = rint((random() * 3) + 1);
while (dogs < randint)
{
//Spawn Dog Code here
dogs = dogs + 1;
}
randint = rint((random() * 3) + 1);
while (ogres < randint)
{
//Spawn Dog Code here
ogres = ogres + 1;
}
Re: monster_spawn function?
so, creating a wave based monster spawner is seriously this indepth? it seems like it should be easier... ugh my qc is rusty again..
also im not sure if iv seen qc that uses while? and why do you need a random number?
also found this, going to look through it as i try to understand your code. just to see if its any similar so i can compare and learn something.
and does anyone think it would be easier to adapt something like this to my purpose? instead of spawning a bot that acts like a monster actually use it to spawn my monsters? http://quakewiki.net/archives/aicafe/tu ... tutor9.htm
also im not sure if iv seen qc that uses while? and why do you need a random number?
also found this, going to look through it as i try to understand your code. just to see if its any similar so i can compare and learn something.
and does anyone think it would be easier to adapt something like this to my purpose? instead of spawning a bot that acts like a monster actually use it to spawn my monsters? http://quakewiki.net/archives/aicafe/tu ... tutor9.htm
Re: monster_spawn function?
I used random just to create a certain degree of randomness to how many of each monster is spawned. It was purely an example. Yes, qc can use while loops. The id code does it, I do it, and it works quite nicely. It would be insanely hard to do a lot of things without a looping structure or two.
Re: monster_spawn function?
My current mod, which is basically a wave gametype, cunningly hidden in a story, has avoided all this spawning malarky while I've been testing and just used teleporting monsters. It can't really stay like that, as it gets bigger and more complicated. This code is properly helpful, though, being a control freak with a 'story' to tell, I'll not be randomising
-
- Posts: 2126
- Joined: Sat Nov 25, 2006 1:49 pm
Re: monster_spawn function?
ceriux wrote:so, creating a wave based monster spawner is seriously this indepth? it seems like it should be easier... ugh my qc is rusty again..
also im not sure if iv seen qc that uses while? and why do you need a random number?
also found this, going to look through it as i try to understand your code. just to see if its any similar so i can compare and learn something.
and does anyone think it would be easier to adapt something like this to my purpose? instead of spawning a bot that acts like a monster actually use it to spawn my monsters? http://quakewiki.net/archives/aicafe/tu ... tutor9.htm
Spawning monsters out of thin air (or at least from a info_player_start) is not hard, provided you take some precautions.
First, if you just call the monster spawning function directly (monster_dog(), monster_army(), etc.), it will crash in most engines because it will try to precache all required media during runtime - a big no no, even in more robust engines like DP. So, to fix this, precache all monster stuff in the main() function at world.qc. Then, I use a small trick to not break hell loose with the precache_* functions. In defs.qc find this:
Code: Select all
string(string s) precache_sound = #19;
string(string s) precache_model = #20;
string(string s) precache_file = #68;
string(string s) precache_model2 = #75; // registered version only
string(string s) precache_sound2 = #76; // registered version only
string(string s) precache_file2 = #77; // registered version only
Code: Select all
string(string s) _precache_sound = #19;
string(string s) _precache_model = #20;
string(string s) _precache_file = #68; // no effect except for -copy
string(string s) _precache_model2 = #75; // registered version only
string(string s) _precache_sound2 = #76; // registered version only
string(string s) _precache_file2 = #77; // registered version only
Code: Select all
string(string s) precache_sound =
{
if (time < 3)
{
return (_precache_sound (s));
}
return (string_null);
};
string(string s) precache_model =
{
if (time < 3)
{
return (_precache_model (s));
}
return (string_null);
};
string(string s) precache_file =
{
if (time < 3)
{
return (_precache_file (s));
}
return (string_null);
};
string(string s) precache_sound2 =
{
if (time < 3)
{
return (_precache_sound2 (s));
}
return (string_null);
};
string(string s) precache_model2 =
{
if (time < 3)
{
return (_precache_model2 (s));
}
return (string_null);
};
string(string s) precache_file2 =
{
if (time < 3)
{
return (_precache_file2 (s));
}
return (string_null);
};
Now is safe to call the spawn functions to any monster. Let's try something insane to test, so add this to the top of weapons.qc:
Code: Select all
// spawns a dog over the player's head
// WARNING: not tested, probably wont work from start
void () spawn_dog_over_my_head =
local entity munster;
munster = spawn ();
setorigin (munster, self.origin + '0 0 64');
musnter.classname = "monster_dog";
munster.think = monster_dog;
munster.nextthink = time + 0.1;
};
Code: Select all
void() ImpulseCommands =
{
local entity p;
if (self.impulse >= 1 && self.impulse <= 8)
W_ChangeWeapon ();
if (self.impulse == 9)
CheatCommand ();
if (self.impulse == 10)
CycleWeaponCommand ();
if (self.impulse == 11)
ServerflagsCommand ();
if (self.impulse == 12)
CycleWeaponReverseCommand ();
if (self.impulse == 255)
QuadCheat ();
if (self.impulse == 100)
{
spawn_dog_over_my_head();
}
self.impulse = 0;
};
I know FrikaC made a cgi-bin version of the quakec interpreter once and wrote part of his website in QuakeC (LordHavoc)
Re: monster_spawn function?
well, when i get my code bug free. ill post the src up.
Re: monster_spawn function?
thanks for the replys so far... i have it working kinda. cept i have a few problems...
here's my code.
okay... what this currently does is create a bot that behaves like a monster (monster_army) , the problem that im having is that 1 its moving MY spawn location to that of the monster_spawner, and instead of just spawning 1 monster_army, its spawning one in each location that there is a monster_spawner...
ps: i just fixed the problem with it switching my spawn spot!
lmao i had it like 99% fixed then this happened xD
okay back to 99% fixed. i'm thinking i have to remove the corpses. cause monsters arnt spawning while corpses remain. if they gib... they continue to respawn (i think) so for a temp fix for the moment. ill be gibbing bodies on death. just to see if it works. nope...
so now that i have things fixed-ish.. on the first wave 1 monster spawns as expected.. on the 2nd wave 2 spawns just as expected... on the 3rd wave... only 1 spawns ? why?
code update : http://pastebin.com/TMjUfNJg
I BELIEVE I HAVE IT FIXED! If anyone wants src for what i have for your own use let me know! right now it only supports 1 monster, but others should be easy changes!
here's my code.
Code: Select all
/*
============
SelectSpawnPoint
Returns the entity to spawn at
============
*/
entity() MonsterSpawnPoint =
{
local entity spot;
local entity thing;
local float mcount;
// testinfo_player_start is only found in regioned levels
spot = find (world, classname, "testplayerstart");
if (spot)
return spot;
// choose a info_player_deathmatch point
if (coop)
{
lastspawn = find(lastspawn, classname, "monster_spawner");
if (lastspawn == world)
lastspawn = find (lastspawn, classname, "monster_spawner");
if (lastspawn != world)
return lastspawn;
}
else if (deathmatch)
{
spot = lastspawn;
while (1)
{
spot = find(spot, classname, "monster_spawner");
if (spot != world)
{
if (spot == lastspawn)
return lastspawn;
mcount = 0;
thing = findradius(spot.origin, 32);
while(thing)
{
if (thing.classname == "monster")
mcount = mcount + 1;
thing = thing.chain;
}
if (mcount == 0)
{
lastspawn = spot;
return spot;
}
}
}
}
if (serverflags)
{ // return with a rune to start
spot = find (world, classname, "monster_spawner");
if (spot)
return spot;
}
spot = find (world, classname, "monster_spawner");
if (!spot)
error ("WARNING: no monster_spawner on level");
return spot;
};
Code: Select all
// ------------------------------------------------
void() create_armybot =
// ------------------------------------------------
{
local entity bot, spawn_spot;
// start entity and place it in world
bot = spawn();
spawn_spot = MonsterSpawnPoint ();
bot.origin = spawn_spot.origin + '0 0 1';
bot.angles = spawn_spot.angles;
bot.fixangle = TRUE;
spawn_tfog (bot.origin);
spawn_tdeath (bot.origin, bot);
// set size and shape
setsize (bot, VEC_HULL2_MIN, VEC_HULL2_MAX);
bot.solid = SOLID_SLIDEBOX;
bot.movetype = MOVETYPE_STEP;
setmodel(bot, "progs/soldier.mdl");
bot.flags = bot.flags | FL_MONSTER;
bot.takedamage = DAMAGE_AIM;
// define his animation
bot.th_stand = army_stand1;
bot.th_walk = army_walk1;
bot.th_run = army_run1;
bot.th_die = army_die;
//bot.th_melee = ogre_melee;
bot.th_missile = army_atk1;
bot.th_pain = army_pain;
bot.health = 30;
// polish him up
bot.classname = "monster";
bot.ideal_yaw = bot.angles * '0 1 0';
bot.yaw_speed = 120;
bot.view_ofs = '0 0 22';
//bprint("an ogre joins the game\n");
// begin his thinking
bot.nextthink = time + 0.1 + random();
bot.think = bot.th_walk;
};
Code: Select all
/*
==============================================================================
gamerules
==============================================================================
*/
void () waverules =
{
if (waves == 0 && mkills == 1)
waves = 1;
mkills = 0;
if (waves == 1 && mkills == 2)
waves = 2;
mkills = 0;
};
void () monster_spawner =
{
local float soldiers;
self.classname = "monster_spawner";
while (waves == 0)
while (soldiers < 1)
{
create_armybot();
soldiers = soldiers +1;
return;
}
while (waves == 1)
return;
while (waves == 2)
return;
};
ps: i just fixed the problem with it switching my spawn spot!
lmao i had it like 99% fixed then this happened xD
okay back to 99% fixed. i'm thinking i have to remove the corpses. cause monsters arnt spawning while corpses remain. if they gib... they continue to respawn (i think) so for a temp fix for the moment. ill be gibbing bodies on death. just to see if it works. nope...
so now that i have things fixed-ish.. on the first wave 1 monster spawns as expected.. on the 2nd wave 2 spawns just as expected... on the 3rd wave... only 1 spawns ? why?
code update : http://pastebin.com/TMjUfNJg
I BELIEVE I HAVE IT FIXED! If anyone wants src for what i have for your own use let me know! right now it only supports 1 monster, but others should be easy changes!
Re: monster_spawn function?
okay in attempting to make my wave coding easier, i tried setting soldiers back to 0 at the beginning of a new round, which causes a problem like the one above where craploads of monsters keep spawn and continuously explode... how can i do this with out this problem?
Re: monster_spawn function?
Just for the record, RMQ also has monster summoning code where monsters are spawned "from thin air" at runtime. I probably got the most important parts from Drake, a mod that doesn't get enough credit by the way, and perhaps some from Hipnotic or so, I forget.
as stated, this requires external precaches to avoid an error
And a bit in blahmonster_start to make it go to FoundTarget() and deal with the monstercount
Code: Select all
// gb, spawns a monster - mostly for summoning spells
// Spawn must have an external precache function (blah_cache)
// which the summoner must call >> in his own spawn function! <<
// to avoid "precaches can only be done in spawn functions"
void(vector org, void() spawnfunc, string spawnclass) SpawnMonster =
{
local entity oldself;
oldself = self;
// gb, send totalmonsters update because most clients only check once when connecting
total_monsters = total_monsters + 1;
WriteByte (MSG_ALL, SVC_UPDATESTAT);
WriteByte (MSG_ALL, STAT_TOTALMONSTERS);
WriteLong (MSG_ALL, total_monsters);
self = spawn();
self.flags = 0;
self.spawnflags = 0;
self.enemy = oldself.enemy; // FoundTarget() called for summons in blahmonster_start
org_z = org_z + 64; // lift it up a bit, prevent flymonsters banging on the floor
setorigin (self, org);
self.spawned = TRUE; // bypass its precaches (summoner must call those)
self.classname = spawnclass;
self.angles = oldself.angles + '0 180 0'; // face summoner and hopefully enemy
spawnfunc(); // sneaky, huh?
// switch back
self = oldself;
// thanks to PM, wouldn't have figured this out myself
};
Code: Select all
void() monster_shalrath_go =
{
self.solid = SOLID_SLIDEBOX;
self.movetype = MOVETYPE_STEP;
setmodel (self, "progs/shalrath.mdl");
setsize (self, VEC_HULL2_MIN, VEC_HULL2_MAX);
if (self.spawned)
self.health = self.max_health = MonsterHealth(300); //ijed Spawned vores are weaker
else
self.health = self.max_health = MonsterHealth(400);
self.th_stand = shal_stand;
self.th_walk = shal_walk1;
self.th_run = shal_run1;
self.th_die = shalrath_die;
self.th_pain = shalrath_pain;
self.th_missile = shal_attack1;
self.th_submerged = CastPnakoticDefensive;
Gyro_Object_Activate (self, 100000);
if (self.spawned)
self.caster_level = 0; // something was badass enough to summon us, so let's join the party ijed But without magic...
self.magic_done = 1;
walkmonster_start ();
};
// support summoning
void () shalrath_cache =
{
precache_model2 ("progs/shalrath.mdl");
precache_model2 ("progs/h_shal.mdl");
precache_model2 ("progs/v_spike.mdl");
precache_model2 ("progs/deathslither.mdl"); //new spell
precache_sound2 ("magic/deathslither_hit.wav");
precache_sound2 ("shalrath/attack.wav");
precache_sound2 ("shalrath/attack2.wav");
precache_sound2 ("shalrath/death.wav");
precache_sound2 ("shalrath/idle.wav");
precache_sound2 ("shalrath/pain.wav");
precache_sound2 ("shalrath/sight.wav");
// Shn'tkk, AAahton
precache_sound2 ("shalrath/shalstep1.wav");
precache_sound2 ("shalrath/shalstep2.wav");
precache_sound2 ("shalrath/shalstep3.wav");
precache_sound2 ("shalrath/shalstep4.wav");
precache_sound2 ("shalrath/shalstep5.wav");
precache_sound2 ("shalrath/shalstep6.wav");
precache_sound2 ("shalrath/shalstep7.wav");
precache_sound2 ("shalrath/shalstep8.wav");
};
/*QUAKED monster_shalrath (1 0 0) (-32 -32 -24) (32 32 48) Ambush
*/
void() monster_shalrath =
{
if (deathmatch)
{
remove(self);
return;
}
if (!self.spawned) // This has to be done like this to avoid a crash it if summons itself
{
shalrath_cache(); // precaches
proto_cache(); // can summon proto-shalrath at caster_level 2, includes magic precaches
wizard_cache(); // summons hordes of wizards
}
self.go = monster_shalrath_go;
if(!CheckGroup()) self.go();
};
And a bit in blahmonster_start to make it go to FoundTarget() and deal with the monstercount
Code: Select all
void() walkmonster_start_go =
{
local string stemp;
local entity etemp;
self.origin_z = self.origin_z + 1; // raise off floor a bit
if (!(self.spawnflags & 64)) // SPAWNED, gb
{
droptofloor ();
if (!walkmove (0, 0))
{
dprint ("walkmonster in wall at: ");
dprint (vtos (self.origin));
dprint ("\n");
}
}
self.takedamage = DAMAGE_AIM;
/*
// DRS: Monster Combat think
// Supa, argh, don't do this, it makes Darkplaces cry! >:
if(!self.monsterthink)
self.monsterthink = SUB_Null;
*/
self.ideal_yaw = self.angles * '0 1 0';
if (!self.yaw_speed)
self.yaw_speed = 20;
self.view_ofs = '0 0 25';
self.use = monster_use;
// self.flags = self.flags | FL_MONSTER;
// Abort if monster got telefragged upon level placement.
if (self.health <= 0)
{
print_self ("walkmonster", "killed");
return;
}
if (self.spawned) // gb, summoned...
{
self.nextthink = time + 0.1;
self.think = FoundTarget;
return;
}
if (self.target)
{
self.goalentity = self.movetarget = find(world, targetname, self.target);
if (self.h2olevel < 3) // not submerged
self.ideal_yaw = vectoyaw(self.goalentity.origin - self.origin);
if (!self.movetarget)
print_self ("walkmonster", "can't find target");
// this used to be an objerror
if (self.movetarget.classname == "path_corner")
self.th_walk ();
else
{
self.pausetime = 99999999;
self.th_stand ();
}
}
else
{
self.pausetime = 99999999;
self.th_stand ();
}
// spread think times so they don't all happen at same time
self.nextthink = self.nextthink + random()*0.5;
// gb, func_hordespawn
if (self.awake == 1 && !(self.enemy.items & IT_INVISIBILITY) && !(self.enemy.flags & FL_NOTARGET) && self.enemy.classname == "player")
{
self.nextthink = time + 0.1;
self.think = FoundTarget;
}
};
void () walkmonster_start =
{
if ((self.spawnflags & 3) || self.targetname)
self.goalentity = self; //ijed Hack that stops monsters from wandering whilst asleep or ambush
self.flags = self.flags | FL_MONSTER; // Supa, if it's stupid and it works..
if ((self.spawnflags & 64)) // SPAWNED, gb
{
if (self.targetname == string_null) //ijed Alerts mapper to lost monsters caused by broken spawns
{
dprint ("walkmonster at ");
dprint (vtos (self.origin));
dprint ("is set as spawned but has no targetname\n");
}
self.wad = self.model;
self.pos1 = self.mins;
self.pos2 = self.maxs;
self.walkframe = self.solid;
self.fly_sound = self.movetype;
if (!self.statue) // gb
{
self.model = "";
self.solid = SOLID_NOT;
}
self.movetype = MOVETYPE_NONE;
self.use = monster_teleport;
self.think1 = walkmonster_start_go;
if (self.statue)
{
self.takedamage = DAMAGE_AIM; // this makes them awaken when shot at
droptofloor();
}
if (!(self.specialflags & GOOD) ) // Supa, friendly NPCs don't show up on killcount
if (!(self.specialflags & IMMORTAL) ) // + immortals too
total_monsters = total_monsters + 1;
return;
}
self.nextthink = time + (random() * 0.5);
self.think = walkmonster_start_go;
if (!(self.specialflags & GOOD) ) // Supa, friendly NPCs don't show up on killcount
if (!(self.specialflags & IMMORTAL) ) // + immortals too
if (!(self.spawned)) // gb, summon spell sends totalmonsters update itself
total_monsters = total_monsters + 1;
};