Forum

True client bots?

Discuss Artificial Intelligence and Bot programming.

Moderator: InsideQC Admins

True client bots?

Postby redrum » Wed Dec 12, 2007 3:58 am

Is there a way to implement bot functions (AI, waypointing, ...) on a true client?
Let's say I'm playing QW DM and I want to get a beer?
Can I switch to auto-pilot until I get back?
Welcome to the Overlook Hotel: The-Overlook-Hotel.game-server.cc
User avatar
redrum
 
Posts: 410
Joined: Wed Mar 28, 2007 11:35 pm
Location: Long Island, New York

Postby Entar » Wed Dec 12, 2007 5:04 am

I imagine this could be done with CSQC for clients that support it and on servers that don't have any protection against third party CSQC progs.dat's, but that kind of thing is generally considered cheating, to my knowledge.
User avatar
Entar
 
Posts: 439
Joined: Fri Nov 05, 2004 7:27 pm
Location: At my computer

Postby Urre » Wed Dec 12, 2007 7:07 am

You could do it serverside as well, which is easier to protect against cheating too. Just add a cvar check in say PlayerPreThink, or SV_PlayerPhysics, if on, run AI code on client. Not harder than making a regular bot. Which, ofcourse, is very hard :)
I was once a Quake modder
User avatar
Urre
 
Posts: 1109
Joined: Fri Nov 05, 2004 2:36 am
Location: Moon

Postby Electro » Wed Dec 12, 2007 11:41 pm

shockbot has this option already :)
Benjamin Darling
http://www.bendarling.net/

Reflex - In development competitive arena fps combining modern tech with the speed, precision and freedom of 90's shooters.
http://www.reflexfps.net/
Electro
 
Posts: 312
Joined: Wed Dec 29, 2004 11:25 pm
Location: Brisbane, Australia

Postby redrum » Wed Dec 12, 2007 11:44 pm

Has the Shockbot been released yet?
Welcome to the Overlook Hotel: The-Overlook-Hotel.game-server.cc
User avatar
redrum
 
Posts: 410
Joined: Wed Mar 28, 2007 11:35 pm
Location: Long Island, New York

Postby Electro » Thu Dec 13, 2007 1:09 am

nope, i'm currently without internet at home (i check this occassionally at work)

Redoing a heap of the core navigation stuff in shockbot at the moment. There's a bunch of things I want to make nicer, and how I was doing things was really limiting (pick a goal, then route to a goal).

Whereas with the new system, i should hopefully be able to get it working where it can know how to get to everything, then decide which one it wants to go for. The difference being that real travel distances can be taken into account rather than just a straight distance check to items. Also this will allow for a better way to do item respawn prediction. ;)
Benjamin Darling
http://www.bendarling.net/

Reflex - In development competitive arena fps combining modern tech with the speed, precision and freedom of 90's shooters.
http://www.reflexfps.net/
Electro
 
Posts: 312
Joined: Wed Dec 29, 2004 11:25 pm
Location: Brisbane, Australia

Postby Orion » Thu Dec 13, 2007 4:25 pm

Redrum, I just made a human bot.

BUT, it's not smart as a player, and it has perfect aim with instant weapons, so the shotguns and sniper rifle are undodgeable.

Impulse 150 will toggle between on and off.

It will respawn automatically when dead and will shoot the enemies.

Another thing is that he won't choose a weapon too, he'll fire with the current weapon until the ammo depletes.

I made it to be simple.


So let's do it.

Open world.qc and add this before StartFrame():

Code: Select all
float() FindEnemy =
{
   local entity head;
   
   head = findradius(self.origin, 2000);
   while (head)
   {
      if (head != self)
      if (!(head.flags & FL_ITEM))
      if (head.health > 0)
      if (head.takedamage == DAMAGE_AIM)
      if (visible(head))
         self.enemy = head;
      head = head.chain;
   }
   
   if (self.enemy != world)
      return TRUE;
   
   return FALSE;
};

