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