Monsters searching player

Discuss Artificial Intelligence and Bot programming.
Post Reply
toneddu2000
Posts: 1395
Joined: Tue Feb 24, 2009 4:39 pm
Location: Italy

Monsters searching player

Post by toneddu2000 »

Hi guys. I'm trying to understand if there's a simple way to add in qc a way to make monsters search ALWAYS player (not only when they "see" him). I've look at this tutorial but here explains how to make monsters roaming along the map. I'm searching for something even (I hope) simpler: I'd only like that a fiend, an ogre or a dog could always know where's player and searching him.

Thanks in advance
gnounc
Posts: 428
Joined: Mon Apr 06, 2009 6:26 am

Post by gnounc »

I'd have to look, but I think you just change the "wakes up" code in that monsters qc file. Change the circumstances to something more broad and frequent.
frag.machine
Posts: 2126
Joined: Sat Nov 25, 2006 1:49 pm

Post by frag.machine »

Open ai.qc and find this:

Code: Select all

float() FindTarget =
{
    local entity    client;
    local float        r;

// 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;
    }
    
//
// got one
//
    self.enemy = client;
    if (self.enemy.classname != "player")
    {
        self.enemy = self.enemy.enemy;
        if (self.enemy.classname != "player")
        {
            self.enemy = world;
            return FALSE;
        }
    }
    
    FoundTarget ();

    return TRUE;
};

Basically, it's a matter of removing the undesired checking steps in the function above (doesn't matter if the monster can see the player or not ? Fine, just skip these steps then). The code is self explanatory enough, I believe you can grasp the idea by yourself. Have fun.
I know FrikaC made a cgi-bin version of the quakec interpreter once and wrote part of his website in QuakeC :) (LordHavoc)
Chip
Posts: 575
Joined: Wed Jan 21, 2009 9:12 am
Location: Dublin, Ireland
Contact:

Post by Chip »

So you mean if you remove RANGE_, the monsters will virtually see the entire map and know exactly where you are? That's a bit scary! :shock:

This should be the same as changing the RANGE_ values to something really large. Or to add additional checks for dogs (for example), such as change

Code: Select all

if (r == RANGE_FAR)
to

Code: Select all

if (r == 90000)
Although I'm pretty sure this holds a key, too:

Code: Select all

    client = checkclient ();
        if (!client)
            return FALSE;    // current check entity isn't in PVS 
Remove this and the PVS check will not be performed. This could be applied to some survival mod, where monsters come pouring in from every direction.
QuakeWiki
getButterfly - WordPress Support Services
Roo Holidays

Fear not the dark, but what the dark hides.
frag.machine
Posts: 2126
Joined: Sat Nov 25, 2006 1:49 pm

Post by frag.machine »

Not exactly. Let's read the code, step by step:

Code: Select all

    
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
    }
Here, we check if there's a hint by other entity about an enemy. Basically, somebody is pointing, during some time (sight_entity_time) a potential enemy (sight_entity). If that's the case, the monster will accept that, otherwise a call to checkclient() will return a client (IIRC, every call cycles thru all clients, so monsters in coop won't all get mad at the same player). Note that checkclient() already culls out non-visible clients, so if you want to always have a valid target here, you'll need to write your own code to do so. Also, keep in mind that the returned entity in both cases MAY NOT BE A PLAYER, but instead, a monster mad at a player. We'll see this at the end of the function.

Code: Select all

    if (client == self.enemy)
        return FALSE;
If the returned entity is the current enemy, then there's nothing to do, we can just return and keep chasing/attacking it.

Code: Select all

    if (client.flags & FL_NOTARGET)
        return FALSE;
    if (client.items & IT_INVISIBILITY)
        return FALSE;
These are the tests for the "notarget" cheat and the Ring of Shadows power-up. You can disable any of this just removing this code.

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 ( /* client.show_hostile < time || */ !infront (client))
            return FALSE;
    }
Here is the test to check if the monster is too far to notice the player. If the monster must be aware of the player regardless of their positions in the map, remove entirely this.

Code: Select all

   
//
// got one
//
    self.enemy = client;
    if (self.enemy.classname != "player")
    {
        self.enemy = self.enemy.enemy;
        if (self.enemy.classname != "player")
        {
            self.enemy = world;
            return FALSE;
        }
    }
This code tests if the entity that we are testing is a player or actually a monster. In the later case, we change the reference to the actual player entity.

Code: Select all

   
    FoundTarget ();

    return TRUE;
};
I know FrikaC made a cgi-bin version of the quakec interpreter once and wrote part of his website in QuakeC :) (LordHavoc)
toneddu2000
Posts: 1395
Joined: Tue Feb 24, 2009 4:39 pm
Location: Italy

Post by toneddu2000 »

Thank you guys for all your replies. I tried to remove all lines frag.machine told but, with no luck. Monsters seem stupids, even if I go closer to their noses, they stand still.
Note that checkclient() already culls out non-visible clients, so if you want to always have a valid target here, you'll need to write your own code to do so
That's exactly what I'd like to do! There was anyone who did an experiment like this to use the code? Because I've really no idea how to do it.
Thanks a lot

EDIT: I got worked. I removed some line and edited another one, like this:

Code: Select all


float() FindTarget =
{
	local entity	client;
	local float		r;

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

	
	
//
// got one
//
	self.enemy = client;
	if (self.enemy.classname == "player")
	
	
	FoundTarget ();

	return TRUE;
};


now monsters seem to be alerted all the time and searching for the player. Thanks frag.machine!
blubswillrule
Posts: 68
Joined: Mon Oct 04, 2010 9:08 pm
Location: Lincoln, California

Post by blubswillrule »

any way to make this work with waypoints? :P
A truly rewarding experience for an AI coder: watching your ai navigate the map... makes all the time invested in the code worth it :)
Post Reply