Forum

monster_spawn function?

Discuss programming in the QuakeC language.

Moderator: InsideQC Admins

monster_spawn function?

Postby ceriux » Mon Mar 12, 2012 6:02 am

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?
User avatar
ceriux
 
Posts: 2223
Joined: Sat Sep 06, 2008 3:30 pm
Location: Indiana, USA

Re: monster_spawn function?

Postby Baker » Mon Mar 12, 2012 8:02 am

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 ..
User avatar
Baker
 
Posts: 3666
Joined: Tue Mar 14, 2006 5:15 am

Re: monster_spawn function?

Postby andrewj » Mon Mar 12, 2012 10:35 am

I think it would go something like this:
Code: Select all
  self = spawn();
  self.origin = '0 0 0';  // put some calculation here!
  monster_army();
andrewj
 
Posts: 133
Joined: Mon Aug 30, 2010 3:29 pm
Location: Australia

Re: monster_spawn function?

Postby Ace12GA » Mon Mar 12, 2012 1:20 pm

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.
Ace12GA
 
Posts: 56
Joined: Sat Jan 28, 2012 12:08 am

Re: monster_spawn function?

Postby ceriux » Mon Mar 12, 2012 5:29 pm

thanks for the info guys ill be sure to look into it all.
User avatar
ceriux
 
Posts: 2223
Joined: Sat Sep 06, 2008 3:30 pm
Location: Indiana, USA

Re: monster_spawn function?

Postby ceriux » Mon Mar 12, 2012 6:19 pm

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?
User avatar
ceriux
 
Posts: 2223
Joined: Sat Sep 06, 2008 3:30 pm
Location: Indiana, USA

Re: monster_spawn function?

Postby Ace12GA » Mon Mar 12, 2012 7:10 pm

The trick is to spawn the monster you want.

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;


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


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...
Ace12GA
 
Posts: 56
Joined: Sat Jan 28, 2012 12:08 am

Re: monster_spawn function?

Postby ceriux » Tue Mar 13, 2012 5:51 am

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
User avatar
ceriux
 
Posts: 2223
Joined: Sat Sep 06, 2008 3:30 pm
Location: Indiana, USA

Re: monster_spawn function?

Postby Ace12GA » Tue Mar 13, 2012 12:44 pm

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.
Ace12GA
 
Posts: 56
Joined: Sat Jan 28, 2012 12:08 am

Re: monster_spawn function?

Postby ajay » Tue Mar 13, 2012 7:59 pm

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
User avatar
ajay
 
Posts: 559
Joined: Fri Oct 29, 2004 6:44 am
Location: Swindon, UK

Re: monster_spawn function?

Postby frag.machine » Wed Mar 14, 2012 1:28 am

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


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


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


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


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


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)
User avatar
frag.machine
 
Posts: 2090
Joined: Sat Nov 25, 2006 1:49 pm

Re: monster_spawn function?

Postby ceriux » Wed Mar 14, 2012 2:37 am

well, when i get my code bug free. ill post the src up.
User avatar
ceriux
 
Posts: 2223
Joined: Sat Sep 06, 2008 3:30 pm
Location: Indiana, USA

Re: monster_spawn function?

Postby ceriux » Wed Mar 14, 2012 3:55 am

thanks for the replys so far... i have it working kinda. cept i have a few problems...


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


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!
User avatar
ceriux
 
Posts: 2223
Joined: Sat Sep 06, 2008 3:30 pm
Location: Indiana, USA

Re: monster_spawn function?

Postby ceriux » Fri Mar 16, 2012 4:44 am

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?
User avatar
ceriux
 
Posts: 2223
Joined: Sat Sep 06, 2008 3:30 pm
Location: Indiana, USA

Re: monster_spawn function?

Postby goldenboy » Fri Mar 16, 2012 5:34 pm

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


as stated, this requires external precaches to avoid an error

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;
};
User avatar
goldenboy
 
Posts: 924
Joined: Fri Sep 05, 2008 11:04 pm
Location: Kiel

Next

Return to QuakeC Programming

Who is online

Users browsing this forum: No registered users and 1 guest