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