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