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