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