Inside3D!
     

Monsters vs. Monsters

 
Post new topic   Reply to topic    Inside3d Forums Forum Index -> QuakeC Programming
View previous topic :: View next topic  
Author Message
redrum



Joined: 28 Mar 2007
Posts: 367
Location: Long Island, New York

PostPosted: Sun Apr 26, 2009 12:33 am    Post subject: Monsters vs. Monsters Reply with quote

Whats the easiest way to get monsters to fight each other?
_________________
Welcome to the Overlook Hotel 69.113.123.178:27500
Back to top
View user's profile Send private message Send e-mail
ceriux



Joined: 06 Sep 2008
Posts: 969
Location: Florida, USA

PostPosted: Sun Apr 26, 2009 1:38 am    Post subject: Reply with quote

classnames and targets?
_________________
QuakeDB - Quake ModDB Group
Back to top
View user's profile Send private message Yahoo Messenger
Wazat



Joined: 15 Oct 2004
Posts: 732
Location: Middle 'o the desert, USA

PostPosted: Sun Apr 26, 2009 2:03 pm    Post subject: Reply with quote

Well, monster infighting lets monsters fight each other when one hurts the other. You can make this more normal by enhancing the monster search-for-players function (can't remember what the blasted thing is called) so that it searches for other monsters too, that have a different classname or are of a monster type that this monster doesn't like (however you want to define that).

So monsters will prioritize killing the player, but if they don't find him they'll turn on each other. Makes the player's job easier -- nearly empty map soon after he spawns, and what's left is pretty hurt. Smile
_________________
When my computer inevitably explodes and kills me, my cat inherits everything I own. He may be the only one capable of continuing my work.
Back to top
View user's profile Send private message MSN Messenger
redrum



Joined: 28 Mar 2007
Posts: 367
Location: Long Island, New York

PostPosted: Sun Apr 26, 2009 9:37 pm    Post subject: Reply with quote

Here's the code:

float() FindTarget =
{
local entity client;
local float r;
local vector dist;
dist = self.enemy.origin - self.origin;

// if the first spawnflag bit is set, the monster will only wake up on
// really seeing the player, not another monster getting angry

// spawnflags & 3 is a big hack, because zombie crucified used the first
// spawn flag prior to the ambush flag, and I forgot about it, so the second
// spawn flag works as well


if (sight_entity_time >= time - 0.1 && !(self.spawnflags & 3) )
{
client = sight_entity;
if (client.enemy == self.enemy)
return;
}
else
{
client = checkclient ();
if (!client)
return FALSE; // current check entity isn't in PVS
}


if (client == self.enemy)
return FALSE;

if (client.flags & FL_NOTARGET)
return FALSE;
if (client.items & IT_INVISIBILITY)
return FALSE;

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 ( /* client.show_hostile < time || */ !infront (client))
return FALSE;
}

self.enemy = client;
if (self.enemy.classname != "player")
{
self.enemy = self.enemy.enemy;
if (self.enemy.classname != "player")
{
self.enemy = world;
return FALSE;
}
}

FoundTarget ();

if (self.health <= 35) // make the blood
Bleeding1(self);

return TRUE;

if (self.watertype == CONTENT_LAVA) // do lava damage
T_Damage (self, world, world, 10*self.waterlevel);

if (self.watertype == CONTENT_SLIME) // do slime damage
T_Damage (self, world, world, 6*self.waterlevel);

if (self.watertype == CONTENT_WATER) // do water damage
T_Damage (self, world, world, 3*self.waterlevel);
};[code]

I'm an average coder, this seemingly simple task is driving me nutz!
I've tried numerous code changes to no avail.
Can someone please point me in the right direction. Thanks.
_________________
Welcome to the Overlook Hotel 69.113.123.178:27500
Back to top
View user's profile Send private message Send e-mail
MauveBib



Joined: 04 Nov 2004
Posts: 602

PostPosted: Sun Apr 26, 2009 9:50 pm    Post subject: Reply with quote

It's not quite as simple as it seems unfortunately. The function the monsters use to find players to attack is a built-in function of the quake engine called checkclient(), which returns a client entity, a different one each turn. There is no equivilent built-in function for finding monsters, so you have to make your own, something along these lines:

Code:

