Forum

endless enemy spawn

Discuss programming in the QuakeC language.

Moderator: InsideQC Admins

Postby Hazematman » Fri Oct 15, 2010 10:25 pm

why don't you do something like what frag.machine suggested above and create an entity and when the zombie dies it will look for the coordinates of that entity and spawn there?
User avatar
Hazematman
 
Posts: 54
Joined: Thu Jul 15, 2010 1:58 am
Location: Canada

Postby Mexicouger » Fri Oct 15, 2010 10:30 pm

frag.machine wrote:No exactly. Ok, let's start from start.

What you need is to create a new map entity that works pretty much like the info_player_start: it's something that you put in a map to tell Quake "I want to spawn zombies in this place".

This thing (let's call it "info_zombie_start" to make things easier) will work in the following way:

1) when spawned, the first thing it does is precache all models and sounds used by the zombie;
2) we want to spawn a new zombie once ours is killed, so we need to track it. To do this, we can either create a specific field in the entity structure (for example, ".entity myzombie;" at the end of defs.qc) or reuse one of the already existing .entity fields. Since our info_zombie_start is not a monster, it won't have enemies, so we can reuse the .enemy field (and save a couple bytes);
3) we need to set a "think" function to the info_zombie_start to check, every fews seconds, if the zombie pointed by .enemy is dead:
Code: Select all
 if (self.enemy.health <= 0)

If that's the case, we create a new entity, set its origin to the info_zombie_start origin (plus a bit in the z axis so the zombie don't get stuck in ground) and we can repeat the initialization steps that you can see in zombie.qc (setmodel, setsize, etc). Then, we store a reference to the new zombie into our .enemy field, and we can schedule our return to the checking loop above.

There are a lot more of details, but this covers the main idea.

Heh. I did mine a bit differently. I spawned an object in the map that precaches all the Stuff for that monster, and then it removed itself from the world.
User avatar
Mexicouger
 
Posts: 514
Joined: Sat May 01, 2010 10:12 pm

Postby frag.machine » Sat Oct 16, 2010 4:52 pm

There are many ways to do that. I'll show you how I did (yeah, I tested this in a map and worked like a charm, so feel free to use it):
Code: Select all
/**
 * info_zombie_start.qc
 **/
void () info_zombie_think =
{
    local entity zomb, oldself;

    // do we have a zombie under our control ?
    if (self.enemy != world)
    {
        // is it still alive ? (okay, it's a zombie, so
        // it's an undead by definition, but you know
        // what I mean :P )
        if (self.enemy.health <= 0)
        {
            // it's dead, Jim. Clean up the mess.
            self.enemy = world;
        }
    }

    if (self.enemy == world)
    {
        // time for another zombie!

        // we temporarily change the "self"
        // reference to our new zombie
        oldself = self;
        self = spawn ();

        // here we basically copy & paste the
        // original monster_zombie code, with
        // a couple changes
        setorigin (self, oldself.origin);
        self.origin_z = self.origin_z + 2;
        self.solid = SOLID_SLIDEBOX;
        self.movetype = MOVETYPE_STEP;
        setmodel (self, "progs/zombie.mdl");
        setsize (self, '-16 -16 -24', '16 16 40');
        self.health = 60;
        self.th_stand = zombie_stand1;
        self.th_walk = zombie_walk1;
        self.th_run = zombie_run1;
        self.th_pain = zombie_pain;
        self.th_die = zombie_die;
        self.th_missile = zombie_missile;

        // this is a little trick, so we can make
        // the just born zombie to follow a path,
        // if we want (you'll find it useful to
        // spread info_zombie_starts around
        // a map and make them follow predefined
        // routes, like if they were hunting the
        // player from ewverywhere
        if (oldself.target != string_null)
        {
            self.target = oldself.target;
        }

        walkmonster_start();

        // we store a reference to our new zombie
        oldself.enemy = self;

        // then we restore "self" to point back to
        // our info_zombie_start
        self = oldself;
    }

    // finally, let's schedule the execution of our "think" function
    self.think = info_zombie_think;

    self.nextthink = time + 0.5;
};

