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