.float goleft, goright;
void() Autopilot =
{
   if (self.health <= 0)
   {
      stuffcmd (self, "-forward\n");
      stuffcmd (self, "-moveleft\n");
      stuffcmd (self, "-moveright\n");
      return;
   }
   
   self.fixangle = TRUE;
   
   if (self.enemy == world)
   {
      if (FindEnemy())
         return;
      
      self.angles_x = 0;
      stuffcmd (self, "+forward\n");
      stuffcmd (self, "-moveleft\n");
      stuffcmd (self, "-moveright\n");
      
      if (random() < 0.02)
      {
         if (self.turndir == 45)
            self.turndir = -45;
         else
            self.turndir = 45;
      }
      
      traceline (self.origin, self.origin + v_forward*320, TRUE, self);
      if (trace_fraction < 0.2)
         self.angles_y = self.angles_y - self.turndir;
   }
   
   if (self.enemy != world)
   {
      if (self.enemy.health <= 0 || !visible(self.enemy))
      {
         self.enemy = world;
         return;
      }
      
      stuffcmd (self, "-forward\n");
      
      traceline (self.origin, self.origin + v_right*320, TRUE, self);
      if (trace_fraction < 0.2)
      {
         self.goleft = TRUE;
         self.goright = FALSE;
      }
      
      traceline (self.origin, self.origin - v_right*320, TRUE, self);
      if (trace_fraction < 0.2)
      {
         self.goleft = FALSE;
         self.goright = TRUE;
      }
      
      self.angles = vectoangles(self.enemy.origin - self.origin);
      self.angles_x = self.angles_x * -1;
      
      if (self.goleft)
      {
         stuffcmd (self, "+moveleft\n");
         stuffcmd (self, "-moveright\n");
      }
      if (self.goright)
      {
         stuffcmd (self, "-moveleft\n");
         stuffcmd (self, "+moveright\n");
      }
      
      if (infront(self.enemy) && self.attack_finished < time)
         W_Attack ();
   }
};


Ok, now add this at the very bottom of StartFrame(), before framecount line:

Code: Select all
local entity plr, oself;
plr = find(world, classname, "player");
while (plr != world)
{
   if (plr.autopilot)
   {
      oself = self;
      self = plr;
      Autopilot ();
      self = oself;
   }
   plr = find(plr, classname, "player");
}


Now go to the very top of world.qc, and add these 2 lines:

Code: Select all
float(entity targ) visible;
float(entity targ) infront;



Now save and close the file. Open defs.qc and add this to the very bottom:

Code: Select all
.float autopilot, turndir;


Save and close.
Now open client.qc, scroll down to PlayerPreThink() and find:

Code: Select all
if (self.deadflag >= DEAD_DEAD)
{
   PlayerDeathThink ();
   return;
}


Replace the whole statement with this:

Code: Select all
if (self.deadflag >= DEAD_DEAD)
{
   if (self.autopilot)
   {
      respawn();
      return;
   }
   PlayerDeathThink ();
   return;
}


Now scroll up to PutClientInServer(), and add this before DecodeLevelParms():

Code: Select all
self.enemy = world;
self.turndir = 45;


Save and close client.qc and open player.qc, scroll down to player_nail1().
Replace these 4 functions with these:

Code: Select all
void() player_nail1   =[$nailatt1, player_nail2  ]
{
   muzzleflash();

   if (self.autopilot)
   {
      if (self.enemy == world || intermission_running || self.impulse || self.ammo_nails < 1)
         {player_run ();return;}
   }
   else
   {
      if (!self.button0 || intermission_running || self.impulse || self.ammo_nails < 1)
         {player_run ();return;}
   }
   self.weaponframe = self.weaponframe + 1;
   if (self.weaponframe == 9)
      self.weaponframe = 1;
   SuperDamageSound();
   W_FireSpikes (4);
   self.attack_finished = time + .2;
        self.fired_nails = self.fired_nails + 1;
};
void() player_nail2   =[$nailatt2, player_nail1  ]
{
   muzzleflash();

   if (self.autopilot)
   {
      if (self.enemy == world || intermission_running || self.impulse || self.ammo_nails < 1)
         {player_run ();return;}
   }
   else
   {
      if (!self.button0 || intermission_running || self.impulse || self.ammo_nails < 1)
         {player_run ();return;}
   }
   self.weaponframe = self.weaponframe + 1;
   if (self.weaponframe == 9)
      self.weaponframe = 1;
   SuperDamageSound();
   W_FireSpikes (-4);
   self.attack_finished = time + .2;
        self.fired_nails = self.fired_nails + 1;
};

