monsters fighting each other

Discuss programming in the QuakeC language.
Post Reply
ajay
Posts: 559
Joined: Fri Oct 29, 2004 6:44 am
Location: Swindon, UK

monsters fighting each other

Post by ajay »

Oh boy this is irritating me, and i've hung off posting about it for ages; mostly 'cos I really thought I knew enough by now to work it out for myself.
I want 2 types of monsters fight each other on sight - like they would on seeing the player. I can get an individual monster (e.g. one of a type) but groups of two types just don't work.
It's the kind of thing I would have guessed had a relative simple solution; but it doesn't appear to. I've tinkered (and I'm missing out what i've tried, cos firstly, I'm slightly embaressed byt what I've gone through,a nd secondly, there's too many to list!) all over ai.qc and even fight.qc, but to no avail.
I would really appreciated some nudging in the right direction, it's kind of crucial to my mod.
cheers
ajay
Sajt
Posts: 1215
Joined: Sat Oct 16, 2004 3:39 am

Post by Sajt »

I haven't really ever looked at the Quake AI code to much extent, but I think the function you're looking for is FindTarget. Be warned however that monsters sighting monsters will be slower than monsters sighting players, because vs players they can use a fast PVS solution, but the Quake builtins don't have a function to check for anything other than clients by PVS. Therefore you must use find() or findradius() (the sighting players code used checkclient()).

checkclient will return a client in the current PVS of 'self'. If there is more than one client in the PVS, it will cycle through them each time it is called, or each frame, or every 0.1 seconds or something (not sure), so every time you call checkclient() you will get the next player in the PVS.

Instead of that you will probably have to do something such as

ent = findradius (self, 1024);
while (ent != world)
{
if ((ent.flags & FL_MONSTER || ent.classname == "player") && ent.classname != self.classname)
{
// traceline for line of sight, check dot-product for FOV/facing, whatever else you want to do
// if true, set the target or whatever the ai does
}
ent = ent.chain;
}

You can change the 1024 to a higher number for a longer-range vision, but it may be slower. The MAIN thing to remember when using findradius like this is to call it as sparsely as possible, and try not to have 10 guys calling it at once. Perhaps make them check for opponents every 0.2 - 0.5 seconds or so. And try to spread out think times by setting the initial thinktime with an additional random factor. This may take some modifications to the AI code though, because I think currently AI is tied to animation (i.e. animation calls ai_stand() which calls FindTarget if there is no enemy). Perhaps you could make only every other stand frame, and every other walk frame, etc, call ai_stand/ai_walk.

Not sure if that helped at all...
Last edited by Sajt on Sat Jul 23, 2005 7:07 am, edited 1 time in total.
F. A. Špork, an enlightened nobleman and a great patron of art, had a stately Baroque spa complex built on the banks of the River Labe.
Spike
Posts: 2914
Joined: Fri Nov 05, 2004 3:12 am
Location: UK
Contact:

Post by Spike »

Monsters 'look' for enemies via the checkclient builtin. They only check it once, and it cycles through one, one client per frame.
Which means that coop gives slightly slower monsters.

That means you can't just test to see if it's a monster that you've got, because it'll only check client slots.
you'll have to use a findradius instead. Don't use classnames to identify things and it should run fast enough, but you might have to make them even slower at noticing, to help the cpu a bit. Seeing other monsters isn't as important as seeing the player.

It's somewhere in ai.qc, look for the checkclient. Enjoy.
Urre
Posts: 1109
Joined: Fri Nov 05, 2004 2:36 am
Location: Moon
Contact:

Post by Urre »

I'm going to attempt getting grunts to attack enforcers, and enforcers to attack grunts, on sight, based on regular progs106.

First, in ai.qc, just above the FindTarget function, ctrl+v this little bit of code:

Code: Select all

