]> icculus.org git repositories - divverent/nexuiz.git/blob - data/qcsrc/server/bots_scripting.qc
Converted the bot command queue from arrays to entities. Now server/ compiles again...
[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:\r
295                                 break;\r
296                         case BOT_CMD_RESETGOAL:\r
297                                 break;\r
298                         case BOT_CMD_CC:\r
299                                 print("Execute client command. Examples: cc \"say something\"; cc god; cc \"name newnickname\"; cc kill;");\r
300                                 break;\r
301                         case BOT_CMD_IF:\r
302                                 print("Perform simple conditional execution.\n");\r
303                                 print("Syntax: \n");\r
304                                 print("        sv_cmd .. if \"condition\"\n");\r
305                                 print("        sv_cmd ..        <instruction if true>\n");\r
306                                 print("        sv_cmd ..        <instruction if true>\n");\r
307                                 print("        sv_cmd .. else\n");\r
308                                 print("        sv_cmd ..        <instruction if false>\n");\r
309                                 print("        sv_cmd ..        <instruction if false>\n");\r
310                                 print("        sv_cmd .. fi\n");\r
311                                 print("Conditions: a=b, a>b, a<b, a\t\t(spaces not allowed)\n");\r
312                                 print("            Values in conditions can be numbers, cvars in the form cvar.cvar_string or special fields\n");\r
313                                 print("Fields: health, speed, flagcarrier\n");\r
314                                 print("Examples: if health>50; if health>cvar.g_balance_laser_primary_damage; if flagcarrier;");\r
315                                 break;\r
316                         case BOT_CMD_RESETAIM:\r
317                                 print("Points the aim to the coordinates x,y 0,0");\r
318                                 break;\r
319                         case BOT_CMD_AIM:\r
320                                 print("Move the aim x/y (horizontal/vertical) degrees relatives to the bot\n");\r
321                                 print("There is a 3rd optional parameter telling in how many seconds the aim has to reach the new position\n");\r
322                                 print("Examples: aim \"90 0\"   // Turn 90 degrees inmediately (positive numbers move to the left/up)\n");\r
323                                 print("          aim \"0 90 2\" // Will gradually look to the sky in the next two seconds");\r
324                                 break;\r
325                         case BOT_CMD_PRESSKEY:\r
326                                 print("Press one of the following keys: forward, backward, left, right, jump, crouch, attack1, attack2, use\n");\r
327                                 print("Multiple keys can be pressed at time (with many presskey calls) and it will remain pressed until the command \"releasekey\" is called");\r
328                                 print("Note: The script will not return the control to the bot ai until all keys are released");\r
329                                 break;\r
330                         case BOT_CMD_RELEASEKEY:\r
331                                 print("Release previoulsy used keys. Use the parameter \"all\" to release all keys");\r
332                                 break;\r
333                         default:\r
334                                 print("This command has no description yet.");\r
335                                 break;\r
336                 }\r
337                 print("\n");\r
338         }\r
339 }\r
340 \r
341 void bot_list_commands()\r
342 {\r
343         local float i;\r
344         local string ptype;\r
345 \r
346         if(!bot_cmds_initialized)\r
347                 bot_commands_init();\r
348 \r
349         print("List of all available commands:\n");\r
350         print(" Command\t\t\t\tParameter Type\n");\r
351 \r
352         for(i=1;i<BOT_CMD_COUNTER;++i)\r
353         {\r
354                 switch(bot_cmd_parm_type[i])\r
355                 {\r
356                         case BOT_CMD_PARAMETER_FLOAT:\r
357                                 ptype = "float number";\r
358                                 break;\r
359                         case BOT_CMD_PARAMETER_STRING:\r
360                                 ptype = "string";\r
361                                 break;\r
362                         case BOT_CMD_PARAMETER_VECTOR:\r
363                                 ptype = "vector";\r
364                                 break;\r
365                         default:\r
366                                 ptype = "none";\r
367                                 break;\r
368                 }\r
369                 print(strcat(" ",bot_cmd_string[i],"\t\t\t\t<",ptype,"> \n"));\r
370         }\r
371 }\r
372 \r
373 // Commands code\r
374 .float bot_exec_status;\r
375 \r
376 #define BOT_EXEC_STATUS_IDLE    0\r
377 #define BOT_EXEC_STATUS_PAUSED  1\r
378 #define BOT_EXEC_STATUS_WAITING 2\r
379 \r
380 #define CMD_STATUS_EXECUTING    0\r
381 #define CMD_STATUS_FINISHED     1\r
382 #define CMD_STATUS_ERROR        2\r
383 \r
384 float bot_cmd_cc()\r
385 {\r
386         clientcommand(self,bot_cmd.bot_cmd_parm_string);\r
387         return CMD_STATUS_FINISHED;\r
388 }\r
389 \r
390 float bot_cmd_impulse()\r
391 {\r
392         self.impulse = bot_cmd.bot_cmd_parm_float;\r
393         return CMD_STATUS_FINISHED;\r
394 }\r
395 \r
396 float bot_cmd_continue()\r
397 {\r
398         self.bot_exec_status &~= BOT_EXEC_STATUS_PAUSED;\r
399         return CMD_STATUS_FINISHED;\r
400 }\r
401 \r
402 .float bot_cmd_wait_time;\r
403 float bot_cmd_wait()\r
404 {\r
405         if(self.bot_exec_status & BOT_EXEC_STATUS_WAITING)\r
406         {\r
407                 if(time>=self.bot_cmd_wait_time)\r
408                 {\r
409                         self.bot_exec_status &~= BOT_EXEC_STATUS_WAITING;\r
410                         return CMD_STATUS_FINISHED;\r
411                 }\r
412                 else\r
413                         return CMD_STATUS_EXECUTING;\r
414         }\r
415 \r
416         self.bot_cmd_wait_time = time + bot_cmd.bot_cmd_parm_float;\r
417         self.bot_exec_status |= BOT_EXEC_STATUS_WAITING;\r
418         return CMD_STATUS_EXECUTING;\r
419 }\r
420 \r
421 float bot_cmd_turn()\r
422 {\r
423         self.v_angle_y = self.v_angle_y + bot_cmd.bot_cmd_parm_float;\r
424         self.v_angle_y = self.v_angle_y - floor(self.v_angle_y / 360) * 360;\r
425         return CMD_STATUS_FINISHED;\r
426 }\r
427 \r
428 float bot_cmd_select_weapon()\r
429 {\r
430         local float id;\r
431 \r
432         id = bot_cmd.bot_cmd_parm_float;\r
433 \r
434         if(id < WEP_FIRST || id > WEP_LAST)\r
435                 return CMD_STATUS_ERROR;\r
436 \r
437         if(client_hasweapon(self, id, TRUE, FALSE))\r
438                 self.switchweapon = id;\r
439 \r
440         return CMD_STATUS_FINISHED;\r
441 }\r
442 \r
443 .float bot_cmd_condition_status;\r
444 \r
445 #define CMD_CONDITION_NONE              0\r
446 #define CMD_CONDITION_TRUE              1\r
447 #define CMD_CONDITION_FALSE             2\r
448 #define CMD_CONDITION_TRUE_BLOCK        4\r
449 #define CMD_CONDITION_FALSE_BLOCK       8\r
450 \r
451 float bot_cmd_eval(string expr)\r
452 {\r
453         // Search for numbers\r
454         if(strstrofs("0123456789", substring(expr, 0, 1), 0) >= 0)\r
455         {\r
456                 return stof(expr);\r
457         }\r
458 \r
459         // Search for cvars\r
460         if(substring(expr, 0, 5)=="cvar.")\r
461         {\r
462                 return cvar(substring(expr, 5, strlen(expr)));\r
463         }\r
464 \r
465         // Search for fields\r
466         switch(expr)\r
467         {\r
468                 case "health":\r
469                         return self.health;\r
470                 case "speed":\r
471                         return vlen(self.velocity);\r
472                 case "flagcarrier":\r
473                         return ((self.flagcarried!=world));\r
474         }\r
475 \r
476         print(strcat("ERROR: Unable to convert the expression '",expr,"' into a numeric value\n"));\r
477         return 0;\r
478 }\r
479 \r
480 float bot_cmd_if()\r
481 {\r
482         local string expr, val_a, val_b;\r
483         local float cmpofs;\r
484 \r
485         if(self.bot_cmd_condition_status != CMD_CONDITION_NONE)\r
486         {\r
487                 // Only one "if" block is allowed at time\r
488                 print("ERROR: Only one conditional block can be processed at time");\r
489                 bot_clearqueue();\r
490                 return CMD_STATUS_ERROR;\r
491         }\r
492 \r
493         self.bot_cmd_condition_status |= CMD_CONDITION_TRUE_BLOCK;\r
494 \r
495         // search for operators\r
496         expr = bot_cmd.bot_cmd_parm_string;\r
497 \r
498         cmpofs = strstrofs(expr,"=",0);\r
499 \r
500         if(cmpofs>0)\r
501         {\r
502                 val_a = substring(expr,0,cmpofs);\r
503                 val_b = substring(expr,cmpofs+1,strlen(expr));\r
504 \r
505                 if(bot_cmd_eval(val_a)==bot_cmd_eval(val_b))\r
506                         self.bot_cmd_condition_status |= CMD_CONDITION_TRUE;\r
507                 else\r
508                         self.bot_cmd_condition_status |= CMD_CONDITION_FALSE;\r
509 \r
510                 return CMD_STATUS_FINISHED;\r
511         }\r
512 \r
513         cmpofs = strstrofs(expr,">",0);\r
514 \r
515         if(cmpofs>0)\r
516         {\r
517                 val_a = substring(expr,0,cmpofs);\r
518                 val_b = substring(expr,cmpofs+1,strlen(expr));\r
519 \r
520                 if(bot_cmd_eval(val_a)>bot_cmd_eval(val_b))\r
521                         self.bot_cmd_condition_status |= CMD_CONDITION_TRUE;\r
522                 else\r
523                         self.bot_cmd_condition_status |= CMD_CONDITION_FALSE;\r
524 \r
525                 return CMD_STATUS_FINISHED;\r
526         }\r
527 \r
528         cmpofs = strstrofs(expr,"<",0);\r
529 \r
530         if(cmpofs>0)\r
531         {\r
532                 val_a = substring(expr,0,cmpofs);\r
533                 val_b = substring(expr,cmpofs+1,strlen(expr));\r
534 \r
535                 if(bot_cmd_eval(val_a)<bot_cmd_eval(val_b))\r
536                         self.bot_cmd_condition_status |= CMD_CONDITION_TRUE;\r
537                 else\r
538                         self.bot_cmd_condition_status |= CMD_CONDITION_FALSE;\r
539 \r
540                 return CMD_STATUS_FINISHED;\r
541         }\r
542 \r
543         if(bot_cmd_eval(expr))\r
544                 self.bot_cmd_condition_status |= CMD_CONDITION_TRUE;\r
545         else\r
546                 self.bot_cmd_condition_status |= CMD_CONDITION_FALSE;\r
547 \r
548         return CMD_STATUS_FINISHED;\r
549 }\r
550 \r
551 float bot_cmd_else()\r
552 {\r
553         self.bot_cmd_condition_status &~= CMD_CONDITION_TRUE_BLOCK;\r
554         self.bot_cmd_condition_status |= CMD_CONDITION_FALSE_BLOCK;\r
555         return CMD_STATUS_FINISHED;\r
556 }\r
557 \r
558 float bot_cmd_fi()\r
559 {\r
560         self.bot_cmd_condition_status = CMD_CONDITION_NONE;\r
561         return CMD_STATUS_FINISHED;\r
562 }\r
563 \r
564 float bot_cmd_resetaim()\r
565 {\r
566         self.v_angle = '0 0 0';\r
567         return CMD_STATUS_FINISHED;\r
568 }\r
569 \r
570 .float bot_cmd_aim_begintime;\r
571 .float bot_cmd_aim_endtime;\r
572 .vector bot_cmd_aim_begin;\r
573 .vector bot_cmd_aim_end;\r
574 \r
575 float bot_cmd_aim()\r
576 {\r
577         // Current direction\r
578         if(self.bot_cmd_aim_endtime)\r
579         {\r
580                 local float progress;\r
581 \r
582                 progress = min(1 - (self.bot_cmd_aim_endtime - time) / (self.bot_cmd_aim_endtime - self.bot_cmd_aim_begintime),1);\r
583                 self.v_angle = self.bot_cmd_aim_begin + ((self.bot_cmd_aim_end - self.bot_cmd_aim_begin) * progress);\r
584 \r
585                 if(time>=self.bot_cmd_aim_endtime)\r
586                 {\r
587                         self.bot_cmd_aim_endtime = 0;\r
588                         return CMD_STATUS_FINISHED;\r
589                 }\r
590                 else\r
591                         return CMD_STATUS_EXECUTING;\r
592         }\r
593 \r
594         // New aiming direction\r
595         local string parms;\r
596         local float tokens, step;\r
597 \r
598         parms = bot_cmd.bot_cmd_parm_string;\r
599 \r
600         tokens = tokenizebyseparator(parms, " ");\r
601 \r
602         if(tokens==2)\r
603         {\r
604                 self.v_angle_x -= stof(argv(1));\r
605                 self.v_angle_y += stof(argv(0));\r
606                 return CMD_STATUS_FINISHED;\r
607         }\r
608 \r
609         if(tokens<1||tokens>3)\r
610                 return CMD_STATUS_ERROR;\r
611 \r
612         step = stof(argv(2));\r
613 \r
614         self.bot_cmd_aim_begin = self.v_angle;\r
615 \r
616         self.bot_cmd_aim_end_x = self.v_angle_x - stof(argv(1));\r
617         self.bot_cmd_aim_end_y = self.v_angle_y + stof(argv(0));\r
618         self.bot_cmd_aim_end_z = 0;\r
619 \r
620         self.bot_cmd_aim_begintime = time;\r
621         self.bot_cmd_aim_endtime = time + step;\r
622 \r
623         return CMD_STATUS_EXECUTING;\r
624 }\r
625 \r
626 .float bot_cmd_keys;\r
627 \r
628 #define BOT_CMD_KEY_NONE        0\r
629 #define BOT_CMD_KEY_FORWARD     1\r
630 #define BOT_CMD_KEY_BACKWARD    2\r
631 #define BOT_CMD_KEY_RIGHT       4\r
632 #define BOT_CMD_KEY_LEFT        8\r
633 #define BOT_CMD_KEY_JUMP        16\r
634 #define BOT_CMD_KEY_ATTACK1     32\r
635 #define BOT_CMD_KEY_ATTACK2     64\r
636 #define BOT_CMD_KEY_USE         128\r
637 #define BOT_CMD_KEY_HOOK        256\r
638 #define BOT_CMD_KEY_CROUCH      512\r
639 \r
640 float bot_presskeys()\r
641 {\r
642         self.movement = '0 0 0';\r
643 \r
644         if(self.bot_cmd_keys == BOT_CMD_KEY_NONE)\r
645                 return FALSE;\r
646 \r
647         if(self.bot_cmd_keys & BOT_CMD_KEY_FORWARD)\r
648                 self.movement_x = cvar("sv_maxspeed");\r
649         else if(self.bot_cmd_keys & BOT_CMD_KEY_BACKWARD)\r
650                 self.movement_x = -cvar("sv_maxspeed");\r
651 \r
652         if(self.bot_cmd_keys & BOT_CMD_KEY_RIGHT)\r
653                 self.movement_y = cvar("sv_maxspeed");\r
654         else if(self.bot_cmd_keys & BOT_CMD_KEY_LEFT)\r
655                 self.movement_y = -cvar("sv_maxspeed");\r
656 \r
657         if(self.bot_cmd_keys & BOT_CMD_KEY_JUMP)\r
658                 self.BUTTON_JUMP = TRUE;\r
659 \r
660         if(self.bot_cmd_keys & BOT_CMD_KEY_CROUCH)\r
661                 self.BUTTON_CROUCH = TRUE;\r
662 \r
663         if(self.bot_cmd_keys & BOT_CMD_KEY_ATTACK1)\r
664                 self.BUTTON_ATCK = TRUE;\r
665 \r
666         if(self.bot_cmd_keys & BOT_CMD_KEY_ATTACK2)\r
667                 self.BUTTON_ATCK2 = TRUE;\r
668 \r
669         if(self.bot_cmd_keys & BOT_CMD_KEY_USE)\r
670                 self.BUTTON_USE = TRUE;\r
671 \r
672         return TRUE;\r
673 }\r
674 \r
675 \r
676 float bot_cmd_keypress_handler(string key, float enabled)\r
677 {\r
678         switch(key)\r
679         {\r
680                 case "all":\r
681                         if(enabled)\r
682                                 self.bot_cmd_keys = power2of(20) - 1; // >:)\r
683                         else\r
684                                 self.bot_cmd_keys = BOT_CMD_KEY_NONE;\r
685                 case "forward":\r
686                         if(enabled)\r
687                         {\r
688                                 self.bot_cmd_keys |= BOT_CMD_KEY_FORWARD;\r
689                                 self.bot_cmd_keys &~= BOT_CMD_KEY_BACKWARD;\r
690                         }\r
691                         else\r
692                                 self.bot_cmd_keys &~= BOT_CMD_KEY_FORWARD;\r
693                         break;\r
694                 case "backward":\r
695                         if(enabled)\r
696                         {\r
697                                 self.bot_cmd_keys |= BOT_CMD_KEY_BACKWARD;\r
698                                 self.bot_cmd_keys &~= BOT_CMD_KEY_FORWARD;\r
699                         }\r
700                         else\r
701                                 self.bot_cmd_keys &~= BOT_CMD_KEY_BACKWARD;\r
702                         break;\r
703                 case "left":\r
704                         if(enabled)\r
705                         {\r
706                                 self.bot_cmd_keys |= BOT_CMD_KEY_LEFT;\r
707                                 self.bot_cmd_keys &~= BOT_CMD_KEY_RIGHT;\r
708                         }\r
709                         else\r
710                                 self.bot_cmd_keys &~= BOT_CMD_KEY_LEFT;\r
711                         break;\r
712                 case "right":\r
713                         if(enabled)\r
714                         {\r
715                                 self.bot_cmd_keys |= BOT_CMD_KEY_RIGHT;\r
716                                 self.bot_cmd_keys &~= BOT_CMD_KEY_LEFT;\r
717                         }\r
718                         else\r
719                                 self.bot_cmd_keys &~= BOT_CMD_KEY_RIGHT;\r
720                         break;\r
721                 case "jump":\r
722                         if(enabled)\r
723                                 self.bot_cmd_keys |= BOT_CMD_KEY_JUMP;\r
724                         else\r
725                                 self.bot_cmd_keys &~= BOT_CMD_KEY_JUMP;\r
726                         break;\r
727                 case "crouch":\r
728                         if(enabled)\r
729                                 self.bot_cmd_keys |= BOT_CMD_KEY_CROUCH;\r
730                         else\r
731                                 self.bot_cmd_keys &~= BOT_CMD_KEY_CROUCH;\r
732                         break;\r
733                 case "attack1":\r
734                         if(enabled)\r
735                                 self.bot_cmd_keys |= BOT_CMD_KEY_ATTACK1;\r
736                         else\r
737                                 self.bot_cmd_keys &~= BOT_CMD_KEY_ATTACK1;\r
738                         break;\r
739                 case "attack2":\r
740                         if(enabled)\r
741                                 self.bot_cmd_keys |= BOT_CMD_KEY_ATTACK2;\r
742                         else\r
743                                 self.bot_cmd_keys &~= BOT_CMD_KEY_ATTACK2;\r
744                         break;\r
745                 case "use":\r
746                         if(enabled)\r
747                                 self.bot_cmd_keys |= BOT_CMD_KEY_USE;\r
748                         else\r
749                                 self.bot_cmd_keys &~= BOT_CMD_KEY_USE;\r
750                         break;\r
751                 default:\r
752                         break;\r
753         }\r
754 \r
755         return CMD_STATUS_FINISHED;\r
756 }\r
757 \r
758 float bot_cmd_presskey()\r
759 {\r
760         local string key;\r
761 \r
762         key = bot_cmd.bot_cmd_parm_string;\r
763 \r
764         bot_cmd_keypress_handler(key,TRUE);\r
765 \r
766         return CMD_STATUS_FINISHED;\r
767 }\r
768 \r
769 float bot_cmd_releasekey()\r
770 {\r
771         local string key;\r
772 \r
773         key = bot_cmd.bot_cmd_parm_string;\r
774 \r
775         return bot_cmd_keypress_handler(key,FALSE);\r
776 }\r
777 \r
778 float bot_cmd_pause()\r
779 {\r
780         self.button1        = 0;\r
781         self.button8        = 0;\r
782         self.BUTTON_USE     = 0;\r
783         self.BUTTON_ATCK    = 0;\r
784         self.BUTTON_JUMP    = 0;\r
785         self.BUTTON_HOOK    = 0;\r
786         self.BUTTON_CHAT    = 0;\r
787         self.BUTTON_ATCK2   = 0;\r
788         self.BUTTON_CROUCH  = 0;\r
789 \r
790         self.movement = '0 0 0';\r
791         self.bot_cmd_keys = BOT_CMD_KEY_NONE;\r
792 \r
793         self.bot_exec_status = self.bot_exec_status | BOT_EXEC_STATUS_PAUSED;\r
794         return CMD_STATUS_FINISHED;\r
795 }\r
796 \r
797 void bot_command_executed(float rm)\r
798 {\r
799         entity cmd;\r
800 \r
801         cmd = bot_cmd;\r
802 \r
803         self.bot_cmd_execution_index++;\r
804 \r
805         if(rm)\r
806         {\r
807                 if(bot_cmd_parm_type[cmd.bot_cmd_type]==BOT_CMD_PARAMETER_STRING)\r
808                 {\r
809                         strunzone(cmd.bot_cmd_parm_string);\r
810                 }\r
811                 remove(cmd);\r
812                 return;\r
813         }\r
814 \r
815         cmd.bot_cmd_execution_counter++;\r
816 }\r
817 \r
818 void bot_setcurrentcommand()\r
819 {\r
820         entity cmd;\r
821 \r
822         bot_cmd = world;\r
823 \r
824         if(self.bot_cmd_execution_index==0)\r
825                 self.bot_cmd_execution_index=1;\r
826 \r
827         for (cmd = findchainfloat(bot_cmd_index, self.bot_cmd_execution_index); cmd; cmd = cmd.chain)\r
828         {\r
829                 if(cmd.owner==self)\r
830                 {\r
831                         bot_cmd = cmd;\r
832                         return;\r
833                 }\r
834         }\r
835 }\r
836 \r
837 // This function should be (the only) called directly from the bot ai loop\r
838 // It maps commands to functions and deal with complex interactions between commands and execution states\r
839 // NOTE: Of course you need to include your commands here too :)\r
840 float bot_execute_commands()\r
841 {\r
842         local float status, index, ispressingkey;\r
843         local entity cmd;\r
844 \r
845         // Find command\r
846         bot_setcurrentcommand();\r
847 \r
848         // Keep pressing keys raised by the "presskey" command\r
849         ispressingkey = bot_presskeys();\r
850 \r
851         if(bot_cmd==world)\r
852                 return ispressingkey;\r
853 \r
854         // Ignore all commands except continue when the bot is paused\r
855         if(self.bot_exec_status & BOT_EXEC_STATUS_PAUSED)\r
856         if(bot_cmd.bot_cmd_type!=BOT_CMD_CONTINUE)\r
857         {\r
858                 if(bot_cmd.bot_cmd_type!=BOT_CMD_NULL)\r
859                 {\r
860                         bot_command_executed(TRUE);\r
861                         print( "WARNING: Commands are ignored while the bot is paused. Use the command 'continue' instead.\n");\r
862                 }\r
863                 return TRUE;\r
864         }\r
865 \r
866         // Handle conditions\r
867         if not(bot_cmd.bot_cmd_type==BOT_CMD_FI||bot_cmd.bot_cmd_type==BOT_CMD_ELSE)\r
868         if(self.bot_cmd_condition_status & CMD_CONDITION_TRUE && self.bot_cmd_condition_status & CMD_CONDITION_FALSE_BLOCK)\r
869         {\r
870                 bot_command_executed(TRUE);\r
871                 return TRUE;\r
872         }\r
873         else if(self.bot_cmd_condition_status & CMD_CONDITION_FALSE && self.bot_cmd_condition_status & CMD_CONDITION_TRUE_BLOCK)\r
874         {\r
875                 bot_command_executed(TRUE);\r
876                 return TRUE;\r
877         }\r
878 \r
879         // Map commands to functions\r
880         switch(bot_cmd.bot_cmd_type)\r
881         {\r
882                 case BOT_CMD_NULL:\r
883                         return ispressingkey;\r
884                         break;\r
885                 case BOT_CMD_PAUSE:\r
886                         status = bot_cmd_pause();\r
887                         break;\r
888                 case BOT_CMD_CONTINUE:\r
889                         status = bot_cmd_continue();\r
890                         break;\r
891                 case BOT_CMD_WAIT:\r
892                         status = bot_cmd_wait();\r
893                         break;\r
894                 case BOT_CMD_TURN:\r
895                         status = bot_cmd_turn();\r
896                         break;\r
897                 case BOT_CMD_MOVETO:\r
898                 //      status = bot.bot_cmd_moveto();\r
899                         status = CMD_STATUS_FINISHED;\r
900                         break;\r
901                 case BOT_CMD_RESETGOAL:\r
902                 //      status = bot.cmd_resetgoal();\r
903                         status = CMD_STATUS_FINISHED;\r
904                         break;\r
905                 case BOT_CMD_CC:\r
906                         status = bot_cmd_cc();\r
907                         break;\r
908                 case BOT_CMD_IF:\r
909                         status = bot_cmd_if();\r
910                         break;\r
911                 case BOT_CMD_ELSE:\r
912                         status = bot_cmd_else();\r
913                         break;\r
914                 case BOT_CMD_FI:\r
915                         status = bot_cmd_fi();\r
916                         break;\r
917                 case BOT_CMD_RESETAIM:\r
918                         status = bot_cmd_resetaim();\r
919                         break;\r
920                 case BOT_CMD_AIM:\r
921                         status = bot_cmd_aim();\r
922                         break;\r
923                 case BOT_CMD_PRESSKEY:\r
924                         status = bot_cmd_presskey();\r
925                         break;\r
926                 case BOT_CMD_RELEASEKEY:\r
927                         status = bot_cmd_releasekey();\r
928                         break;\r
929                 case BOT_CMD_SELECTWEAPON:\r
930                         status = bot_cmd_select_weapon();\r
931                         break;\r
932                 case BOT_CMD_IMPULSE:\r
933                         status = bot_cmd_impulse();\r
934                         break;\r
935                 default:\r
936                         print(strcat("ERROR: Invalid command on queue with id '",ftos(bot_cmd.bot_cmd_type),"'\n"));\r
937                         return FALSE;\r
938         }\r
939 \r
940         if (status==CMD_STATUS_ERROR)\r
941                 print(strcat("ERROR: The command '",bot_cmd_string[bot_cmd.bot_cmd_type],"' returned an error status\n"));\r
942 \r
943         // Move execution pointer\r
944         if not(status==CMD_STATUS_EXECUTING)\r
945         {\r
946                 if(cvar("g_debug_bot_commands"))\r
947                 {\r
948                         local string parms;\r
949 \r
950                         switch(bot_cmd_parm_type[bot_cmd.bot_cmd_type])\r
951                         {\r
952                                 case BOT_CMD_PARAMETER_FLOAT:\r
953                                         parms = ftos(bot_cmd.bot_cmd_parm_float);\r
954                                         break;\r
955                                 case BOT_CMD_PARAMETER_STRING:\r
956                                         parms = bot_cmd.bot_cmd_parm_string;\r
957                                         break;\r
958                                 case BOT_CMD_PARAMETER_VECTOR:\r
959                                         parms = vtos(bot_cmd.bot_cmd_parm_vector);\r
960                                         break;\r
961                                 default:\r
962                                         parms = "";\r
963                                         break;\r
964                         }\r
965                         clientcommand(self,strcat("say ^7", bot_cmd_string[bot_cmd.bot_cmd_type]," ",parms,"\n"));\r
966                 }\r
967 \r
968                 bot_command_executed(TRUE);\r
969         }\r
970 \r
971         return TRUE;\r
972 }\r