entity() findmonster =
{
   local entity guy, found;
   local float closest;

   closest = 1000;

   guy = findradius(self.origin, closest);
   
   while(guy)
   {
      if ((guy.health > 0) && (guy != self))
      {
         if (vlen(guy.origin - self.origin < closest)
         {
            if ((visible(guy)) && (infront(guy)))
            {
               closest = vlen(guy.origin - self.origin);
               found = guy;
            }
         }
      }
      guy = guy.chain;
   }

   return found;
};


This function shouldreturn the nearest visible monster or player. It can easily be altered to just find monsters by changing this line:

if ((guy.health > 0) && (guy != self))

to this:

if ((guy.health > 0) && (guy != self) && (guy.flags & FL_MONSTER))


Do you want the monsters to only attack other monsters? Or particular other monsters? Or both monsters and the player? The answer to this question leads into how you use this function to achieve what you want.
_________________
Apathy Now!
Back to top
View user's profile Send private message
redrum



Joined: 28 Mar 2007
Posts: 367
Location: Long Island, New York

PostPosted: Sun Apr 26, 2009 10:28 pm    Post subject: Reply with quote

ok, that clears things up a bit, thanks.

I would like that soldiers attack everything except other soldiers. Ogres attack everything except ogres.....

I'll give it a shot on my own. I'll reply back if I get into trouble!
_________________
Welcome to the Overlook Hotel 69.113.123.178:27500
Back to top
View user's profile Send private message Send e-mail
Electro



Joined: 29 Dec 2004
Posts: 241
Location: Brisbane, Australia

PostPosted: Tue Apr 28, 2009 5:30 am    Post subject: Reply with quote

In that case, check to make sure they're not the same classname...

Code:

entity() findmonster =
{
   local entity guy, found;
   local float closest;

   closest = 1000;

   guy = findradius(self.origin, closest);
   
   while(guy)
   {
      if ((guy.health > 0) && (guy != self))
      {
         if (vlen(guy.origin - self.origin < closest)
         {
            if ((visible(guy)) && (infront(guy)))
            {
               if (guy.classname != self.classname)
               {
                  closest = vlen(guy.origin - self.origin);
                  found = guy;
               }
            }
         }
      }
      guy = guy.chain;
   }

   return found;
};

_________________
Unit reporting!
http://www.bendarling.net/
Back to top
View user's profile Send private message Visit poster's website MSN Messenger
redrum



Joined: 28 Mar 2007
Posts: 367
Location: Long Island, New York

PostPosted: Wed Apr 29, 2009 4:29 pm    Post subject: Reply with quote

Thanks!
_________________
Welcome to the Overlook Hotel 69.113.123.178:27500
Back to top
View user's profile Send private message Send e-mail
ajay



Joined: 29 Oct 2004
Posts: 295
Location: Swindon, UK

PostPosted: Wed Apr 29, 2009 6:42 pm    Post subject: Reply with quote

I played around with this while making the final levels of Lunkin's Journey. the plan was to have a huge mass battle between NPCs. I got them finding each other and attacking each other. My aged memory being what is, I can't remember exact details but there was a major problem with it in large groups; basically all of one group would attack just one of the other until that one died, rather than lots of different battles going on. I may be wrong, but I think they also then failed to find another from the other group to atatck, but I may ahve solved that.
_________________
my site
Back to top
View user's profile Send private message MSN Messenger
Wazat



Joined: 15 Oct 2004
Posts: 732
Location: Middle 'o the desert, USA

PostPosted: Thu Apr 30, 2009 3:55 am    Post subject: Reply with quote

Well, if you are just taking the first target you find, or the closest target, then it's easy for a whole group to target one unit instead of breaking off against different targets. There are many ways to do something about that:

1) While looping through targets finding the best, closest one, add randomness. When you're comparing the distance to the current subject vs the best distance so far, multiply the distance by (1 + random()*.5). That randomly adds 50% distance and will jumble the targets a bit, causing monsters to not always pick the best target so that a group will have a better chance of splitting up their enemies. +25% random probably works just as well.

2) While looping through enemies and looking for the best target, factor the number of monsters attacking that enemy into the "best" rating. This increases the number of iterations in your loop (nxm instead of just n iterations), but it's another easy way to write the logic. If many monsters are attacking that enemy, it becomes less desirable to attack than the enemy that is a little farther away.

3) If you don't parse through all possible targets and select the best one by distance (but instead just select the first target you find that you can see, the way monsters do with players now), this is an option. Instead of starting the loop with the first entity in the list, remember the last entity that any monster searched for and start with the one after that one. This is similar to how the player selects a deathmatch spawn point.