void () info_zombie_start =
{
    // if is deathmatch, we don't want zombies popping around (otherwise, just omit this part)
    if (deathmatch)
    {
        remove (self);
    }

    // we need to precache all stuff used by the monster_zombie
    precache_model ("progs/zombie.mdl");
    precache_model ("progs/h_zombie.mdl");
    precache_model ("progs/zom_gib.mdl");
    precache_sound ("zombie/z_idle.wav");
    precache_sound ("zombie/z_idle1.wav");
    precache_sound ("zombie/z_shot1.wav");
    precache_sound ("zombie/z_gib.wav");
    precache_sound ("zombie/z_pain.wav");
    precache_sound ("zombie/z_pain1.wav");
    precache_sound ("zombie/z_fall.wav");
    precache_sound ("zombie/z_miss.wav");
    precache_sound ("zombie/z_hit.wav");
    precache_sound ("zombie/idle_w2.wav");

    // we will reuse one of the entity fields - in this case,
    // the .enemy field - to keep a reference to our pet zombie,
    // so we can know when to spawn another one. This field need
    // to start with a blank reference, so let's point it to world
    self.enemy = world;

// finally, let's schedule the execution of our "think" function
    self.think = info_zombie_think;
    self.nextthink = time + 0.1;
};


Now all you need to do is create a map and place the info_zombie_start where you want the zombies to appear. If you want to make the just spawned zombies to follow paths, set the .target value to the info_zombie_spawn to point to a path_corner (like you usually would to a monster). Have fun.
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

Postby Hazematman » Sun Oct 17, 2010 1:30 am

Thanks Frag.machine, I added that code to my mod, and it worked pretty well, except for the fact that the ai kinda messed up after you kill the zombie but I will look into that. Heres a video of the mod http://www.youtube.com/watch?v=-73d-cuFti4 (The quality is not that good.)
User avatar
Hazematman
 
Posts: 54
Joined: Thu Jul 15, 2010 1:58 am
Location: Canada

Postby frag.machine » Sun Oct 17, 2010 3:26 am

What exactly haven't worked as expected when you say "the ai kinda messed up" ? I can't tell what's wrong by your video.
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

Postby Hazematman » Sun Oct 17, 2010 4:14 pm

oh sorry, after the zombie dies, the new one won't attack you.
User avatar
Hazematman
 
Posts: 54
Joined: Thu Jul 15, 2010 1:58 am
Location: Canada

Postby hondobondo » Mon Oct 18, 2010 12:27 am

Hazematman wrote:oh sorry, after the zombie dies, the new one won't attack you.


go to FindTarget in ai.qc and remove these lines
Code: Select all
   r = range (client);
   if ( (r == RANGE_FAR) ) {

      return ( FALSE );

   }
   if ( !visible (client) ) {

      return ( FALSE );

   }
   if ( (r == RANGE_NEAR) ) {

      if ( ((client.show_hostile < time) && !infront (client)) ) {

         return ( FALSE );

      }

   } else {

      if ( (r == RANGE_MID) ) {

         if ( !infront (client) ) {

            return ( FALSE );

         }

      }

   }


this should make the monsters roam around
hondobondo
 
Posts: 207
Joined: Tue Sep 26, 2006 2:48 am

Postby frag.machine » Mon Oct 18, 2010 12:27 am

Hmm, haven't observed that in my tests.
Does the first zombie attacks you ? You can do something like this to help the AI, when creating the new zombie:

Code: Select all
  // after this line:
  self.th_missile = zombie_missile;
 
  // add this:
  self.enemy = oldself.enemy.enemy;

  // just to remember:
  // self = the new zombie;
  // oldself = the info_zombie_start
  // oldself.enemy = the killed zombie
  // oldself.enemy.enemy = who killed the zombie (usually, the player)

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

Postby hondobondo » Mon Oct 18, 2010 12:38 am

frag.machine wrote:The problem is a bit more complicated than this.

What you need is an entity that spawns another entity (the zombie), and keep checking if it stills alive. If is dead, then spawn another one, and repeat.


actually it's not.

