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