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