//============================================================================

void() player_light1   =[$light1, player_light2  ]
{
   muzzleflash();

   if (self.autopilot)
   {
      if (self.enemy == world || intermission_running || self.impulse || self.ammo_cells < 1)
         {player_run ();return;}
   }
   else
   {
      if (!self.button0 || intermission_running || self.impulse || self.ammo_cells < 1)
         {player_run ();return;}
   }
   self.weaponframe = self.weaponframe + 1;
   if (self.weaponframe == 5)
      self.weaponframe = 1;
   SuperDamageSound();
   W_FireLightning();
   self.attack_finished = time + 0.2;
};
void() player_light2   =[$light2, player_light1  ]
{
   muzzleflash();

   if (self.autopilot)
   {
      if (self.enemy == world || intermission_running || self.impulse || self.ammo_cells < 1)
         {player_run ();return;}
   }
   else
   {
      if (!self.button0 || intermission_running || self.impulse || self.ammo_cells < 1)
         {player_run ();return;}
   }
   self.weaponframe = self.weaponframe + 1;
   if (self.weaponframe == 5)
      self.weaponframe = 1;
   SuperDamageSound();
   W_FireLightning();
   self.attack_finished = time + 0.2;
};


Lastly, save and close player.qc and open weapons.qc, add this to ImpulseCommands():

Code: Select all
if (self.impulse == 150)
{
   if (!self.autopilot)
   {
      self.autopilot = 1;
      sprint (self, PRINT_HIGH, "Autopilot engaged.\n");
   }
   else
   {
      self.autopilot = 0;
      sprint (self, PRINT_HIGH, "Autopilot disengaged.\n");
      stuffcmd (self, "-forward\n");
      stuffcmd (self, "-moveleft\n");
      stuffcmd (self, "-moveright\n");
   }
}



There you go!
User avatar
Orion
 
Posts: 476
Joined: Fri Jan 12, 2007 6:32 pm
Location: Brazil

Postby redrum » Thu Dec 13, 2007 10:30 pm

Thanks man!
Won't be home till tomorrow to check it out.
Welcome to the Overlook Hotel: The-Overlook-Hotel.game-server.cc
User avatar
redrum
 
Posts: 410
Joined: Wed Mar 28, 2007 11:35 pm
Location: Long Island, New York

Postby redrum » Thu Dec 13, 2007 11:16 pm

Redrum (from work PC)

Orion, I made the necessary changes and it compiled :)
BUT, I got this error message when launching the server:

SV_Error: You must have the progs.dat from QuakeWorld installed

I do have that file. What's up?
Welcome to the Overlook Hotel: The-Overlook-Hotel.game-server.cc
User avatar
redrum
 
Posts: 410
Joined: Wed Mar 28, 2007 11:35 pm
Location: Long Island, New York

Postby Orion » Thu Dec 13, 2007 11:20 pm

What the hell?

Did you test at work?
Your code could be different. Try at home.

I tested here and nothing happened. Really weird.
User avatar
Orion
 
Posts: 476
Joined: Fri Jan 12, 2007 6:32 pm
Location: Brazil

Postby Spike » Fri Dec 14, 2007 1:55 am

redrum wrote:SV_Error: You must have the progs.dat from QuakeWorld installed

Means that the crc inside the progs.dat was not recognised for that platform. You get this error if you run an nq mod in a qw server.

The message should actually read 'qwprogs.dat'... The engine uses qwprogs.dat first if it can, and progs.dat after. But that's just a naming convention.

If it worked before, then you've probably wrongly modified defs.qc and added/removed/changed something before end_sys_fields. Restore the top two sections of that file to the previous version.
Spike
 
Posts: 2892
Joined: Fri Nov 05, 2004 3:12 am
Location: UK

Postby redrum » Fri Dec 14, 2007 3:16 am

Only modified defs.qc at the bottom though?
Welcome to the Overlook Hotel: The-Overlook-Hotel.game-server.cc
User avatar
redrum
 
Posts: 410
Joined: Wed Mar 28, 2007 11:35 pm
Location: Long Island, New York


Return to Artificial Intelligence

Who is online

Users browsing this forum: No registered users and 1 guest