i put a call to this function at the end of knight_die, whether or not he gets gibbed:

Code: Select all
void() monster_knight_respawn =
{

   local entity new,temp;
   
   new = spawn();
   
   traceline(self.origin,self.origin + '40 0 0',TRUE,self); //ignore monsters, telefrag if in the way
   if (trace_fraction == TRUE)   //no wall in the way
      setorigin(new,trace_endpos+'0 0 32');
   else
   {
      traceline(self.origin,self.origin + '0 40 0',TRUE,self); //ignore monsters, telefrag if in the way
      if (trace_fraction == TRUE)
         setorigin(new,trace_endpos+'0 0 32');
      else
      {
         traceline(self.origin,self.origin + '0 -40 0',TRUE,self); //ignore monsters, telefrag if in the way
         if (trace_fraction == TRUE)
            setorigin(new,trace_endpos+'0 0 32');
         else
         {
            traceline(self.origin,self.origin + '-40 0 0',TRUE,self); //ignore monsters, telefrag if in the way
            if (trace_fraction == TRUE)
               setorigin(new,trace_endpos+'0 0 32');
            else
               return;   //can't spawn
         }
      }
   }
   
   spawn_tfog(new.origin);
   spawn_tdeath (new.origin,new);
   
   new.solid = SOLID_SLIDEBOX;
   new.movetype = MOVETYPE_STEP;

   setmodel (new, "progs/knight.mdl");

   setsize (new, '-16 -16 -24', '16 16 40');
   new.health = 75;

   new.th_stand = knight_stand1;
   new.th_walk = knight_walk1;
   new.th_run = knight_run1;
   new.th_melee = knight_atk1;
   new.th_pain = knight_pain;
   new.th_die = knight_die;
   
   temp = self;
   self = new;
   walkmonster_start ();
   new = self;
   self = temp;
   
   remove(self);
};


here's the die function:

Code: Select all
void () monster_knight_respawn;

void() knight_die =
{
// check for gib
   if (self.health < -40)
   {
      sound (self, CHAN_VOICE, "player/udeath.wav", 1, ATTN_NORM);
      ThrowHead ("progs/h_knight.mdl", self.health);
      ThrowGib ("progs/gib1.mdl", self.health);
      ThrowGib ("progs/gib2.mdl", self.health);
      ThrowGib ("progs/gib3.mdl", self.health);
      self.think = monster_knight_respawn;
      self.nextthink = time + random()*3 + 2;
      return;
   }

// regular death
   sound (self, CHAN_VOICE, "knight/kdeath.wav", 1, ATTN_NORM);
   if (random() < 0.5)
      knight_die1 ();
   else
      knight_dieb1 ();

   self.think = monster_knight_respawn;
   self.nextthink = time + random()*3 + 2;
};


the respawn function checks around the spot where the knight dies, if a wall isn't nearby, it spawns a knight nearby. i had it spawning right on top of the dead knight but that caused problems, but i think that problem was solved with
Code: Select all
remove(self);

but i didn't test that. anyway, try it
hondobondo
 
Posts: 207
Joined: Tue Sep 26, 2006 2:48 am

Postby Spirit » Fri Feb 18, 2011 11:46 am

Using hondobondo's code (thanks!) I now get monsters that arrange in a nice circle around me but stay on a distance. Is that a bug in the code above or did I screw up something (in my horrid code base)?
Improve Quaddicted, send me a pull request: https://github.com/SpiritQuaddicted/Quaddicted-reviews
Spirit
 
Posts: 1031
Joined: Sat Nov 20, 2004 9:00 pm

Postby hondobondo » Tue Feb 22, 2011 3:04 am

Spirit wrote:Using hondobondo's code (thanks!) I now get monsters that arrange in a nice circle around me but stay on a distance. Is that a bug in the code above or did I screw up something (in my horrid code base)?


not sure what you mean. maybe you could post a screenshot
hondobondo
 
Posts: 207
Joined: Tue Sep 26, 2006 2:48 am

Previous

Return to QuakeC Programming

Who is online

Users browsing this forum: No registered users and 1 guest