FAQ  •  Register  •  Login

monster_spawn function?

Moderator: Inside3D Admins

<<

ceriux

User avatar

Posts: 1853

Joined: Sat Sep 06, 2008 3:30 pm

Location: Florida, USA

Post Mon Mar 12, 2012 6:02 am

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?
QuakeDB - if you have any cool quake video's please feel free to upload them on my moddb group!
<<

Baker

User avatar

Posts: 3180

Joined: Tue Mar 14, 2006 5:15 am

Post Mon Mar 12, 2012 8:02 am

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: 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? 8) Inquisitive minds want to know ! And if they don't -- well like that ever has stopped me before ..
<<

andrewj

Posts: 132

Joined: Mon Aug 30, 2010 3:29 pm

Location: Tasmania

Post Mon Mar 12, 2012 10:35 am

Re: monster_spawn function?

I think it would go something like this:
  Code:
  self = spawn();
  self.origin = '0 0 0';  // put some calculation here!
  monster_army();
<<

Ace12GA

Posts: 54

Joined: Sat Jan 28, 2012 12:08 am

Post Mon Mar 12, 2012 1:20 pm

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: 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.
<<

ceriux

User avatar

Posts: 1853

Joined: Sat Sep 06, 2008 3:30 pm

Location: Florida, USA

Post Mon Mar 12, 2012 5:29 pm

Re: monster_spawn function?

thanks for the info guys ill be sure to look into it all.
QuakeDB - if you have any cool quake video's please feel free to upload them on my moddb group!
<<

ceriux

User avatar

Posts: 1853

Joined: Sat Sep 06, 2008 3:30 pm

Location: Florida, USA

Post Mon Mar 12, 2012 6:19 pm

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?
QuakeDB - if you have any cool quake video's please feel free to upload them on my moddb group!
<<

Ace12GA

Posts: 54

Joined: Sat Jan 28, 2012 12:08 am

Post Mon Mar 12, 2012 7:10 pm

Re: monster_spawn function?

The trick is to spawn the monster you want.

  Code:
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;


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:

  Code:
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;
}


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...
<<

ceriux

User avatar

Posts: 1853

Joined: Sat Sep 06, 2008 3:30 pm

Location: Florida, USA

Post Tue Mar 13, 2012 5:51 am

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
QuakeDB - if you have any cool quake video's please feel free to upload them on my moddb group!
<<

Ace12GA

Posts: 54

Joined: Sat Jan 28, 2012 12:08 am

Post Tue Mar 13, 2012 12:44 pm

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.
<<

ajay

User avatar

Posts: 471

Joined: Fri Oct 29, 2004 6:44 am

Location: Swindon, UK

Post Tue Mar 13, 2012 7:59 pm

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
<<

frag.machine

User avatar

Posts: 1473

Joined: Sat Nov 25, 2006 1:49 pm

Post Wed Mar 14, 2012 1:28 am

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:
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


and change to this:
  Code:
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


after this, at the end of the file, add these wrapper functions:
  Code:
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);
};


This way if any precache_* function is called in the first 3 seconds, it's treated by the regular builtins, otherwise they are silently ignored.
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:
// 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;
};


To fire your new monster spawning spell, add a call to ImpulseCommands():
  Code:
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;
};


Lastly, "bind space impulse 100" and make dogs rain over your head :D
I know FrikaC made a cgi-bin version of the quakec interpreter once and wrote part of his website in QuakeC :) (LordHavoc)
<<

ceriux

User avatar

Posts: 1853

Joined: Sat Sep 06, 2008 3:30 pm

Location: Florida, USA

Post Wed Mar 14, 2012 2:37 am

Re: monster_spawn function?

well, when i get my code bug free. ill post the src up.
QuakeDB - if you have any cool quake video's please feel free to upload them on my moddb group!
<<

ceriux

User avatar

Posts: 1853

Joined: Sat Sep 06, 2008 3:30 pm

Location: Florida, USA

Post Wed Mar 14, 2012 3:55 am

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.

  Code:
/*
============
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:
// ------------------------------------------------
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:
/*
==============================================================================

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;
            
            
      };


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

Image

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!
QuakeDB - if you have any cool quake video's please feel free to upload them on my moddb group!
<<

ceriux

User avatar

Posts: 1853

Joined: Sat Sep 06, 2008 3:30 pm

Location: Florida, USA

Post Fri Mar 16, 2012 4:44 am

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?
QuakeDB - if you have any cool quake video's please feel free to upload them on my moddb group!
<<

goldenboy

User avatar

Posts: 726

Joined: Fri Sep 05, 2008 11:04 pm

Location: Kiel

Post Fri Mar 16, 2012 5:34 pm

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.

  Code:
// 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
};


as stated, this requires external precaches to avoid an error

  Code:
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:
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;
};
Next

Return to QuakeC Programming

Who is online

Users browsing this forum: No registered users and 0 guests

Powered by phpBB © 2000, 2002, 2005, 2007 phpBB Group.
Designed by ST Software for PTF.
Icons provided by Aha Soft