[ About ]
[ FAQ ]
[ Manifesto ]
[ Specs ]
[ Playing ]
[ Defeating ]
[ Fun & Games ]
[ Stills ]
[ Banning ]
[ Credits ]
[ Download ]
[ Utilities ]
[ Resources ]

StoogeBot Technical Specs

A Peek Under the Hood


We don't have much to add to the interpretation of Quake's network protocol. Check out the resources page for pointers to some great docs on the subject. The only important point related to networking is that the StoogeBot operates as a proxy. The StoogeBot software runs on a local machine and connects to a remote server, while a local client (responsible for driving the Stooge) connects to the local machine running the bot. By doing things this way, the StoogeBot may edit all server and client packets. This is different from a pure client-side bot; in fact, the StoogeBot can't run without a Quake client attached to it, since then there would be no driver!

Changing the Direction the Client Faces

The big "ah-hah!" for the StoogeBot was the complete decoupling of the player orientation from the direction of player motion. When the client transmits a packet to the the server, it specifies a heading, a forward velocity, and a strafing velocity. Of course, everyone knows this already; we've all seen players circle-strafe, run backwards, and pull all sorts of clever moves.

The StoogeBot proxy adds a few steps to the process. It can dictate which way the server thinks the client is facing by:

  • receiving a packet from the client
  • translating the forward and strafe velocities to x and y velocities
  • choosing a new client yaw (and pitch)
  • recalculating the client forward and strafe velocities given the new yaw
  • forwarding an edited packet to the server

By going through these extra steps, the client always has complete control of its orientation (pitch, yaw, and roll) and merely advises the StoogeBot, so when the StoogeBot lies to the server about the client's orientation, the client is none the wiser.

There are a couple of failure modes, of course. The above discussion assumes that the client pitch (up/down angle) is ignored by the server for motion control, and it is, except when the client is in the water. Loosely speaking, the client assumes he is navigating in a plane perpendicular to the vector pointing up through his head, and the StoogeBot assumes the client is navigating in a plane parallel to the floor (thus ignorning pitch). This can make navigating with the StoogeBot underwater a little frustrating. This should all also apply in the air, but in most cases you have little control over your in-flight trajectory, and you are airborn briefly enough that it isn't noticable. Although one might still expect problems on "Ziggurat Vertigo" we haven't noticed any yet. As a side note, we have thought of ways to improve motion control underwater, but haven't been pressed to implement them yet.

Selecting Targets

Target selection is performed whenever the StoogeBot does not have an active target. A target is any dynamic entity which both alive and classified as an enemy (player.mdl, dog.mdl, etc.). The liveness test can be performed by looking at the animation frame of an entity and checking for the absence of "death" or "deth" in the frame name. Given this criterion, the StoogeBot may select the living target closest to the client's line of sight.

When the StoogeBot's personality has been set to "Moe" mode, target selection works as follows: once a target has been selected, it remains selected until it either dies or leaves the visible set of the StoogeBot. The bot's visible set is a super-set of the true visible set. Some discussion of what this means can be found in the page on client-side bot abilities. What this all boils down to is that, in Moe mode, bot targeting has some hysteresis, so when a target runs behind a wall as you pursue it, the StoogeBot doesn't go looking for a new target immediately. Similarly, the StoogeBot won't randomly fire at different targets in a room, but will instead try persistently to kill a single target at a time. By doing this, the bot can be made to appear to be vaguely human.

Other StoogeBot personalities allow the StoogeBot to be more or less aggressive when seeking out targets. For example, in "Curly" mode, the bot constantly cycles between all targets it thinks it can hit, and in "Larry" mode, the bot selects the target with the with the greatest observed frag count.

Continual bot fire control can be a bit of a problem at times. There are always triggers and doors which need to be shot, and sometimes the computer simply chooses to attack the wrong enemy. To solve these problems we allow the Quake client to make shots on its own. If the client presses the fire key, the StoogeBot does no fire control and sends client packets on to the server unmodified. Moreover, the StoogeBot figures out which target was closest to the client's line of sight of when the shot was made, and makes that enemy the currently active target. When Bubba gets right up in your face while the StoogeBot is busy taking out another target, just let a shot loose and watch the StoogeBot fire control finish Bubba off.

Fire Control

Once the StoogeBot has an active target, it is very particular about when it will fire. It will only shoot at a target if there is a direct line of sight between it and its prey, and therefore, the StoogeBot doesn't blindly shoot walls. Also, if the target is running behind a series of pillars and is popping in and out of visibility rapidly, the resulting behavior of pulsing the weapon on and off very quickly can be quite entertaining.

In addition to performing a line-of-sight check, the StoogeBot accounts for the time of flight of its projectiles. Rather than shooting directly at a target, the StoogeBot will lead its target, so that shots head toward the spot the target is predicted to be when the shot finally arrives. The intersection point of the projectile and the target's predicted path is determined as follows:

dt  = 0; // time for shot to reach target
do {
    loc   = dt * target_velocity + target_loc;
    guess = calculate_time_of_flight( our_loc, loc );
    err   = abs( guess - dt );
    dt    = guess;
} while ( err > tolerance );

After executing this loop, the target will be at "loc" when our shot reaches it, and that is the location the bot fires at. A little bit of geometry determines the pitch and yaw needed for the StoogeBot to make the shot.

The StoogeBot faces the point it wants to shoot at, whether it intends to make a shot or not. This leads to the eerie behavior of an enemy that always faces you, no matter how you dodge and no matter what else that enemy might be occupied with, including things such as navigating a spiral staircase, jumping between platforms, etc. Quite unnerving.

Other Details

These technical specs take a fairly-high level approach to explaining the workings of the StoogeBot. Explanations of many of details have been swept under the carpet, including explanations of the bot's ability to take into account moving BSP entities (doors, platforms, etc.), the fast algorithm used for intersecting a ray or a line segment with a BSP tree (for determining visibility), and the algorithm used to account for client-to-server latency. If we find the time, we hope to explain the other technical details of the StoogeBot.