| 37 | Your AINode function should fulfill these requirements: |
| 38 | * When the behavior is finished, the function should call AIExit_Quagents. The parameters you should pass to this are (1) the botstate, (2) the id of the command (this is cmd->id), (3) the value of your command macro (usually easiest to just do cmd->command), and (4) a string stating the return status ("done" for normal execution, or a meaningful error message (no spaces allowed) otherwise). After exiting, the function should return qfalse so that the next command on the command queue is handled. |
| 39 | * When the function has run everything it can this frame but the behavior is not finished (has not called AIExit_Quagents), the function must return qtrue, else the function will be called again. |
| 40 | * All current Quagent-specific AINodes are named AINode_G*, where * is the task name. The G is for quaGent, and is there to prevent collision with Quake's built-in AINodes. |
| 41 | |
| 42 | ==== BotQuagentAILatentNodeHelper() ==== |
| 43 | Some Quagent behaviors require updates after the movement portion of the frame has been executed. However, because the user may interrupt the action with another, the functions cannot rely on being called again on the next frame (because a different command may be executed), and there is never a guarantee that they will ever be called again (for example if the user commands a "n [[fa]]", all previous interrupted behaviors are never resumed). For some behaviors (usually ones that track progress of movement), this is unacceptable, so part of their functionality is defined in BotQuagentAILatentNodeHelper() in game/ai_main.c. This function is called every AIFrame, and usually updates a progress value for whatever behavior was active in the most recent frame and then clears its memory of which were active. |
| 44 | |
| 45 | If you examine the function you'll see that it updates the values pointed to by pointers in bot_state_t. These pointers point into the commandqueue, at elements of a quagent_command_t. Under our design, a quagent command must store all progress and state information into its quagent_command_t. This is because multiple commands of the same type are allowed to be on the queue, so per-type storage won't work, and because the queue has already allocated space for storing their parameters. |
| 46 | |
| 47 | Take [[mb]] as an example (AINode_GWalkBy is the function). A moveby command attempts to move the bot in a specified direction for a specific distance. However, it is difficult to tell how far exactly the bot will move on a given frame, so what the function does is update a pointer in bot_state, merely request a move, return, and then when BotQuagentAILatentNodeHelper is called on the next AI frame, it figures out how far the bot has moved and decrements the moveby distance by that amount. So on the first frame, the distance may be 50, but on the second, it may be 41, then 22, then 2, then -5. When it becomes negative the bot enters a "backpedaling" scenario, and (if on the ground) teleports back to where it should have moved to. |
| 48 | |
| 49 | ==== Thread Safety ==== |
| 50 | Thread safety is not a concern because Quake is single-threaded. If a user sends a command while an AINode is midway through execution, it will not be read until the next AI frame. You are guaranteed that the state of the command queue is constant through the execution of an AINode. For this reason, it is safe to make multiple calls to trap_Com_QuagentsWriteStr, as the batch rangefinder does. |
| 51 | |