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