4) Combine one of the methods above with this one: Occasionally monsters re-search for a target and switch to the new one found if it doesn't match their old one.
_________________
When my computer inevitably explodes and kills me, my cat inherits everything I own. He may be the only one capable of continuing my work.
Back to top
View user's profile Send private message MSN Messenger
redrum



Joined: 28 Mar 2007
Posts: 367
Location: Long Island, New York

PostPosted: Mon May 04, 2009 9:45 pm    Post subject: Reply with quote

How do I implement the new code?

I added it to this code:
Code:
void(float dist) ai_walk =
{
   movedist = dist;
   
   if (self.classname == "monster_dragon")
   {
      movetogoal (dist);
      return;
   }

   if (FindMonster ())                                                                       // check for noticing a monster
      return;

   if (FindTarget ())                                                                       // check for noticing a player
      return;

   movetogoal (dist);

        if (self.health <= 35)                                                                    //make the blood
           Bleeding1(self);

   if (self.watertype == CONTENT_LAVA)                                                       // do lava damage
      T_Damage (self, world, world, 10*self.waterlevel);

   if (self.watertype == CONTENT_SLIME)                                                      // do slime damage
      T_Damage (self, world, world, 6*self.waterlevel);

   if (self.watertype == CONTENT_WATER)                                                      // do water damage
      T_Damage (self, world, world, 3*self.waterlevel);
};


Now they just walk in place. I'm using QW if that makes a difference.
I noticed that this code is entity() instead of void(). Can someone explain in laymans terms what is actually happening here?
_________________
Welcome to the Overlook Hotel 69.113.123.178:27500
Back to top
View user's profile Send private message Send e-mail
MauveBib



Joined: 04 Nov 2004
Posts: 602

PostPosted: Mon May 04, 2009 10:00 pm    Post subject: Reply with quote

the key difference between entity() and void() is what is returned by the function.

A void() function returns nothing, but an entity() function (or a float() or vector() function) returns a value (be it an entity, float or vector as appropriate.

Therefore, in order to use the function you'll need to do something along the lines of:

Code:

self.enemy = FindMonster();
if (self.enemy)
     return;


The code you've written is what would be appropriate if FindMonster were a float().
_________________
Apathy Now!
Back to top
View user's profile Send private message
redrum



Joined: 28 Mar 2007
Posts: 367
Location: Long Island, New York

PostPosted: Mon May 04, 2009 10:25 pm    Post subject: Reply with quote

I tried this:
Code:
void(float dist) ai_walk =
{
   movedist = dist;
   
   if (self.classname == "monster_dragon")
   {
      movetogoal (dist);
      return;
   }

        self.enemy = FindMonster();

        if (FindMonster ())
           return;

   if (FindTarget ())                                                                       // check for noticing a player
      return;

   movetogoal (dist);

        if (self.health <= 35)                                                                    //make the blood
           Bleeding1(self);

   if (self.watertype == CONTENT_LAVA)                                                       // do lava damage
      T_Damage (self, world, world, 10*self.waterlevel);

   if (self.watertype == CONTENT_SLIME)                                                      // do slime damage
      T_Damage (self, world, world, 6*self.waterlevel);

   if (self.watertype == CONTENT_WATER)                                                      // do water damage
      T_Damage (self, world, world, 3*self.waterlevel);
};

Still not working  :(

Maybe a QW thing?
_________________
Welcome to the Overlook Hotel 69.113.123.178:27500
Back to top
View user's profile Send private message Send e-mail
MauveBib



Joined: 04 Nov 2004
Posts: 602

PostPosted: Mon May 04, 2009 10:47 pm    Post subject: Reply with quote

No. The code you've used won't work, as you're not telling it what to do once it has an enemy. It has to change think functions and whatnot, which is done by the FoundTarget() function.

Try this for a failsafe:

Code:

if (!self.enemy)
     self.enemy = FindMonster();

if (self.enemy)
{
     FoundTarget();
     return;
}

_________________
Apathy Now!
Back to top
View user's profile Send private message
redrum



Joined: 28 Mar 2007
Posts: 367
Location: Long Island, New York

PostPosted: Tue May 05, 2009 12:01 am    Post subject: Reply with quote

ok, thanks for your help. It's working now. Just gotta make some tweaks.
_________________
Welcome to the Overlook Hotel 69.113.123.178:27500
Back to top
View user's profile Send private message Send e-mail
Display posts from previous:   
Post new topic   Reply to topic    Inside3d Forums Forum Index -> QuakeC Programming All times are GMT
Page 1 of 1

 
Jump to:  
You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot vote in polls in this forum


Powered by phpBB © 2004 phpBB Group