.entity last_check;
entity(string match) checktarget =
{
 self.last_check = find(self.last_check, classname, match);
 if (self.last_check)
  traceline(self.origin, self.last_check.origin, TRUE, self);
 if (trace_fraction == 1)
  return self.last_check;
};
Then go to line 371, which is inside the FindTarget function, a line which says:

Code: Select all

client = checkclient ();
Now, remove that line, and type this in, letter by letter (HAH!):

Code: Select all

if (self.classname == "monster_army")
 client = checktarget ("monster_enforcer");
else if (self.classname == "monster_enforcer")
 client = checktarget ("monster_army");
if (!client)
 client = checkclient ();
I should note that this will make them look for eachother as effectively as they look for multiple players in coop (slow), and it can get especially slow (performance-wise) if there are a lot of these specific monsters. If it gets too slow, you might want to add a findradius check of 1000 units (that's as far as they can see according to the range check code a bit further down in the FindTarget function) before the traceline in the checktarget function I told you to add. They will with this code also prefer their "hate" target before the player. If you want to change that, add the checkclient part above the monster checks.

I should also note that I just made this up, completely untested (haven't even tried compiling), and might include spelling errors and/or faulty use of builtins, but I hope you can figure those parts out.
I was once a Quake modder
Urre
Posts: 1109
Joined: Fri Nov 05, 2004 2:36 am
Location: Moon
Contact:

Post by Urre »

Haha, just noticed people posted while I was away typing...

/me reads other people's posts now
I was once a Quake modder
Urre
Posts: 1109
Joined: Fri Nov 05, 2004 2:36 am
Location: Moon
Contact:

Post by Urre »

Both Sajt and Spike have very valid points, the stuff I did was really just a quick hack, it's probably way too slow to actually use, but like I said, haven't tried it, so I don't really know.
I was once a Quake modder
ajay
Posts: 559
Joined: Fri Oct 29, 2004 6:44 am
Location: Swindon, UK

Post by ajay »

Thanks for all your replies. I'm still struggling a little. I've tried your suggestion Urre, but it didn't work, they stood looking blankly at each other ;)

I'm struggling a little with your suggestion Sajt, it makes sense and apears similar to the find_monster stuff at AI-cafe. Incidently I tried that and it works fine for just one protagonist, but as soon as you put mroe than one in, it just stops. (which is strange, because when I was playing with Raptors2, I can remember getting multiple raptors of one type to go hunting others... mmm, by using the Ai-cafe stuff - see at bottom)

I'm a little stuck Sajt by knowing also how to randomise the looking so that they're not all looking at once - yes I know, it's a wonder I get anything coded at all ;)

Thanks anyway everyone for your help.


ai-cafe code:
local entity beast;

if (self.classname == "monster_whatever")
{

if (self.enemy)
return FALSE;

beast = findradius(self.origin, 2000);

while(beast)
{
if ( (beast.flags & FL_MONSTER) && visible(beast) && beast != self && beast.health > 0)
self.enemy = beast;
beast = beast.chain;
}


if (!self.enemy)
return FALSE;

FoundTarget();
return TRUE;
}
Urre
Posts: 1109
Joined: Fri Nov 05, 2004 2:36 am
Location: Moon
Contact:

Post by Urre »

I got curious as to why it didn't work, so I went and coded it, and noticed I had forgot to check further down in FindTarget, so here's the solution (yes, it works with this, I tried):

Inside FindTarget, scroll down to the comment that says:

Code: Select all

//
// got one
//
Just below that, add this:

Code: Select all

if (self.classname == "monster_army")
if (self.enemy.classname == "monster_enforcer")
{
 FoundTarget ();
 return TRUE;
}
if (self.classname == "monster_enforcer")
if (self.enemy.classname == "monster_army")
{
 FoundTarget ();
 return TRUE;
}
Enjoy!

EDIT: I should note that this is a continuation of the code I posted previously, this alone won't work, you need to smack 'em both in.
I was once a Quake modder
Post Reply