]> de.git.xonotic.org Git - xonotic/xonotic-data.pk3dir.git/blob - qcsrc/server/bot/scripting.qc
Merge branch 'master' into terencehill/music_player
[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 #define 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                 float 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 #define BOT_CMD_NULL                    0
142 #define BOT_CMD_PAUSE                   1
143 #define BOT_CMD_CONTINUE                2
144 #define BOT_CMD_WAIT                    3
145 #define BOT_CMD_TURN                    4
146 #define BOT_CMD_MOVETO                  5
147 #define BOT_CMD_RESETGOAL               6       // Not implemented yet
148 #define BOT_CMD_CC                      7
149 #define BOT_CMD_IF                      8
150 #define BOT_CMD_ELSE                    9
151 #define BOT_CMD_FI                      10
152 #define BOT_CMD_RESETAIM                11
153 #define BOT_CMD_AIM                     12
154 #define BOT_CMD_PRESSKEY                13
155 #define BOT_CMD_RELEASEKEY              14
156 #define BOT_CMD_SELECTWEAPON            15
157 #define BOT_CMD_IMPULSE                 16
158 #define BOT_CMD_WAIT_UNTIL              17
159 #define BOT_CMD_MOVETOTARGET    18
160 #define BOT_CMD_AIMTARGET       19
161 #define BOT_CMD_BARRIER         20
162 #define BOT_CMD_CONSOLE                 21
163 #define BOT_CMD_SOUND                   22
164 #define BOT_CMD_DEBUG_ASSERT_CANFIRE 23
165 #define BOT_CMD_WHILE                   24      // TODO: Not implemented yet
166 #define BOT_CMD_WEND                    25      // TODO: Not implemented yet
167 #define BOT_CMD_CHASE                   26      // TODO: Not implemented yet
168
169 #define 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 #define BOT_CMD_PARAMETER_NONE          0
178 #define BOT_CMD_PARAMETER_FLOAT         1
179 #define BOT_CMD_PARAMETER_STRING        2
180 #define BOT_CMD_PARAMETER_VECTOR        3
181
182 float bot_cmds_initialized;
183 float 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 .float 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, i;
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         for(i=1;i<BOT_CMD_COUNTER;++i)
343         {
344                 if(bot_cmd_string[i]!=cmdstring)
345                         continue;
346
347                 cmd_parm_type = bot_cmd_parm_type[i];
348
349                 if(cmd_parm_type!=BOT_CMD_PARAMETER_NONE&&parm=="")
350                 {
351                         print("ERROR: A parameter is required for this command\n");
352                         return 0;
353                 }
354
355                 // Load command into queue
356                 bot_cmd.bot_cmd_type = i;
357
358                 // Attach parameter
359                 switch(cmd_parm_type)
360                 {
361                         case BOT_CMD_PARAMETER_FLOAT:
362                                 bot_cmd.bot_cmd_parm_float = stof(parm);
363                                 break;
364                         case BOT_CMD_PARAMETER_STRING:
365                                 if(bot_cmd.bot_cmd_parm_string)
366                                         strunzone(bot_cmd.bot_cmd_parm_string);
367                                 bot_cmd.bot_cmd_parm_string = strzone(parm);
368                                 break;
369                         case BOT_CMD_PARAMETER_VECTOR:
370                                 bot_cmd.bot_cmd_parm_vector = stov(parm);
371                                 break;
372                         default:
373                                 break;
374                 }
375                 return 1;
376         }
377         print("ERROR: No such command '", cmdstring, "'\n");
378         return 0;
379 }
380
381 void bot_cmdhelp(string scmd)
382 {
383         float i, ntype;
384         string stype;
385
386         if(!bot_cmds_initialized)
387                 bot_commands_init();
388
389         for(i=1;i<BOT_CMD_COUNTER;++i)
390         {
391                 if(bot_cmd_string[i]!=scmd)
392                         continue;
393
394                 ntype = bot_cmd_parm_type[i];
395
396                 switch(ntype)
397                 {
398                         case BOT_CMD_PARAMETER_FLOAT:
399                                 stype = "float number";
400                                 break;
401                         case BOT_CMD_PARAMETER_STRING:
402                                 stype = "string";
403                                 break;
404                         case BOT_CMD_PARAMETER_VECTOR:
405                                 stype = "vector";
406                                 break;
407                         default:
408                                 stype = "none";
409                                 break;
410                 }
411
412                 print(strcat("Command: ",bot_cmd_string[i],"\nParameter: <",stype,"> \n"));
413
414                 print("Description: ");
415                 switch(i)
416                 {
417                         case BOT_CMD_PAUSE:
418                                 print("Stops the bot completely. Any command other than 'continue' will be ignored.");
419                                 break;
420                         case BOT_CMD_CONTINUE:
421                                 print("Disable paused status");
422                                 break;
423                         case BOT_CMD_WAIT:
424                                 print("Pause command parsing and bot ai for N seconds. Pressed key will remain pressed");
425                                 break;
426                         case BOT_CMD_WAIT_UNTIL:
427                                 print("Pause command parsing and bot ai until time is N from the last barrier. Pressed key will remain pressed");
428                                 break;
429                         case BOT_CMD_BARRIER:
430                                 print("Waits till all bots that have a command queue reach this command. Pressed key will remain pressed");
431                                 break;
432                         case BOT_CMD_TURN:
433                                 print("Look to the right or left N degrees. For turning to the left use positive numbers.");
434                                 break;
435                         case BOT_CMD_MOVETO:
436                                 print("Walk to an specific coordinate on the map. Usage: moveto \"x y z\"");
437                                 break;
438                         case BOT_CMD_MOVETOTARGET:
439                                 print("Walk to the specific target on the map");
440                                 break;
441                         case BOT_CMD_RESETGOAL:
442                                 print("Resets the goal stack");
443                                 break;
444                         case BOT_CMD_CC:
445                                 print("Execute client command. Examples: cc \"say something\"; cc god; cc \"name newnickname\"; cc kill;");
446                                 break;
447                         case BOT_CMD_IF:
448                                 print("Perform simple conditional execution.\n");
449                                 print("Syntax: \n");
450                                 print("        sv_cmd .. if \"condition\"\n");
451                                 print("        sv_cmd ..        <instruction if true>\n");
452                                 print("        sv_cmd ..        <instruction if true>\n");
453                                 print("        sv_cmd .. else\n");
454                                 print("        sv_cmd ..        <instruction if false>\n");
455                                 print("        sv_cmd ..        <instruction if false>\n");
456                                 print("        sv_cmd .. fi\n");
457                                 print("Conditions: a=b, a>b, a<b, a\t\t(spaces not allowed)\n");
458                                 print("            Values in conditions can be numbers, cvars in the form cvar.cvar_string or special fields\n");
459                                 print("Fields: health, speed, flagcarrier\n");
460                                 print("Examples: if health>50; if health>cvar.g_balance_laser_primary_damage; if flagcarrier;");
461                                 break;
462                         case BOT_CMD_RESETAIM:
463                                 print("Points the aim to the coordinates x,y 0,0");
464                                 break;
465                         case BOT_CMD_AIM:
466                                 print("Move the aim x/y (horizontal/vertical) degrees relatives to the bot\n");
467                                 print("There is a 3rd optional parameter telling in how many seconds the aim has to reach the new position\n");
468                                 print("Examples: aim \"90 0\"   // Turn 90 degrees inmediately (positive numbers move to the left/up)\n");
469                                 print("          aim \"0 90 2\" // Will gradually look to the sky in the next two seconds");
470                                 break;
471                         case BOT_CMD_AIMTARGET:
472                                 print("Points the aim to given target");
473                                 break;
474                         case BOT_CMD_PRESSKEY:
475                                 print("Press one of the following keys: forward, backward, left, right, jump, crouch, attack1, attack2, use\n");
476                                 print("Multiple keys can be pressed at time (with many presskey calls) and it will remain pressed until the command \"releasekey\" is called");
477                                 print("Note: The script will not return the control to the bot ai until all keys are released");
478                                 break;
479                         case BOT_CMD_RELEASEKEY:
480                                 print("Release previoulsy used keys. Use the parameter \"all\" to release all keys");
481                                 break;
482                         case BOT_CMD_SOUND:
483                                 print("play sound file at bot location");
484                                 break;
485                         case BOT_CMD_DEBUG_ASSERT_CANFIRE:
486                                 print("verify the state of the weapon entity");
487                                 break;
488                         default:
489                                 print("This command has no description yet.");
490                                 break;
491                 }
492                 print("\n");
493         }
494 }
495
496 void bot_list_commands()
497 {
498         float i;
499         string ptype;
500
501         if(!bot_cmds_initialized)
502                 bot_commands_init();
503
504         print("List of all available commands:\n");
505         print("  Command - Parameter Type\n");
506
507         for(i=1;i<BOT_CMD_COUNTER;++i)
508         {
509                 switch(bot_cmd_parm_type[i])
510                 {
511                         case BOT_CMD_PARAMETER_FLOAT:
512                                 ptype = "float number";
513                                 break;
514                         case BOT_CMD_PARAMETER_STRING:
515                                 ptype = "string";
516                                 break;
517                         case BOT_CMD_PARAMETER_VECTOR:
518                                 ptype = "vector";
519                                 break;
520                         default:
521                                 ptype = "none";
522                                 break;
523                 }
524                 print(strcat("  ",bot_cmd_string[i]," - <",ptype,"> \n"));
525         }
526 }
527
528 // Commands code
529 .float bot_exec_status;
530
531 #define BOT_EXEC_STATUS_IDLE    0
532 #define BOT_EXEC_STATUS_PAUSED  1
533 #define BOT_EXEC_STATUS_WAITING 2
534
535 #define CMD_STATUS_EXECUTING    0
536 #define CMD_STATUS_FINISHED     1
537 #define CMD_STATUS_ERROR        2
538
539 void SV_ParseClientCommand(string s);
540 float bot_cmd_cc()
541 {
542         SV_ParseClientCommand(bot_cmd.bot_cmd_parm_string);
543         return CMD_STATUS_FINISHED;
544 }
545
546 float bot_cmd_impulse()
547 {
548         self.impulse = bot_cmd.bot_cmd_parm_float;
549         return CMD_STATUS_FINISHED;
550 }
551
552 float bot_cmd_continue()
553 {
554         self.bot_exec_status &= ~BOT_EXEC_STATUS_PAUSED;
555         return CMD_STATUS_FINISHED;
556 }
557
558 .float bot_cmd_wait_time;
559 float bot_cmd_wait()
560 {
561         if(self.bot_exec_status & BOT_EXEC_STATUS_WAITING)
562         {
563                 if(time>=self.bot_cmd_wait_time)
564                 {
565                         self.bot_exec_status &= ~BOT_EXEC_STATUS_WAITING;
566                         return CMD_STATUS_FINISHED;
567                 }
568                 else
569                         return CMD_STATUS_EXECUTING;
570         }
571
572         self.bot_cmd_wait_time = time + bot_cmd.bot_cmd_parm_float;
573         self.bot_exec_status |= BOT_EXEC_STATUS_WAITING;
574         return CMD_STATUS_EXECUTING;
575 }
576
577 float bot_cmd_wait_until()
578 {
579         if(time < bot_cmd.bot_cmd_parm_float + bot_barriertime)
580         {
581                 self.bot_exec_status |= BOT_EXEC_STATUS_WAITING;
582                 return CMD_STATUS_EXECUTING;
583         }
584         self.bot_exec_status &= ~BOT_EXEC_STATUS_WAITING;
585         return CMD_STATUS_FINISHED;
586 }
587
588 float bot_cmd_barrier()
589 {
590         entity cl;
591
592         // 0 = no barrier, 1 = waiting, 2 = waiting finished
593
594         if(self.bot_barrier == 0) // initialization
595         {
596                 self.bot_barrier = 1;
597
598                 //self.colormod = '4 4 0';
599         }
600
601         if(self.bot_barrier == 1) // find other bots
602         {
603                 FOR_EACH_CLIENT(cl) if(cl.isbot)
604                 {
605                         if(cl.bot_cmdqueuebuf_allocated)
606                                 if(cl.bot_barrier != 1)
607                                         return CMD_STATUS_EXECUTING; // not all are at the barrier yet
608                 }
609
610                 // all bots hit the barrier!
611                 FOR_EACH_CLIENT(cl) if(cl.isbot)
612                 {
613                         cl.bot_barrier = 2; // acknowledge barrier
614                 }
615
616                 bot_barriertime = time;
617         }
618
619         // if we get here, the barrier is finished
620         // so end it...
621         self.bot_barrier = 0;
622         //self.colormod = '0 0 0';
623
624         return CMD_STATUS_FINISHED;
625 }
626
627 float bot_cmd_turn()
628 {
629         self.v_angle_y = self.v_angle_y + bot_cmd.bot_cmd_parm_float;
630         self.v_angle_y = self.v_angle_y - floor(self.v_angle_y / 360) * 360;
631         return CMD_STATUS_FINISHED;
632 }
633
634 float bot_cmd_select_weapon()
635 {
636         float id;
637
638         id = bot_cmd.bot_cmd_parm_float;
639
640         if(id < WEP_FIRST || id > WEP_LAST)
641                 return CMD_STATUS_ERROR;
642
643         if(client_hasweapon(self, id, TRUE, FALSE))
644                 self.switchweapon = id;
645         else
646                 return CMD_STATUS_ERROR;
647
648         return CMD_STATUS_FINISHED;
649 }
650
651 .float bot_cmd_condition_status;
652
653 #define CMD_CONDITION_NONE              0
654 #define CMD_CONDITION_TRUE              1
655 #define CMD_CONDITION_FALSE             2
656 #define CMD_CONDITION_TRUE_BLOCK        4
657 #define CMD_CONDITION_FALSE_BLOCK       8
658
659 float bot_cmd_eval(string expr)
660 {
661         // Search for numbers
662         if(strstrofs("0123456789", substring(expr, 0, 1), 0) >= 0)
663         {
664                 return stof(expr);
665         }
666
667         // Search for cvars
668         if(substring(expr, 0, 5)=="cvar.")
669         {
670                 return cvar(substring(expr, 5, strlen(expr)));
671         }
672
673         // Search for fields
674         switch(expr)
675         {
676                 case "health":
677                         return self.health;
678                 case "speed":
679                         return vlen(self.velocity);
680                 case "flagcarrier":
681                         return ((self.flagcarried!=world));
682         }
683
684         print(strcat("ERROR: Unable to convert the expression '",expr,"' into a numeric value\n"));
685         return 0;
686 }
687
688 float bot_cmd_if()
689 {
690         string expr, val_a, val_b;
691         float cmpofs;
692
693         if(self.bot_cmd_condition_status != CMD_CONDITION_NONE)
694         {
695                 // Only one "if" block is allowed at time
696                 print("ERROR: Only one conditional block can be processed at time");
697                 bot_clearqueue(self);
698                 return CMD_STATUS_ERROR;
699         }
700
701         self.bot_cmd_condition_status |= CMD_CONDITION_TRUE_BLOCK;
702
703         // search for operators
704         expr = bot_cmd.bot_cmd_parm_string;
705
706         cmpofs = strstrofs(expr,"=",0);
707
708         if(cmpofs>0)
709         {
710                 val_a = substring(expr,0,cmpofs);
711                 val_b = substring(expr,cmpofs+1,strlen(expr));
712
713                 if(bot_cmd_eval(val_a)==bot_cmd_eval(val_b))
714                         self.bot_cmd_condition_status |= CMD_CONDITION_TRUE;
715                 else
716                         self.bot_cmd_condition_status |= CMD_CONDITION_FALSE;
717
718                 return CMD_STATUS_FINISHED;
719         }
720
721         cmpofs = strstrofs(expr,">",0);
722
723         if(cmpofs>0)
724         {
725                 val_a = substring(expr,0,cmpofs);
726                 val_b = substring(expr,cmpofs+1,strlen(expr));
727
728                 if(bot_cmd_eval(val_a)>bot_cmd_eval(val_b))
729                         self.bot_cmd_condition_status |= CMD_CONDITION_TRUE;
730                 else
731                         self.bot_cmd_condition_status |= CMD_CONDITION_FALSE;
732
733                 return CMD_STATUS_FINISHED;
734         }
735
736         cmpofs = strstrofs(expr,"<",0);
737
738         if(cmpofs>0)
739         {
740                 val_a = substring(expr,0,cmpofs);
741                 val_b = substring(expr,cmpofs+1,strlen(expr));
742
743                 if(bot_cmd_eval(val_a)<bot_cmd_eval(val_b))
744                         self.bot_cmd_condition_status |= CMD_CONDITION_TRUE;
745                 else
746                         self.bot_cmd_condition_status |= CMD_CONDITION_FALSE;
747
748                 return CMD_STATUS_FINISHED;
749         }
750
751         if(bot_cmd_eval(expr))
752                 self.bot_cmd_condition_status |= CMD_CONDITION_TRUE;
753         else
754                 self.bot_cmd_condition_status |= CMD_CONDITION_FALSE;
755
756         return CMD_STATUS_FINISHED;
757 }
758
759 float bot_cmd_else()
760 {
761         self.bot_cmd_condition_status &= ~CMD_CONDITION_TRUE_BLOCK;
762         self.bot_cmd_condition_status |= CMD_CONDITION_FALSE_BLOCK;
763         return CMD_STATUS_FINISHED;
764 }
765
766 float bot_cmd_fi()
767 {
768         self.bot_cmd_condition_status = CMD_CONDITION_NONE;
769         return CMD_STATUS_FINISHED;
770 }
771
772 float bot_cmd_resetaim()
773 {
774         self.v_angle = '0 0 0';
775         return CMD_STATUS_FINISHED;
776 }
777
778 .float bot_cmd_aim_begintime;
779 .float bot_cmd_aim_endtime;
780 .vector bot_cmd_aim_begin;
781 .vector bot_cmd_aim_end;
782
783 float bot_cmd_aim()
784 {
785         // Current direction
786         if(self.bot_cmd_aim_endtime)
787         {
788                 float progress;
789
790                 progress = min(1 - (self.bot_cmd_aim_endtime - time) / (self.bot_cmd_aim_endtime - self.bot_cmd_aim_begintime),1);
791                 self.v_angle = self.bot_cmd_aim_begin + ((self.bot_cmd_aim_end - self.bot_cmd_aim_begin) * progress);
792
793                 if(time>=self.bot_cmd_aim_endtime)
794                 {
795                         self.bot_cmd_aim_endtime = 0;
796                         return CMD_STATUS_FINISHED;
797                 }
798                 else
799                         return CMD_STATUS_EXECUTING;
800         }
801
802         // New aiming direction
803         string parms;
804         float tokens, step;
805
806         parms = bot_cmd.bot_cmd_parm_string;
807
808         tokens = tokenizebyseparator(parms, " ");
809
810         if(tokens<2||tokens>3)
811                 return CMD_STATUS_ERROR;
812
813         step = (tokens == 3) ? stof(argv(2)) : 0;
814
815         if(step == 0)
816         {
817                 self.v_angle_x -= stof(argv(1));
818                 self.v_angle_y += stof(argv(0));
819                 return CMD_STATUS_FINISHED;
820         }
821
822         self.bot_cmd_aim_begin = self.v_angle;
823
824         self.bot_cmd_aim_end_x = self.v_angle_x - stof(argv(1));
825         self.bot_cmd_aim_end_y = self.v_angle_y + stof(argv(0));
826         self.bot_cmd_aim_end_z = 0;
827
828         self.bot_cmd_aim_begintime = time;
829         self.bot_cmd_aim_endtime = time + step;
830
831         return CMD_STATUS_EXECUTING;
832 }
833
834 float bot_cmd_aimtarget()
835 {
836         if(self.bot_cmd_aim_endtime)
837         {
838                 return bot_cmd_aim();
839         }
840
841         entity e;
842         string parms;
843         vector v;
844         float tokens, step;
845
846         parms = bot_cmd.bot_cmd_parm_string;
847
848         tokens = tokenizebyseparator(parms, " ");
849
850         e = bot_getplace(argv(0));
851         if(!e)
852                 return CMD_STATUS_ERROR;
853
854         v = e.origin + (e.mins + e.maxs) * 0.5;
855
856         if(tokens==1)
857         {
858                 self.v_angle = vectoangles(v - (self.origin + self.view_ofs));
859                 self.v_angle_x = -self.v_angle_x;
860                 return CMD_STATUS_FINISHED;
861         }
862
863         if(tokens<1||tokens>2)
864                 return CMD_STATUS_ERROR;
865
866         step = stof(argv(1));
867
868         self.bot_cmd_aim_begin = self.v_angle;
869         self.bot_cmd_aim_end = vectoangles(v - (self.origin + self.view_ofs));
870         self.bot_cmd_aim_end_x = -self.bot_cmd_aim_end_x;
871
872         self.bot_cmd_aim_begintime = time;
873         self.bot_cmd_aim_endtime = time + step;
874
875         return CMD_STATUS_EXECUTING;
876 }
877
878 .float bot_cmd_keys;
879
880 #define BOT_CMD_KEY_NONE        0
881 #define BOT_CMD_KEY_FORWARD     1
882 #define BOT_CMD_KEY_BACKWARD    2
883 #define BOT_CMD_KEY_RIGHT       4
884 #define BOT_CMD_KEY_LEFT        8
885 #define BOT_CMD_KEY_JUMP        16
886 #define BOT_CMD_KEY_ATTACK1     32
887 #define BOT_CMD_KEY_ATTACK2     64
888 #define BOT_CMD_KEY_USE         128
889 #define BOT_CMD_KEY_HOOK        256
890 #define BOT_CMD_KEY_CROUCH      512
891 #define BOT_CMD_KEY_CHAT        1024
892
893 float bot_presskeys()
894 {
895         self.movement = '0 0 0';
896         self.BUTTON_JUMP = FALSE;
897         self.BUTTON_CROUCH = FALSE;
898         self.BUTTON_ATCK = FALSE;
899         self.BUTTON_ATCK2 = FALSE;
900         self.BUTTON_USE = FALSE;
901         self.BUTTON_HOOK = FALSE;
902         self.BUTTON_CHAT = FALSE;
903
904         if(self.bot_cmd_keys == BOT_CMD_KEY_NONE)
905                 return FALSE;
906
907         if(self.bot_cmd_keys & BOT_CMD_KEY_FORWARD)
908                 self.movement_x = autocvar_sv_maxspeed;
909         else if(self.bot_cmd_keys & BOT_CMD_KEY_BACKWARD)
910                 self.movement_x = -autocvar_sv_maxspeed;
911
912         if(self.bot_cmd_keys & BOT_CMD_KEY_RIGHT)
913                 self.movement_y = autocvar_sv_maxspeed;
914         else if(self.bot_cmd_keys & BOT_CMD_KEY_LEFT)
915                 self.movement_y = -autocvar_sv_maxspeed;
916
917         if(self.bot_cmd_keys & BOT_CMD_KEY_JUMP)
918                 self.BUTTON_JUMP = TRUE;
919
920         if(self.bot_cmd_keys & BOT_CMD_KEY_CROUCH)
921                 self.BUTTON_CROUCH = TRUE;
922
923         if(self.bot_cmd_keys & BOT_CMD_KEY_ATTACK1)
924                 self.BUTTON_ATCK = TRUE;
925
926         if(self.bot_cmd_keys & BOT_CMD_KEY_ATTACK2)
927                 self.BUTTON_ATCK2 = TRUE;
928
929         if(self.bot_cmd_keys & BOT_CMD_KEY_USE)
930                 self.BUTTON_USE = TRUE;
931
932         if(self.bot_cmd_keys & BOT_CMD_KEY_HOOK)
933                 self.BUTTON_HOOK = TRUE;
934
935         if(self.bot_cmd_keys & BOT_CMD_KEY_CHAT)
936                 self.BUTTON_CHAT = TRUE;
937
938         return TRUE;
939 }
940
941
942 float bot_cmd_keypress_handler(string key, float enabled)
943 {
944         switch(key)
945         {
946                 case "all":
947                         if(enabled)
948                                 self.bot_cmd_keys = power2of(20) - 1; // >:)
949                         else
950                                 self.bot_cmd_keys = BOT_CMD_KEY_NONE;
951                 case "forward":
952                         if(enabled)
953                         {
954                                 self.bot_cmd_keys |= BOT_CMD_KEY_FORWARD;
955                                 self.bot_cmd_keys &= ~BOT_CMD_KEY_BACKWARD;
956                         }
957                         else
958                                 self.bot_cmd_keys &= ~BOT_CMD_KEY_FORWARD;
959                         break;
960                 case "backward":
961                         if(enabled)
962                         {
963                                 self.bot_cmd_keys |= BOT_CMD_KEY_BACKWARD;
964                                 self.bot_cmd_keys &= ~BOT_CMD_KEY_FORWARD;
965                         }
966                         else
967                                 self.bot_cmd_keys &= ~BOT_CMD_KEY_BACKWARD;
968                         break;
969                 case "left":
970                         if(enabled)
971                         {
972                                 self.bot_cmd_keys |= BOT_CMD_KEY_LEFT;
973                                 self.bot_cmd_keys &= ~BOT_CMD_KEY_RIGHT;
974                         }
975                         else
976                                 self.bot_cmd_keys &= ~BOT_CMD_KEY_LEFT;
977                         break;
978                 case "right":
979                         if(enabled)
980                         {
981                                 self.bot_cmd_keys |= BOT_CMD_KEY_RIGHT;
982                                 self.bot_cmd_keys &= ~BOT_CMD_KEY_LEFT;
983                         }
984                         else
985                                 self.bot_cmd_keys &= ~BOT_CMD_KEY_RIGHT;
986                         break;
987                 case "jump":
988                         if(enabled)
989                                 self.bot_cmd_keys |= BOT_CMD_KEY_JUMP;
990                         else
991                                 self.bot_cmd_keys &= ~BOT_CMD_KEY_JUMP;
992                         break;
993                 case "crouch":
994                         if(enabled)
995                                 self.bot_cmd_keys |= BOT_CMD_KEY_CROUCH;
996                         else
997                                 self.bot_cmd_keys &= ~BOT_CMD_KEY_CROUCH;
998                         break;
999                 case "attack1":
1000                         if(enabled)
1001                                 self.bot_cmd_keys |= BOT_CMD_KEY_ATTACK1;
1002                         else
1003                                 self.bot_cmd_keys &= ~BOT_CMD_KEY_ATTACK1;
1004                         break;
1005                 case "attack2":
1006                         if(enabled)
1007                                 self.bot_cmd_keys |= BOT_CMD_KEY_ATTACK2;
1008                         else
1009                                 self.bot_cmd_keys &= ~BOT_CMD_KEY_ATTACK2;
1010                         break;
1011                 case "use":
1012                         if(enabled)
1013                                 self.bot_cmd_keys |= BOT_CMD_KEY_USE;
1014                         else
1015                                 self.bot_cmd_keys &= ~BOT_CMD_KEY_USE;
1016                         break;
1017                 case "hook":
1018                         if(enabled)
1019                                 self.bot_cmd_keys |= BOT_CMD_KEY_HOOK;
1020                         else
1021                                 self.bot_cmd_keys &= ~BOT_CMD_KEY_HOOK;
1022                         break;
1023                 case "chat":
1024                         if(enabled)
1025                                 self.bot_cmd_keys |= BOT_CMD_KEY_CHAT;
1026                         else
1027                                 self.bot_cmd_keys &= ~BOT_CMD_KEY_CHAT;
1028                         break;
1029                 default:
1030                         break;
1031         }
1032
1033         return CMD_STATUS_FINISHED;
1034 }
1035
1036 float bot_cmd_presskey()
1037 {
1038         string key;
1039
1040         key = bot_cmd.bot_cmd_parm_string;
1041
1042         bot_cmd_keypress_handler(key,TRUE);
1043
1044         return CMD_STATUS_FINISHED;
1045 }
1046
1047 float bot_cmd_releasekey()
1048 {
1049         string key;
1050
1051         key = bot_cmd.bot_cmd_parm_string;
1052
1053         return bot_cmd_keypress_handler(key,FALSE);
1054 }
1055
1056 float bot_cmd_pause()
1057 {
1058         self.button1        = 0;
1059         self.button8        = 0;
1060         self.BUTTON_USE     = 0;
1061         self.BUTTON_ATCK    = 0;
1062         self.BUTTON_JUMP    = 0;
1063         self.BUTTON_HOOK    = 0;
1064         self.BUTTON_CHAT    = 0;
1065         self.BUTTON_ATCK2   = 0;
1066         self.BUTTON_CROUCH  = 0;
1067
1068         self.movement = '0 0 0';
1069         self.bot_cmd_keys = BOT_CMD_KEY_NONE;
1070
1071         self.bot_exec_status |= BOT_EXEC_STATUS_PAUSED;
1072         return CMD_STATUS_FINISHED;
1073 }
1074
1075 float bot_cmd_moveto()
1076 {
1077         return self.cmd_moveto(bot_cmd.bot_cmd_parm_vector);
1078 }
1079
1080 float bot_cmd_movetotarget()
1081 {
1082         entity e;
1083         e = bot_getplace(bot_cmd.bot_cmd_parm_string);
1084         if(!e)
1085                 return CMD_STATUS_ERROR;
1086         return self.cmd_moveto(e.origin + (e.mins + e.maxs) * 0.5);
1087 }
1088
1089 float bot_cmd_resetgoal()
1090 {
1091         return self.cmd_resetgoal();
1092 }
1093
1094
1095 float bot_cmd_sound()
1096 {
1097         string f;
1098         f = bot_cmd.bot_cmd_parm_string;
1099
1100         float n = tokenizebyseparator(f, " ");
1101
1102         string sample = f;
1103         float chan = CH_WEAPON_B;
1104         float vol = VOL_BASE;
1105         float atten = ATTEN_MIN;
1106
1107         if(n >= 1)
1108                 sample = argv(n - 1);
1109         if(n >= 2)
1110                 chan = stof(argv(0));
1111         if(n >= 3)
1112                 vol = stof(argv(1));
1113         if(n >= 4)
1114                 atten = stof(argv(2));
1115
1116         precache_sound(f);
1117         sound(self, chan, sample, vol, atten);
1118
1119         return CMD_STATUS_FINISHED;
1120 }
1121
1122 .entity tuba_note;
1123 float bot_cmd_debug_assert_canfire()
1124 {
1125         float f;
1126         f = bot_cmd.bot_cmd_parm_float;
1127
1128         if(self.weaponentity.state != WS_READY)
1129         {
1130                 if(f)
1131                 {
1132                         self.colormod = '0 8 8';
1133                         print("Bot ", self.netname, " using ", self.weaponname, " wants to fire, inhibited by weaponentity state\n");
1134                 }
1135         }
1136         else if(ATTACK_FINISHED(self) > time)
1137         {
1138                 if(f)
1139                 {
1140                         self.colormod = '8 0 8';
1141                         print("Bot ", self.netname, " using ", self.weaponname, " wants to fire, inhibited by ATTACK_FINISHED (", ftos(ATTACK_FINISHED(self) - time), " seconds left)\n");
1142                 }
1143         }
1144         else if(self.tuba_note)
1145         {
1146                 if(f)
1147                 {
1148                         self.colormod = '8 0 0';
1149                         print("Bot ", self.netname, " using ", self.weaponname, " wants to fire, bot still has an active tuba note\n");
1150                 }
1151         }
1152         else
1153         {
1154                 if(!f)
1155                 {
1156                         self.colormod = '8 8 0';
1157                         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");
1158                 }
1159         }
1160
1161         return CMD_STATUS_FINISHED;
1162 }
1163
1164 //
1165
1166 void bot_command_executed(float rm)
1167 {
1168         entity cmd;
1169
1170         cmd = bot_cmd;
1171
1172         if(rm)
1173                 bot_dequeuecommand(self, self.bot_cmd_execution_index);
1174
1175         self.bot_cmd_execution_index++;
1176 }
1177
1178 void bot_setcurrentcommand()
1179 {
1180         bot_cmd = world;
1181
1182         if(!self.bot_cmd_current)
1183         {
1184                 self.bot_cmd_current = spawn();
1185                 self.bot_cmd_current.classname = "bot_cmd";
1186                 self.bot_cmd_current.is_bot_cmd = 1;
1187         }
1188
1189         bot_cmd = self.bot_cmd_current;
1190         if(bot_cmd.bot_cmd_index != self.bot_cmd_execution_index || self.bot_cmd_execution_index == 0)
1191         {
1192                 if(bot_havecommand(self, self.bot_cmd_execution_index))
1193                 {
1194                         string cmdstring;
1195                         cmdstring = bot_readcommand(self, self.bot_cmd_execution_index);
1196                         if(bot_decodecommand(cmdstring))
1197                         {
1198                                 bot_cmd.owner = self;
1199                                 bot_cmd.bot_cmd_index = self.bot_cmd_execution_index;
1200                         }
1201                         else
1202                         {
1203                                 // Invalid command, remove from queue
1204                                 bot_cmd = world;
1205                                 bot_dequeuecommand(self, self.bot_cmd_execution_index);
1206                                 self.bot_cmd_execution_index++;
1207                         }
1208                 }
1209                 else
1210                         bot_cmd = world;
1211         }
1212 }
1213
1214 void bot_resetqueues()
1215 {
1216         entity cl;
1217         float i;
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(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 }