]> git.xonotic.org Git - xonotic/xonotic-data.pk3dir.git/blob - qcsrc/server/bot/scripting.qc
Merge branch 'master' of http://de.git.xonotic.org/xonotic/xonotic-data.pk3dir
[xonotic/xonotic-data.pk3dir.git] / qcsrc / server / bot / scripting.qc
1 .float bot_cmdqueuebuf_allocated;
2 .float bot_cmdqueuebuf;
3 .float bot_cmdqueuebuf_start;
4 .float bot_cmdqueuebuf_end;
5
6 void bot_clearqueue(entity bot)
7 {
8         if(!bot.bot_cmdqueuebuf_allocated)
9                 error("clearqueue but no queue allocated");
10         buf_del(bot.bot_cmdqueuebuf);
11         bot.bot_cmdqueuebuf_allocated = FALSE;
12         dprint("bot ", bot.netname, " queue cleared\n");
13 }
14
15 void bot_queuecommand(entity bot, string cmdstring)
16 {
17         if(!bot.bot_cmdqueuebuf_allocated)
18         {
19                 bot.bot_cmdqueuebuf = buf_create();
20                 bot.bot_cmdqueuebuf_allocated = TRUE;
21                 bot.bot_cmdqueuebuf_start = 0;
22                 bot.bot_cmdqueuebuf_end = 0;
23         }
24
25         bufstr_set(bot.bot_cmdqueuebuf, bot.bot_cmdqueuebuf_end, cmdstring);
26
27         // if the command was a "sound" command, precache the sound NOW
28         // this prevents lagging!
29         {
30                 float sp;
31                 string parm;
32                 string cmdstr;
33
34                 sp = strstrofs(cmdstr, " ", 0);
35                 if(sp < 0)
36                 {
37                         parm = "";
38                 }
39                 else
40                 {
41                         parm = substring(cmdstr, sp + 1, -1);
42                         cmdstr = substring(cmdstr, 0, sp);
43                         if(cmdstr == "sound")
44                                 precache_sound(cmdstr);
45                 }
46         }
47
48         bot.bot_cmdqueuebuf_end += 1;
49 }
50
51 void bot_dequeuecommand(entity bot, float idx)
52 {
53         if(!bot.bot_cmdqueuebuf_allocated)
54                 error("dequeuecommand but no queue allocated");
55         if(idx < bot.bot_cmdqueuebuf_start)
56                 error("dequeueing a command in the past");
57         if(idx >= bot.bot_cmdqueuebuf_end)
58                 error("dequeueing a command in the future");
59         bufstr_set(bot.bot_cmdqueuebuf, idx, "");
60         if(idx == bot.bot_cmdqueuebuf_start)
61                 bot.bot_cmdqueuebuf_start += 1;
62         if(bot.bot_cmdqueuebuf_start >= bot.bot_cmdqueuebuf_end)
63                 bot_clearqueue(bot);
64 }
65
66 string bot_readcommand(entity bot, float idx)
67 {
68         if(!bot.bot_cmdqueuebuf_allocated)
69                 error("readcommand but no queue allocated");
70         if(idx < bot.bot_cmdqueuebuf_start)
71                 error("reading a command in the past");
72         if(idx >= bot.bot_cmdqueuebuf_end)
73                 error("reading a command in the future");
74         return bufstr_get(bot.bot_cmdqueuebuf, idx);
75 }
76
77 float bot_havecommand(entity bot, float idx)
78 {
79         if(!bot.bot_cmdqueuebuf_allocated)
80                 return 0;
81         if(idx < bot.bot_cmdqueuebuf_start)
82                 return 0;
83         if(idx >= bot.bot_cmdqueuebuf_end)
84                 return 0;
85         return 1;
86 }
87
88 #define MAX_BOT_PLACES 4
89 .float bot_places_count;
90 .entity bot_places[MAX_BOT_PLACES];
91 .string bot_placenames[MAX_BOT_PLACES];
92 entity bot_getplace(string placename)
93 {
94         entity e;
95         if(substring(placename, 0, 1) == "@")
96         {
97                 float i, p;
98                 placename = substring(placename, 1, -1);
99                 string s, s2;
100                 for(i = 0; i < self.bot_places_count; ++i)
101                         if(self.(bot_placenames[i]) == placename)
102                                 return self.(bot_places[i]);
103                 // now: i == self.bot_places_count
104                 s = s2 = cvar_string(placename);
105                 p = strstrofs(s2, " ", 0);
106                 if(p >= 0)
107                 {
108                         s = substring(s2, 0, p);
109                         //print("places: ", placename, " -> ", cvar_string(placename), "\n");
110                         cvar_set(placename, strcat(substring(s2, p+1, -1), " ", s));
111                         //print("places: ", placename, " := ", cvar_string(placename), "\n");
112                 }
113                 e = find(world, targetname, s);
114                 if(!e)
115                         print("invalid place ", s, "\n");
116                 if(i < MAX_BOT_PLACES)
117                 {
118                         self.(bot_placenames[i]) = strzone(placename);
119                         self.(bot_places[i]) = e;
120                         self.bot_places_count += 1;
121                 }
122                 return e;
123         }
124         else
125         {
126                 e = find(world, targetname, placename);
127                 if(!e)
128                         print("invalid place ", placename, "\n");
129                 return e;
130         }
131 }
132
133
134 // NOTE: New commands should be added here. Do not forget to update BOT_CMD_COUNTER
135 #define BOT_CMD_NULL                    0
136 #define BOT_CMD_PAUSE                   1
137 #define BOT_CMD_CONTINUE                2
138 #define BOT_CMD_WAIT                    3
139 #define BOT_CMD_TURN                    4
140 #define BOT_CMD_MOVETO                  5
141 #define BOT_CMD_RESETGOAL               6       // Not implemented yet
142 #define BOT_CMD_CC                      7
143 #define BOT_CMD_IF                      8
144 #define BOT_CMD_ELSE                    9
145 #define BOT_CMD_FI                      10
146 #define BOT_CMD_RESETAIM                11
147 #define BOT_CMD_AIM                     12
148 #define BOT_CMD_PRESSKEY                13
149 #define BOT_CMD_RELEASEKEY              14
150 #define BOT_CMD_SELECTWEAPON            15
151 #define BOT_CMD_IMPULSE                 16
152 #define BOT_CMD_WAIT_UNTIL              17
153 #define BOT_CMD_MOVETOTARGET    18
154 #define BOT_CMD_AIMTARGET       19
155 #define BOT_CMD_BARRIER         20
156 #define BOT_CMD_CONSOLE                 21
157 #define BOT_CMD_SOUND                   22
158 #define BOT_CMD_DEBUG_ASSERT_CANFIRE 23
159 #define BOT_CMD_WHILE                   24      // TODO: Not implemented yet
160 #define BOT_CMD_WEND                    25      // TODO: Not implemented yet
161 #define BOT_CMD_CHASE                   26      // TODO: Not implemented yet
162
163 #define BOT_CMD_COUNTER                 24      // Update this value if you add/remove a command
164
165 // NOTE: Following commands should be implemented on the bot ai
166 //               If a new command should be handled by the target ai(s) please declare it here
167 .float(vector) cmd_moveto;
168 .float() cmd_resetgoal;
169
170 //
171 #define BOT_CMD_PARAMETER_NONE          0
172 #define BOT_CMD_PARAMETER_FLOAT         1
173 #define BOT_CMD_PARAMETER_STRING        2
174 #define BOT_CMD_PARAMETER_VECTOR        3
175
176 float bot_cmds_initialized;
177 float bot_cmd_parm_type[BOT_CMD_COUNTER];
178 string bot_cmd_string[BOT_CMD_COUNTER];
179
180 // Bots command queue
181 entity bot_cmd; // global current command
182 .entity bot_cmd_current; // current command of this bot
183
184 .float is_bot_cmd;                      // Tells if the entity is a bot command
185 .float bot_cmd_index;                   // Position of the command in the queue
186 .float bot_cmd_type;                    // If of command (see the BOT_CMD_* defines)
187 .float bot_cmd_parm_float;              // Field to store a float parameter
188 .string bot_cmd_parm_string;            // Field to store a string parameter
189 .vector bot_cmd_parm_vector;            // Field to store a vector parameter
190
191 float bot_barriertime;
192 .float bot_barrier;
193
194 .float bot_cmd_execution_index;         // Position in the queue of the command to be executed
195
196 // Initialize global commands list
197 // NOTE: New commands should be initialized here
198 void bot_commands_init()
199 {
200         bot_cmd_string[BOT_CMD_NULL] = "";
201         bot_cmd_parm_type[BOT_CMD_NULL] = BOT_CMD_PARAMETER_NONE;
202
203         bot_cmd_string[BOT_CMD_PAUSE] = "pause";
204         bot_cmd_parm_type[BOT_CMD_PAUSE] = BOT_CMD_PARAMETER_NONE;
205
206         bot_cmd_string[BOT_CMD_CONTINUE] = "continue";
207         bot_cmd_parm_type[BOT_CMD_CONTINUE] = BOT_CMD_PARAMETER_NONE;
208
209         bot_cmd_string[BOT_CMD_WAIT] = "wait";
210         bot_cmd_parm_type[BOT_CMD_WAIT] = BOT_CMD_PARAMETER_FLOAT;
211
212         bot_cmd_string[BOT_CMD_TURN] = "turn";
213         bot_cmd_parm_type[BOT_CMD_TURN] = BOT_CMD_PARAMETER_FLOAT;
214
215         bot_cmd_string[BOT_CMD_MOVETO] = "moveto";
216         bot_cmd_parm_type[BOT_CMD_MOVETO] = BOT_CMD_PARAMETER_VECTOR;
217
218         bot_cmd_string[BOT_CMD_MOVETOTARGET] = "movetotarget";
219         bot_cmd_parm_type[BOT_CMD_MOVETOTARGET] = BOT_CMD_PARAMETER_STRING;
220
221         bot_cmd_string[BOT_CMD_RESETGOAL] = "resetgoal";
222         bot_cmd_parm_type[BOT_CMD_RESETGOAL] = BOT_CMD_PARAMETER_NONE;
223
224         bot_cmd_string[BOT_CMD_CC] = "cc";
225         bot_cmd_parm_type[BOT_CMD_CC] = BOT_CMD_PARAMETER_STRING;
226
227         bot_cmd_string[BOT_CMD_IF] = "if";
228         bot_cmd_parm_type[BOT_CMD_IF] = BOT_CMD_PARAMETER_STRING;
229
230         bot_cmd_string[BOT_CMD_ELSE] = "else";
231         bot_cmd_parm_type[BOT_CMD_ELSE] = BOT_CMD_PARAMETER_NONE;
232
233         bot_cmd_string[BOT_CMD_FI] = "fi";
234         bot_cmd_parm_type[BOT_CMD_FI] = BOT_CMD_PARAMETER_NONE;
235
236         bot_cmd_string[BOT_CMD_RESETAIM] = "resetaim";
237         bot_cmd_parm_type[BOT_CMD_RESETAIM] = BOT_CMD_PARAMETER_NONE;
238
239         bot_cmd_string[BOT_CMD_AIM] = "aim";
240         bot_cmd_parm_type[BOT_CMD_AIM] = BOT_CMD_PARAMETER_STRING;
241
242         bot_cmd_string[BOT_CMD_AIMTARGET] = "aimtarget";
243         bot_cmd_parm_type[BOT_CMD_AIMTARGET] = BOT_CMD_PARAMETER_STRING;
244
245         bot_cmd_string[BOT_CMD_PRESSKEY] = "presskey";
246         bot_cmd_parm_type[BOT_CMD_PRESSKEY] = BOT_CMD_PARAMETER_STRING;
247
248         bot_cmd_string[BOT_CMD_RELEASEKEY] = "releasekey";
249         bot_cmd_parm_type[BOT_CMD_RELEASEKEY] = BOT_CMD_PARAMETER_STRING;
250
251         bot_cmd_string[BOT_CMD_SELECTWEAPON] = "selectweapon";
252         bot_cmd_parm_type[BOT_CMD_SELECTWEAPON] = BOT_CMD_PARAMETER_FLOAT;
253
254         bot_cmd_string[BOT_CMD_IMPULSE] = "impulse";
255         bot_cmd_parm_type[BOT_CMD_IMPULSE] = BOT_CMD_PARAMETER_FLOAT;
256
257         bot_cmd_string[BOT_CMD_WAIT_UNTIL] = "wait_until";
258         bot_cmd_parm_type[BOT_CMD_WAIT_UNTIL] = BOT_CMD_PARAMETER_FLOAT;
259
260         bot_cmd_string[BOT_CMD_BARRIER] = "barrier";
261         bot_cmd_parm_type[BOT_CMD_BARRIER] = BOT_CMD_PARAMETER_NONE;
262
263         bot_cmd_string[BOT_CMD_CONSOLE] = "console";
264         bot_cmd_parm_type[BOT_CMD_CONSOLE] = BOT_CMD_PARAMETER_STRING;
265
266         bot_cmd_string[BOT_CMD_SOUND] = "sound";
267         bot_cmd_parm_type[BOT_CMD_SOUND] = BOT_CMD_PARAMETER_STRING;
268
269         bot_cmd_string[BOT_CMD_DEBUG_ASSERT_CANFIRE] = "debug_assert_canfire";
270         bot_cmd_parm_type[BOT_CMD_DEBUG_ASSERT_CANFIRE] = BOT_CMD_PARAMETER_FLOAT;
271
272         bot_cmds_initialized = TRUE;
273 }
274
275 // Returns first bot with matching name
276 entity find_bot_by_name(string name)
277 {
278         entity bot;
279
280         bot = findchainflags(flags, FL_CLIENT);
281         while (bot)
282         {
283                 if(clienttype(bot) == CLIENTTYPE_BOT)
284                 if(bot.netname==name)
285                         return bot;
286
287                 bot = bot.chain;
288         }
289
290         return world;
291 }
292
293 // Returns a bot by number on list
294 entity find_bot_by_number(float number)
295 {
296         entity bot;
297         float c = 0;
298
299         if(!number)
300                 return world;
301
302         bot = findchainflags(flags, FL_CLIENT);
303         while (bot)
304         {
305                 if(clienttype(bot) == CLIENTTYPE_BOT)
306                 {
307                         if(++c==number)
308                                 return bot;
309                 }
310                 bot = bot.chain;
311         }
312
313         return world;
314 }
315
316 float bot_decodecommand(string cmdstring)
317 {
318         float cmd_parm_type, i;
319         float sp;
320         string parm;
321
322         sp = strstrofs(cmdstring, " ", 0);
323         if(sp < 0)
324         {
325                 parm = "";
326         }
327         else
328         {
329                 parm = substring(cmdstring, sp + 1, -1);
330                 cmdstring = substring(cmdstring, 0, sp);
331         }
332
333         if(!bot_cmds_initialized)
334                 bot_commands_init();
335
336         for(i=1;i<BOT_CMD_COUNTER;++i)
337         {
338                 if(bot_cmd_string[i]!=cmdstring)
339                         continue;
340
341                 cmd_parm_type = bot_cmd_parm_type[i];
342
343                 if(cmd_parm_type!=BOT_CMD_PARAMETER_NONE&&parm=="")
344                 {
345                         print("ERROR: A parameter is required for this command\n");
346                         return 0;
347                 }
348
349                 // Load command into queue
350                 bot_cmd.bot_cmd_type = i;
351
352                 // Attach parameter
353                 switch(cmd_parm_type)
354                 {
355                         case BOT_CMD_PARAMETER_FLOAT:
356                                 bot_cmd.bot_cmd_parm_float = stof(parm);
357                                 break;
358                         case BOT_CMD_PARAMETER_STRING:
359                                 if(bot_cmd.bot_cmd_parm_string)
360                                         strunzone(bot_cmd.bot_cmd_parm_string);
361                                 bot_cmd.bot_cmd_parm_string = strzone(parm);
362                                 break;
363                         case BOT_CMD_PARAMETER_VECTOR:
364                                 bot_cmd.bot_cmd_parm_vector = stov(parm);
365                                 break;
366                         default:
367                                 break;
368                 }
369                 return 1;
370         }
371         print("ERROR: No such command '", cmdstring, "'\n");
372         return 0;
373 }
374
375 void bot_cmdhelp(string scmd)
376 {
377         float i, ntype;
378         string stype;
379
380         if(!bot_cmds_initialized)
381                 bot_commands_init();
382
383         for(i=1;i<BOT_CMD_COUNTER;++i)
384         {
385                 if(bot_cmd_string[i]!=scmd)
386                         continue;
387
388                 ntype = bot_cmd_parm_type[i];
389
390                 switch(ntype)
391                 {
392                         case BOT_CMD_PARAMETER_FLOAT:
393                                 stype = "float number";
394                                 break;
395                         case BOT_CMD_PARAMETER_STRING:
396                                 stype = "string";
397                                 break;
398                         case BOT_CMD_PARAMETER_VECTOR:
399                                 stype = "vector";
400                                 break;
401                         default:
402                                 stype = "none";
403                                 break;
404                 }
405
406                 print(strcat("Command: ",bot_cmd_string[i],"\nParameter: <",stype,"> \n"));
407
408                 print("Description: ");
409                 switch(i)
410                 {
411                         case BOT_CMD_PAUSE:
412                                 print("Stops the bot completely. Any command other than 'continue' will be ignored.");
413                                 break;
414                         case BOT_CMD_CONTINUE:
415                                 print("Disable paused status");
416                                 break;
417                         case BOT_CMD_WAIT:
418                                 print("Pause command parsing and bot ai for N seconds. Pressed key will remain pressed");
419                                 break;
420                         case BOT_CMD_WAIT_UNTIL:
421                                 print("Pause command parsing and bot ai until time is N from the last barrier. Pressed key will remain pressed");
422                                 break;
423                         case BOT_CMD_BARRIER:
424                                 print("Waits till all bots that have a command queue reach this command. Pressed key will remain pressed");
425                                 break;
426                         case BOT_CMD_TURN:
427                                 print("Look to the right or left N degrees. For turning to the left use positive numbers.");
428                                 break;
429                         case BOT_CMD_MOVETO:
430                                 print("Walk to an specific coordinate on the map. Usage: moveto \"x y z\"");
431                                 break;
432                         case BOT_CMD_MOVETOTARGET:
433                                 print("Walk to the specific target on the map");
434                                 break;
435                         case BOT_CMD_RESETGOAL:
436                                 print("Resets the goal stack");
437                                 break;
438                         case BOT_CMD_CC:
439                                 print("Execute client command. Examples: cc \"say something\"; cc god; cc \"name newnickname\"; cc kill;");
440                                 break;
441                         case BOT_CMD_IF:
442                                 print("Perform simple conditional execution.\n");
443                                 print("Syntax: \n");
444                                 print("        sv_cmd .. if \"condition\"\n");
445                                 print("        sv_cmd ..        <instruction if true>\n");
446                                 print("        sv_cmd ..        <instruction if true>\n");
447                                 print("        sv_cmd .. else\n");
448                                 print("        sv_cmd ..        <instruction if false>\n");
449                                 print("        sv_cmd ..        <instruction if false>\n");
450                                 print("        sv_cmd .. fi\n");
451                                 print("Conditions: a=b, a>b, a<b, a\t\t(spaces not allowed)\n");
452                                 print("            Values in conditions can be numbers, cvars in the form cvar.cvar_string or special fields\n");
453                                 print("Fields: health, speed, flagcarrier\n");
454                                 print("Examples: if health>50; if health>cvar.g_balance_laser_primary_damage; if flagcarrier;");
455                                 break;
456                         case BOT_CMD_RESETAIM:
457                                 print("Points the aim to the coordinates x,y 0,0");
458                                 break;
459                         case BOT_CMD_AIM:
460                                 print("Move the aim x/y (horizontal/vertical) degrees relatives to the bot\n");
461                                 print("There is a 3rd optional parameter telling in how many seconds the aim has to reach the new position\n");
462                                 print("Examples: aim \"90 0\"   // Turn 90 degrees inmediately (positive numbers move to the left/up)\n");
463                                 print("          aim \"0 90 2\" // Will gradually look to the sky in the next two seconds");
464                                 break;
465                         case BOT_CMD_AIMTARGET:
466                                 print("Points the aim to given target");
467                                 break;
468                         case BOT_CMD_PRESSKEY:
469                                 print("Press one of the following keys: forward, backward, left, right, jump, crouch, attack1, attack2, use\n");
470                                 print("Multiple keys can be pressed at time (with many presskey calls) and it will remain pressed until the command \"releasekey\" is called");
471                                 print("Note: The script will not return the control to the bot ai until all keys are released");
472                                 break;
473                         case BOT_CMD_RELEASEKEY:
474                                 print("Release previoulsy used keys. Use the parameter \"all\" to release all keys");
475                                 break;
476                         case BOT_CMD_SOUND:
477                                 print("play sound file at bot location");
478                                 break;
479                         case BOT_CMD_DEBUG_ASSERT_CANFIRE:
480                                 print("verify the state of the weapon entity");
481                                 break;
482                         default:
483                                 print("This command has no description yet.");
484                                 break;
485                 }
486                 print("\n");
487         }
488 }
489
490 void bot_list_commands()
491 {
492         float i;
493         string ptype;
494
495         if(!bot_cmds_initialized)
496                 bot_commands_init();
497
498         print("List of all available commands:\n");
499         print("  Command - Parameter Type\n");
500
501         for(i=1;i<BOT_CMD_COUNTER;++i)
502         {
503                 switch(bot_cmd_parm_type[i])
504                 {
505                         case BOT_CMD_PARAMETER_FLOAT:
506                                 ptype = "float number";
507                                 break;
508                         case BOT_CMD_PARAMETER_STRING:
509                                 ptype = "string";
510                                 break;
511                         case BOT_CMD_PARAMETER_VECTOR:
512                                 ptype = "vector";
513                                 break;
514                         default:
515                                 ptype = "none";
516                                 break;
517                 }
518                 print(strcat("  ",bot_cmd_string[i]," - <",ptype,"> \n"));
519         }
520 }
521
522 // Commands code
523 .float bot_exec_status;
524
525 #define BOT_EXEC_STATUS_IDLE    0
526 #define BOT_EXEC_STATUS_PAUSED  1
527 #define BOT_EXEC_STATUS_WAITING 2
528
529 #define CMD_STATUS_EXECUTING    0
530 #define CMD_STATUS_FINISHED     1
531 #define CMD_STATUS_ERROR        2
532
533 void SV_ParseClientCommand(string s);
534 float bot_cmd_cc()
535 {
536         SV_ParseClientCommand(bot_cmd.bot_cmd_parm_string);
537         return CMD_STATUS_FINISHED;
538 }
539
540 float bot_cmd_impulse()
541 {
542         self.impulse = bot_cmd.bot_cmd_parm_float;
543         return CMD_STATUS_FINISHED;
544 }
545
546 float bot_cmd_continue()
547 {
548         self.bot_exec_status &~= BOT_EXEC_STATUS_PAUSED;
549         return CMD_STATUS_FINISHED;
550 }
551
552 .float bot_cmd_wait_time;
553 float bot_cmd_wait()
554 {
555         if(self.bot_exec_status & BOT_EXEC_STATUS_WAITING)
556         {
557                 if(time>=self.bot_cmd_wait_time)
558                 {
559                         self.bot_exec_status &~= BOT_EXEC_STATUS_WAITING;
560                         return CMD_STATUS_FINISHED;
561                 }
562                 else
563                         return CMD_STATUS_EXECUTING;
564         }
565
566         self.bot_cmd_wait_time = time + bot_cmd.bot_cmd_parm_float;
567         self.bot_exec_status |= BOT_EXEC_STATUS_WAITING;
568         return CMD_STATUS_EXECUTING;
569 }
570
571 float bot_cmd_wait_until()
572 {
573         if(time < bot_cmd.bot_cmd_parm_float + bot_barriertime)
574         {
575                 self.bot_exec_status |= BOT_EXEC_STATUS_WAITING;
576                 return CMD_STATUS_EXECUTING;
577         }
578         self.bot_exec_status &~= BOT_EXEC_STATUS_WAITING;
579         return CMD_STATUS_FINISHED;
580 }
581
582 float bot_cmd_barrier()
583 {
584         entity cl;
585
586         // 0 = no barrier, 1 = waiting, 2 = waiting finished
587
588         if(self.bot_barrier == 0) // initialization
589         {
590                 self.bot_barrier = 1;
591
592                 //self.colormod = '4 4 0';
593         }
594
595         if(self.bot_barrier == 1) // find other bots
596         {
597                 FOR_EACH_CLIENT(cl) if(cl.isbot)
598                 {
599                         if(cl.bot_cmdqueuebuf_allocated)
600                                 if(cl.bot_barrier != 1)
601                                         return CMD_STATUS_EXECUTING; // not all are at the barrier yet
602                 }
603
604                 // all bots hit the barrier!
605                 FOR_EACH_CLIENT(cl) if(cl.isbot)
606                 {
607                         cl.bot_barrier = 2; // acknowledge barrier
608                 }
609
610                 bot_barriertime = time;
611         }
612
613         // if we get here, the barrier is finished
614         // so end it...
615         self.bot_barrier = 0;
616         //self.colormod = '0 0 0';
617
618         return CMD_STATUS_FINISHED;
619 }
620
621 float bot_cmd_turn()
622 {
623         self.v_angle_y = self.v_angle_y + bot_cmd.bot_cmd_parm_float;
624         self.v_angle_y = self.v_angle_y - floor(self.v_angle_y / 360) * 360;
625         return CMD_STATUS_FINISHED;
626 }
627
628 float bot_cmd_select_weapon()
629 {
630         float id;
631
632         id = bot_cmd.bot_cmd_parm_float;
633
634         if(id < WEP_FIRST || id > WEP_LAST)
635                 return CMD_STATUS_ERROR;
636
637         if(client_hasweapon(self, id, TRUE, FALSE))
638                 self.switchweapon = id;
639         else
640                 return CMD_STATUS_ERROR;
641
642         return CMD_STATUS_FINISHED;
643 }
644
645 .float bot_cmd_condition_status;
646
647 #define CMD_CONDITION_NONE              0
648 #define CMD_CONDITION_TRUE              1
649 #define CMD_CONDITION_FALSE             2
650 #define CMD_CONDITION_TRUE_BLOCK        4
651 #define CMD_CONDITION_FALSE_BLOCK       8
652
653 float bot_cmd_eval(string expr)
654 {
655         // Search for numbers
656         if(strstrofs("0123456789", substring(expr, 0, 1), 0) >= 0)
657         {
658                 return stof(expr);
659         }
660
661         // Search for cvars
662         if(substring(expr, 0, 5)=="cvar.")
663         {
664                 return cvar(substring(expr, 5, strlen(expr)));
665         }
666
667         // Search for fields
668         switch(expr)
669         {
670                 case "health":
671                         return self.health;
672                 case "speed":
673                         return vlen(self.velocity);
674                 case "flagcarrier":
675                         return ((self.flagcarried!=world));
676         }
677
678         print(strcat("ERROR: Unable to convert the expression '",expr,"' into a numeric value\n"));
679         return 0;
680 }
681
682 float bot_cmd_if()
683 {
684         string expr, val_a, val_b;
685         float cmpofs;
686
687         if(self.bot_cmd_condition_status != CMD_CONDITION_NONE)
688         {
689                 // Only one "if" block is allowed at time
690                 print("ERROR: Only one conditional block can be processed at time");
691                 bot_clearqueue(self);
692                 return CMD_STATUS_ERROR;
693         }
694
695         self.bot_cmd_condition_status |= CMD_CONDITION_TRUE_BLOCK;
696
697         // search for operators
698         expr = bot_cmd.bot_cmd_parm_string;
699
700         cmpofs = strstrofs(expr,"=",0);
701
702         if(cmpofs>0)
703         {
704                 val_a = substring(expr,0,cmpofs);
705                 val_b = substring(expr,cmpofs+1,strlen(expr));
706
707                 if(bot_cmd_eval(val_a)==bot_cmd_eval(val_b))
708                         self.bot_cmd_condition_status |= CMD_CONDITION_TRUE;
709                 else
710                         self.bot_cmd_condition_status |= CMD_CONDITION_FALSE;
711
712                 return CMD_STATUS_FINISHED;
713         }
714
715         cmpofs = strstrofs(expr,">",0);
716
717         if(cmpofs>0)
718         {
719                 val_a = substring(expr,0,cmpofs);
720                 val_b = substring(expr,cmpofs+1,strlen(expr));
721
722                 if(bot_cmd_eval(val_a)>bot_cmd_eval(val_b))
723                         self.bot_cmd_condition_status |= CMD_CONDITION_TRUE;
724                 else
725                         self.bot_cmd_condition_status |= CMD_CONDITION_FALSE;
726
727                 return CMD_STATUS_FINISHED;
728         }
729
730         cmpofs = strstrofs(expr,"<",0);
731
732         if(cmpofs>0)
733         {
734                 val_a = substring(expr,0,cmpofs);
735                 val_b = substring(expr,cmpofs+1,strlen(expr));
736
737                 if(bot_cmd_eval(val_a)<bot_cmd_eval(val_b))
738                         self.bot_cmd_condition_status |= CMD_CONDITION_TRUE;
739                 else
740                         self.bot_cmd_condition_status |= CMD_CONDITION_FALSE;
741
742                 return CMD_STATUS_FINISHED;
743         }
744
745         if(bot_cmd_eval(expr))
746                 self.bot_cmd_condition_status |= CMD_CONDITION_TRUE;
747         else
748                 self.bot_cmd_condition_status |= CMD_CONDITION_FALSE;
749
750         return CMD_STATUS_FINISHED;
751 }
752
753 float bot_cmd_else()
754 {
755         self.bot_cmd_condition_status &~= CMD_CONDITION_TRUE_BLOCK;
756         self.bot_cmd_condition_status |= CMD_CONDITION_FALSE_BLOCK;
757         return CMD_STATUS_FINISHED;
758 }
759
760 float bot_cmd_fi()
761 {
762         self.bot_cmd_condition_status = CMD_CONDITION_NONE;
763         return CMD_STATUS_FINISHED;
764 }
765
766 float bot_cmd_resetaim()
767 {
768         self.v_angle = '0 0 0';
769         return CMD_STATUS_FINISHED;
770 }
771
772 .float bot_cmd_aim_begintime;
773 .float bot_cmd_aim_endtime;
774 .vector bot_cmd_aim_begin;
775 .vector bot_cmd_aim_end;
776
777 float bot_cmd_aim()
778 {
779         // Current direction
780         if(self.bot_cmd_aim_endtime)
781         {
782                 float progress;
783
784                 progress = min(1 - (self.bot_cmd_aim_endtime - time) / (self.bot_cmd_aim_endtime - self.bot_cmd_aim_begintime),1);
785                 self.v_angle = self.bot_cmd_aim_begin + ((self.bot_cmd_aim_end - self.bot_cmd_aim_begin) * progress);
786
787                 if(time>=self.bot_cmd_aim_endtime)
788                 {
789                         self.bot_cmd_aim_endtime = 0;
790                         return CMD_STATUS_FINISHED;
791                 }
792                 else
793                         return CMD_STATUS_EXECUTING;
794         }
795
796         // New aiming direction
797         string parms;
798         float tokens, step;
799
800         parms = bot_cmd.bot_cmd_parm_string;
801
802         tokens = tokenizebyseparator(parms, " ");
803
804         if(tokens<2||tokens>3)
805                 return CMD_STATUS_ERROR;
806
807         step = (tokens == 3) ? stof(argv(2)) : 0;
808
809         if(step == 0)
810         {
811                 self.v_angle_x -= stof(argv(1));
812                 self.v_angle_y += stof(argv(0));
813                 return CMD_STATUS_FINISHED;
814         }
815
816         self.bot_cmd_aim_begin = self.v_angle;
817
818         self.bot_cmd_aim_end_x = self.v_angle_x - stof(argv(1));
819         self.bot_cmd_aim_end_y = self.v_angle_y + stof(argv(0));
820         self.bot_cmd_aim_end_z = 0;
821
822         self.bot_cmd_aim_begintime = time;
823         self.bot_cmd_aim_endtime = time + step;
824
825         return CMD_STATUS_EXECUTING;
826 }
827
828 float bot_cmd_aimtarget()
829 {
830         if(self.bot_cmd_aim_endtime)
831         {
832                 return bot_cmd_aim();
833         }
834
835         entity e;
836         string parms;
837         vector v;
838         float tokens, step;
839
840         parms = bot_cmd.bot_cmd_parm_string;
841
842         tokens = tokenizebyseparator(parms, " ");
843
844         e = bot_getplace(argv(0));
845         if(!e)
846                 return CMD_STATUS_ERROR;
847
848         v = e.origin + (e.mins + e.maxs) * 0.5;
849
850         if(tokens==1)
851         {
852                 self.v_angle = vectoangles(v - (self.origin + self.view_ofs));
853                 self.v_angle_x = -self.v_angle_x;
854                 return CMD_STATUS_FINISHED;
855         }
856
857         if(tokens<1||tokens>2)
858                 return CMD_STATUS_ERROR;
859
860         step = stof(argv(1));
861
862         self.bot_cmd_aim_begin = self.v_angle;
863         self.bot_cmd_aim_end = vectoangles(v - (self.origin + self.view_ofs));
864         self.bot_cmd_aim_end_x = -self.bot_cmd_aim_end_x;
865
866         self.bot_cmd_aim_begintime = time;
867         self.bot_cmd_aim_endtime = time + step;
868
869         return CMD_STATUS_EXECUTING;
870 }
871
872 .float bot_cmd_keys;
873
874 #define BOT_CMD_KEY_NONE        0
875 #define BOT_CMD_KEY_FORWARD     1
876 #define BOT_CMD_KEY_BACKWARD    2
877 #define BOT_CMD_KEY_RIGHT       4
878 #define BOT_CMD_KEY_LEFT        8
879 #define BOT_CMD_KEY_JUMP        16
880 #define BOT_CMD_KEY_ATTACK1     32
881 #define BOT_CMD_KEY_ATTACK2     64
882 #define BOT_CMD_KEY_USE         128
883 #define BOT_CMD_KEY_HOOK        256
884 #define BOT_CMD_KEY_CROUCH      512
885 #define BOT_CMD_KEY_CHAT        1024
886
887 float bot_presskeys()
888 {
889         self.movement = '0 0 0';
890         self.BUTTON_JUMP = FALSE;
891         self.BUTTON_CROUCH = FALSE;
892         self.BUTTON_ATCK = FALSE;
893         self.BUTTON_ATCK2 = FALSE;
894         self.BUTTON_USE = FALSE;
895         self.BUTTON_HOOK = FALSE;
896         self.BUTTON_CHAT = FALSE;
897
898         if(self.bot_cmd_keys == BOT_CMD_KEY_NONE)
899                 return FALSE;
900
901         if(self.bot_cmd_keys & BOT_CMD_KEY_FORWARD)
902                 self.movement_x = autocvar_sv_maxspeed;
903         else if(self.bot_cmd_keys & BOT_CMD_KEY_BACKWARD)
904                 self.movement_x = -autocvar_sv_maxspeed;
905
906         if(self.bot_cmd_keys & BOT_CMD_KEY_RIGHT)
907                 self.movement_y = autocvar_sv_maxspeed;
908         else if(self.bot_cmd_keys & BOT_CMD_KEY_LEFT)
909                 self.movement_y = -autocvar_sv_maxspeed;
910
911         if(self.bot_cmd_keys & BOT_CMD_KEY_JUMP)
912                 self.BUTTON_JUMP = TRUE;
913
914         if(self.bot_cmd_keys & BOT_CMD_KEY_CROUCH)
915                 self.BUTTON_CROUCH = TRUE;
916
917         if(self.bot_cmd_keys & BOT_CMD_KEY_ATTACK1)
918                 self.BUTTON_ATCK = TRUE;
919
920         if(self.bot_cmd_keys & BOT_CMD_KEY_ATTACK2)
921                 self.BUTTON_ATCK2 = TRUE;
922
923         if(self.bot_cmd_keys & BOT_CMD_KEY_USE)
924                 self.BUTTON_USE = TRUE;
925
926         if(self.bot_cmd_keys & BOT_CMD_KEY_HOOK)
927                 self.BUTTON_HOOK = TRUE;
928
929         if(self.bot_cmd_keys & BOT_CMD_KEY_CHAT)
930                 self.BUTTON_CHAT = TRUE;
931
932         return TRUE;
933 }
934
935
936 float bot_cmd_keypress_handler(string key, float enabled)
937 {
938         switch(key)
939         {
940                 case "all":
941                         if(enabled)
942                                 self.bot_cmd_keys = power2of(20) - 1; // >:)
943                         else
944                                 self.bot_cmd_keys = BOT_CMD_KEY_NONE;
945                 case "forward":
946                         if(enabled)
947                         {
948                                 self.bot_cmd_keys |= BOT_CMD_KEY_FORWARD;
949                                 self.bot_cmd_keys &~= BOT_CMD_KEY_BACKWARD;
950                         }
951                         else
952                                 self.bot_cmd_keys &~= BOT_CMD_KEY_FORWARD;
953                         break;
954                 case "backward":
955                         if(enabled)
956                         {
957                                 self.bot_cmd_keys |= BOT_CMD_KEY_BACKWARD;
958                                 self.bot_cmd_keys &~= BOT_CMD_KEY_FORWARD;
959                         }
960                         else
961                                 self.bot_cmd_keys &~= BOT_CMD_KEY_BACKWARD;
962                         break;
963                 case "left":
964                         if(enabled)
965                         {
966                                 self.bot_cmd_keys |= BOT_CMD_KEY_LEFT;
967                                 self.bot_cmd_keys &~= BOT_CMD_KEY_RIGHT;
968                         }
969                         else
970                                 self.bot_cmd_keys &~= BOT_CMD_KEY_LEFT;
971                         break;
972                 case "right":
973                         if(enabled)
974                         {
975                                 self.bot_cmd_keys |= BOT_CMD_KEY_RIGHT;
976                                 self.bot_cmd_keys &~= BOT_CMD_KEY_LEFT;
977                         }
978                         else
979                                 self.bot_cmd_keys &~= BOT_CMD_KEY_RIGHT;
980                         break;
981                 case "jump":
982                         if(enabled)
983                                 self.bot_cmd_keys |= BOT_CMD_KEY_JUMP;
984                         else
985                                 self.bot_cmd_keys &~= BOT_CMD_KEY_JUMP;
986                         break;
987                 case "crouch":
988                         if(enabled)
989                                 self.bot_cmd_keys |= BOT_CMD_KEY_CROUCH;
990                         else
991                                 self.bot_cmd_keys &~= BOT_CMD_KEY_CROUCH;
992                         break;
993                 case "attack1":
994                         if(enabled)
995                                 self.bot_cmd_keys |= BOT_CMD_KEY_ATTACK1;
996                         else
997                                 self.bot_cmd_keys &~= BOT_CMD_KEY_ATTACK1;
998                         break;
999                 case "attack2":
1000                         if(enabled)
1001                                 self.bot_cmd_keys |= BOT_CMD_KEY_ATTACK2;
1002                         else
1003                                 self.bot_cmd_keys &~= BOT_CMD_KEY_ATTACK2;
1004                         break;
1005                 case "use":
1006                         if(enabled)
1007                                 self.bot_cmd_keys |= BOT_CMD_KEY_USE;
1008                         else
1009                                 self.bot_cmd_keys &~= BOT_CMD_KEY_USE;
1010                         break;
1011                 case "hook":
1012                         if(enabled)
1013                                 self.bot_cmd_keys |= BOT_CMD_KEY_HOOK;
1014                         else
1015                                 self.bot_cmd_keys &~= BOT_CMD_KEY_HOOK;
1016                         break;
1017                 case "chat":
1018                         if(enabled)
1019                                 self.bot_cmd_keys |= BOT_CMD_KEY_CHAT;
1020                         else
1021                                 self.bot_cmd_keys &~= BOT_CMD_KEY_CHAT;
1022                         break;
1023                 default:
1024                         break;
1025         }
1026
1027         return CMD_STATUS_FINISHED;
1028 }
1029
1030 float bot_cmd_presskey()
1031 {
1032         string key;
1033
1034         key = bot_cmd.bot_cmd_parm_string;
1035
1036         bot_cmd_keypress_handler(key,TRUE);
1037
1038         return CMD_STATUS_FINISHED;
1039 }
1040
1041 float bot_cmd_releasekey()
1042 {
1043         string key;
1044
1045         key = bot_cmd.bot_cmd_parm_string;
1046
1047         return bot_cmd_keypress_handler(key,FALSE);
1048 }
1049
1050 float bot_cmd_pause()
1051 {
1052         self.button1        = 0;
1053         self.button8        = 0;
1054         self.BUTTON_USE     = 0;
1055         self.BUTTON_ATCK    = 0;
1056         self.BUTTON_JUMP    = 0;
1057         self.BUTTON_HOOK    = 0;
1058         self.BUTTON_CHAT    = 0;
1059         self.BUTTON_ATCK2   = 0;
1060         self.BUTTON_CROUCH  = 0;
1061
1062         self.movement = '0 0 0';
1063         self.bot_cmd_keys = BOT_CMD_KEY_NONE;
1064
1065         self.bot_exec_status |= BOT_EXEC_STATUS_PAUSED;
1066         return CMD_STATUS_FINISHED;
1067 }
1068
1069 float bot_cmd_moveto()
1070 {
1071         return self.cmd_moveto(bot_cmd.bot_cmd_parm_vector);
1072 }
1073
1074 float bot_cmd_movetotarget()
1075 {
1076         entity e;
1077         e = bot_getplace(bot_cmd.bot_cmd_parm_string);
1078         if(!e)
1079                 return CMD_STATUS_ERROR;
1080         return self.cmd_moveto(e.origin + (e.mins + e.maxs) * 0.5);
1081 }
1082
1083 float bot_cmd_resetgoal()
1084 {
1085         return self.cmd_resetgoal();
1086 }
1087
1088
1089 float bot_cmd_sound()
1090 {
1091         string f;
1092         f = bot_cmd.bot_cmd_parm_string;
1093
1094         precache_sound(f);
1095         sound(self, CH_WEAPON_B, f, VOL_BASE, ATTN_MIN);
1096
1097         return CMD_STATUS_FINISHED;
1098 }
1099
1100 .entity tuba_note;
1101 float bot_cmd_debug_assert_canfire()
1102 {
1103         float f;
1104         f = bot_cmd.bot_cmd_parm_float;
1105
1106         if(self.weaponentity.state != WS_READY)
1107         {
1108                 if(f)
1109                 {
1110                         self.colormod = '0 8 8';
1111                         print("Bot ", self.netname, " using ", self.weaponname, " wants to fire, inhibited by weaponentity state\n");
1112                 }
1113         }
1114         else if(ATTACK_FINISHED(self) > time)
1115         {
1116                 if(f)
1117                 {
1118                         self.colormod = '8 0 8';
1119                         print("Bot ", self.netname, " using ", self.weaponname, " wants to fire, inhibited by ATTACK_FINISHED (", ftos(ATTACK_FINISHED(self) - time), " seconds left)\n");
1120                 }
1121         }
1122         else if(self.tuba_note)
1123         {
1124                 if(f)
1125                 {
1126                         self.colormod = '8 0 0';
1127                         print("Bot ", self.netname, " using ", self.weaponname, " wants to fire, bot still has an active tuba note\n");
1128                 }
1129         }
1130         else
1131         {
1132                 if(!f)
1133                 {
1134                         self.colormod = '8 8 0';
1135                         print("Bot ", self.netname, " using ", self.weaponname, " thinks it has fired, but apparently did not; ATTACK_FINISHED says ", ftos(ATTACK_FINISHED(self) - time), " seconds left\n");
1136                 }
1137         }
1138
1139         return CMD_STATUS_FINISHED;
1140 }
1141
1142 //
1143
1144 void bot_command_executed(float rm)
1145 {
1146         entity cmd;
1147
1148         cmd = bot_cmd;
1149
1150         if(rm)
1151                 bot_dequeuecommand(self, self.bot_cmd_execution_index);
1152
1153         self.bot_cmd_execution_index++;
1154 }
1155
1156 void bot_setcurrentcommand()
1157 {
1158         bot_cmd = world;
1159
1160         if(!self.bot_cmd_current)
1161         {
1162                 self.bot_cmd_current = spawn();
1163                 self.bot_cmd_current.classname = "bot_cmd";
1164                 self.bot_cmd_current.is_bot_cmd = 1;
1165         }
1166
1167         bot_cmd = self.bot_cmd_current;
1168         if(bot_cmd.bot_cmd_index != self.bot_cmd_execution_index || self.bot_cmd_execution_index == 0)
1169         {
1170                 if(bot_havecommand(self, self.bot_cmd_execution_index))
1171                 {
1172                         string cmdstring;
1173                         cmdstring = bot_readcommand(self, self.bot_cmd_execution_index);
1174                         if(bot_decodecommand(cmdstring))
1175                         {
1176                                 bot_cmd.owner = self;
1177                                 bot_cmd.bot_cmd_index = self.bot_cmd_execution_index;
1178                         }
1179                         else
1180                         {
1181                                 // Invalid command, remove from queue
1182                                 bot_cmd = world;
1183                                 bot_dequeuecommand(self, self.bot_cmd_execution_index);
1184                                 self.bot_cmd_execution_index++;
1185                         }
1186                 }
1187                 else
1188                         bot_cmd = world;
1189         }
1190 }
1191
1192 void bot_resetqueues()
1193 {
1194         entity cl;
1195         float i;
1196
1197         FOR_EACH_CLIENT(cl) if(cl.isbot)
1198         {
1199                 cl.bot_cmd_execution_index = 0;
1200                 if(cl.bot_cmdqueuebuf_allocated)
1201                         bot_clearqueue(cl);
1202                 // also, cancel all barriers
1203                 cl.bot_barrier = 0;
1204                 for(i = 0; i < cl.bot_places_count; ++i)
1205                 {
1206                         strunzone(cl.(bot_placenames[i]));
1207                         cl.(bot_placenames[i]) = string_null;
1208                 }
1209                 cl.bot_places_count = 0;
1210         }
1211
1212         bot_barriertime = time;
1213 }
1214
1215 // Here we map commands to functions and deal with complex interactions between commands and execution states
1216 // NOTE: Of course you need to include your commands here too :)
1217 float bot_execute_commands_once()
1218 {
1219         float status, ispressingkey;
1220
1221         // Find command
1222         bot_setcurrentcommand();
1223
1224         // if we have no bot command, better return
1225         // old logic kept pressing previously pressed keys, but that has problems
1226         // (namely, it means you cannot make a bot "normal" ever again)
1227         // to keep a bot walking for a while, use the "wait" bot command
1228         if(bot_cmd == world)
1229                 return FALSE;
1230
1231         // Ignore all commands except continue when the bot is paused
1232         if(self.bot_exec_status & BOT_EXEC_STATUS_PAUSED)
1233         if(bot_cmd.bot_cmd_type!=BOT_CMD_CONTINUE)
1234         {
1235                 if(bot_cmd.bot_cmd_type!=BOT_CMD_NULL)
1236                 {
1237                         bot_command_executed(TRUE);
1238                         print( "WARNING: Commands are ignored while the bot is paused. Use the command 'continue' instead.\n");
1239                 }
1240                 return 1;
1241         }
1242
1243         // Keep pressing keys raised by the "presskey" command
1244         ispressingkey = !!bot_presskeys();
1245
1246         // Handle conditions
1247         if not(bot_cmd.bot_cmd_type==BOT_CMD_FI||bot_cmd.bot_cmd_type==BOT_CMD_ELSE)
1248         if(self.bot_cmd_condition_status & CMD_CONDITION_TRUE && self.bot_cmd_condition_status & CMD_CONDITION_FALSE_BLOCK)
1249         {
1250                 bot_command_executed(TRUE);
1251                 return -1;
1252         }
1253         else if(self.bot_cmd_condition_status & CMD_CONDITION_FALSE && self.bot_cmd_condition_status & CMD_CONDITION_TRUE_BLOCK)
1254         {
1255                 bot_command_executed(TRUE);
1256                 return -1;
1257         }
1258
1259         // Map commands to functions
1260         switch(bot_cmd.bot_cmd_type)
1261         {
1262                 case BOT_CMD_NULL:
1263                         return ispressingkey;
1264                         //break;
1265                 case BOT_CMD_PAUSE:
1266                         status = bot_cmd_pause();
1267                         break;
1268                 case BOT_CMD_CONTINUE:
1269                         status = bot_cmd_continue();
1270                         break;
1271                 case BOT_CMD_WAIT:
1272                         status = bot_cmd_wait();
1273                         break;
1274                 case BOT_CMD_WAIT_UNTIL:
1275                         status = bot_cmd_wait_until();
1276                         break;
1277                 case BOT_CMD_TURN:
1278                         status = bot_cmd_turn();
1279                         break;
1280                 case BOT_CMD_MOVETO:
1281                         status = bot_cmd_moveto();
1282                         break;
1283                 case BOT_CMD_MOVETOTARGET:
1284                         status = bot_cmd_movetotarget();
1285                         break;
1286                 case BOT_CMD_RESETGOAL:
1287                         status = bot_cmd_resetgoal();
1288                         break;
1289                 case BOT_CMD_CC:
1290                         status = bot_cmd_cc();
1291                         break;
1292                 case BOT_CMD_IF:
1293                         status = bot_cmd_if();
1294                         break;
1295                 case BOT_CMD_ELSE:
1296                         status = bot_cmd_else();
1297                         break;
1298                 case BOT_CMD_FI:
1299                         status = bot_cmd_fi();
1300                         break;
1301                 case BOT_CMD_RESETAIM:
1302                         status = bot_cmd_resetaim();
1303                         break;
1304                 case BOT_CMD_AIM:
1305                         status = bot_cmd_aim();
1306                         break;
1307                 case BOT_CMD_AIMTARGET:
1308                         status = bot_cmd_aimtarget();
1309                         break;
1310                 case BOT_CMD_PRESSKEY:
1311                         status = bot_cmd_presskey();
1312                         break;
1313                 case BOT_CMD_RELEASEKEY:
1314                         status = bot_cmd_releasekey();
1315                         break;
1316                 case BOT_CMD_SELECTWEAPON:
1317                         status = bot_cmd_select_weapon();
1318                         break;
1319                 case BOT_CMD_IMPULSE:
1320                         status = bot_cmd_impulse();
1321                         break;
1322                 case BOT_CMD_BARRIER:
1323                         status = bot_cmd_barrier();
1324                         break;
1325                 case BOT_CMD_CONSOLE:
1326                         localcmd(strcat(bot_cmd.bot_cmd_parm_string, "\n"));
1327                         status = CMD_STATUS_FINISHED;
1328                         break;
1329                 case BOT_CMD_SOUND:
1330                         status = bot_cmd_sound();
1331                         break;
1332                 case BOT_CMD_DEBUG_ASSERT_CANFIRE:
1333                         status = bot_cmd_debug_assert_canfire();
1334                         break;
1335                 default:
1336                         print(strcat("ERROR: Invalid command on queue with id '",ftos(bot_cmd.bot_cmd_type),"'\n"));
1337                         return 0;
1338         }
1339
1340         if (status==CMD_STATUS_ERROR)
1341                 print(strcat("ERROR: The command '",bot_cmd_string[bot_cmd.bot_cmd_type],"' returned an error status\n"));
1342
1343         // Move execution pointer
1344         if(status==CMD_STATUS_EXECUTING)
1345         {
1346                 return 1;
1347         }
1348         else
1349         {
1350                 if(autocvar_g_debug_bot_commands)
1351                 {
1352                         string parms;
1353
1354                         switch(bot_cmd_parm_type[bot_cmd.bot_cmd_type])
1355                         {
1356                                 case BOT_CMD_PARAMETER_FLOAT:
1357                                         parms = ftos(bot_cmd.bot_cmd_parm_float);
1358                                         break;
1359                                 case BOT_CMD_PARAMETER_STRING:
1360                                         parms = bot_cmd.bot_cmd_parm_string;
1361                                         break;
1362                                 case BOT_CMD_PARAMETER_VECTOR:
1363                                         parms = vtos(bot_cmd.bot_cmd_parm_vector);
1364                                         break;
1365                                 default:
1366                                         parms = "";
1367                                         break;
1368                         }
1369                         clientcommand(self,strcat("say ^7", bot_cmd_string[bot_cmd.bot_cmd_type]," ",parms,"\n"));
1370                 }
1371
1372                 bot_command_executed(TRUE);
1373         }
1374
1375         if(status == CMD_STATUS_FINISHED)
1376                 return -1;
1377
1378         return CMD_STATUS_ERROR;
1379 }
1380
1381 // This function should be (the only) called directly from the bot ai loop
1382 float bot_execute_commands()
1383 {
1384         float f;
1385         do
1386         {
1387                 f = bot_execute_commands_once();
1388         }
1389         while(f < 0);
1390         return f;
1391 }