]> icculus.org git repositories - divverent/nexuiz.git/blob - data/qcsrc/server/bots_scripting.qc
remove debug code from barrier (making bots yellow while in one)
[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 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_barrier != 1)
516                                 return CMD_STATUS_EXECUTING; // not all are at the barrier yet
517                 }
518
519                 // all bots hit the barrier!
520                 FOR_EACH_CLIENT(cl) if(cl.isbot)
521                 {
522                         cl.bot_barrier = 2; // acknowledge barrier
523                 }
524
525                 bot_barriertime = time;
526         }
527
528         // if we get here, the barrier is finished
529         // so end it...
530         self.bot_barrier = 0;
531         //self.colormod = '0 0 0';
532
533         return CMD_STATUS_FINISHED;
534 }
535
536 float bot_cmd_turn()
537 {
538         self.v_angle_y = self.v_angle_y + bot_cmd.bot_cmd_parm_float;
539         self.v_angle_y = self.v_angle_y - floor(self.v_angle_y / 360) * 360;
540         return CMD_STATUS_FINISHED;
541 }
542
543 float bot_cmd_select_weapon()
544 {
545         local float id;
546
547         id = bot_cmd.bot_cmd_parm_float;
548
549         if(id < WEP_FIRST || id > WEP_LAST)
550                 return CMD_STATUS_ERROR;
551
552         if(client_hasweapon(self, id, TRUE, FALSE))
553                 self.switchweapon = id;
554         else
555                 return CMD_STATUS_ERROR;
556
557         return CMD_STATUS_FINISHED;
558 }
559
560 .float bot_cmd_condition_status;
561
562 #define CMD_CONDITION_NONE              0
563 #define CMD_CONDITION_TRUE              1
564 #define CMD_CONDITION_FALSE             2
565 #define CMD_CONDITION_TRUE_BLOCK        4
566 #define CMD_CONDITION_FALSE_BLOCK       8
567
568 float bot_cmd_eval(string expr)
569 {
570         // Search for numbers
571         if(strstrofs("0123456789", substring(expr, 0, 1), 0) >= 0)
572         {
573                 return stof(expr);
574         }
575
576         // Search for cvars
577         if(substring(expr, 0, 5)=="cvar.")
578         {
579                 return cvar(substring(expr, 5, strlen(expr)));
580         }
581
582         // Search for fields
583         switch(expr)
584         {
585                 case "health":
586                         return self.health;
587                 case "speed":
588                         return vlen(self.velocity);
589                 case "flagcarrier":
590                         return ((self.flagcarried!=world));
591         }
592
593         print(strcat("ERROR: Unable to convert the expression '",expr,"' into a numeric value\n"));
594         return 0;
595 }
596
597 float bot_cmd_if()
598 {
599         local string expr, val_a, val_b;
600         local float cmpofs;
601
602         if(self.bot_cmd_condition_status != CMD_CONDITION_NONE)
603         {
604                 // Only one "if" block is allowed at time
605                 print("ERROR: Only one conditional block can be processed at time");
606                 bot_clearqueue(self);
607                 return CMD_STATUS_ERROR;
608         }
609
610         self.bot_cmd_condition_status |= CMD_CONDITION_TRUE_BLOCK;
611
612         // search for operators
613         expr = bot_cmd.bot_cmd_parm_string;
614
615         cmpofs = strstrofs(expr,"=",0);
616
617         if(cmpofs>0)
618         {
619                 val_a = substring(expr,0,cmpofs);
620                 val_b = substring(expr,cmpofs+1,strlen(expr));
621
622                 if(bot_cmd_eval(val_a)==bot_cmd_eval(val_b))
623                         self.bot_cmd_condition_status |= CMD_CONDITION_TRUE;
624                 else
625                         self.bot_cmd_condition_status |= CMD_CONDITION_FALSE;
626
627                 return CMD_STATUS_FINISHED;
628         }
629
630         cmpofs = strstrofs(expr,">",0);
631
632         if(cmpofs>0)
633         {
634                 val_a = substring(expr,0,cmpofs);
635                 val_b = substring(expr,cmpofs+1,strlen(expr));
636
637                 if(bot_cmd_eval(val_a)>bot_cmd_eval(val_b))
638                         self.bot_cmd_condition_status |= CMD_CONDITION_TRUE;
639                 else
640                         self.bot_cmd_condition_status |= CMD_CONDITION_FALSE;
641
642                 return CMD_STATUS_FINISHED;
643         }
644
645         cmpofs = strstrofs(expr,"<",0);
646
647         if(cmpofs>0)
648         {
649                 val_a = substring(expr,0,cmpofs);
650                 val_b = substring(expr,cmpofs+1,strlen(expr));
651
652                 if(bot_cmd_eval(val_a)<bot_cmd_eval(val_b))
653                         self.bot_cmd_condition_status |= CMD_CONDITION_TRUE;
654                 else
655                         self.bot_cmd_condition_status |= CMD_CONDITION_FALSE;
656
657                 return CMD_STATUS_FINISHED;
658         }
659
660         if(bot_cmd_eval(expr))
661                 self.bot_cmd_condition_status |= CMD_CONDITION_TRUE;
662         else
663                 self.bot_cmd_condition_status |= CMD_CONDITION_FALSE;
664
665         return CMD_STATUS_FINISHED;
666 }
667
668 float bot_cmd_else()
669 {
670         self.bot_cmd_condition_status &~= CMD_CONDITION_TRUE_BLOCK;
671         self.bot_cmd_condition_status |= CMD_CONDITION_FALSE_BLOCK;
672         return CMD_STATUS_FINISHED;
673 }
674
675 float bot_cmd_fi()
676 {
677         self.bot_cmd_condition_status = CMD_CONDITION_NONE;
678         return CMD_STATUS_FINISHED;
679 }
680
681 float bot_cmd_resetaim()
682 {
683         self.v_angle = '0 0 0';
684         return CMD_STATUS_FINISHED;
685 }
686
687 .float bot_cmd_aim_begintime;
688 .float bot_cmd_aim_endtime;
689 .vector bot_cmd_aim_begin;
690 .vector bot_cmd_aim_end;
691
692 float bot_cmd_aim()
693 {
694         // Current direction
695         if(self.bot_cmd_aim_endtime)
696         {
697                 local float progress;
698
699                 progress = min(1 - (self.bot_cmd_aim_endtime - time) / (self.bot_cmd_aim_endtime - self.bot_cmd_aim_begintime),1);
700                 self.v_angle = self.bot_cmd_aim_begin + ((self.bot_cmd_aim_end - self.bot_cmd_aim_begin) * progress);
701
702                 if(time>=self.bot_cmd_aim_endtime)
703                 {
704                         self.bot_cmd_aim_endtime = 0;
705                         return CMD_STATUS_FINISHED;
706                 }
707                 else
708                         return CMD_STATUS_EXECUTING;
709         }
710
711         // New aiming direction
712         local string parms;
713         local float tokens, step;
714
715         parms = bot_cmd.bot_cmd_parm_string;
716
717         tokens = tokenizebyseparator(parms, " ");
718
719         if(tokens==2)
720         {
721                 self.v_angle_x -= stof(argv(1));
722                 self.v_angle_y += stof(argv(0));
723                 return CMD_STATUS_FINISHED;
724         }
725
726         if(tokens<2||tokens>3)
727                 return CMD_STATUS_ERROR;
728
729         step = stof(argv(2));
730
731         self.bot_cmd_aim_begin = self.v_angle;
732
733         self.bot_cmd_aim_end_x = self.v_angle_x - stof(argv(1));
734         self.bot_cmd_aim_end_y = self.v_angle_y + stof(argv(0));
735         self.bot_cmd_aim_end_z = 0;
736
737         self.bot_cmd_aim_begintime = time;
738         self.bot_cmd_aim_endtime = time + step;
739
740         return CMD_STATUS_EXECUTING;
741 }
742
743 float bot_cmd_aimtarget()
744 {
745         if(self.bot_cmd_aim_endtime)
746         {
747                 return bot_cmd_aim();
748         }
749
750         local entity e;
751         local string parms;
752         local vector v;
753         local float tokens, step;
754
755         parms = bot_cmd.bot_cmd_parm_string;
756
757         tokens = tokenizebyseparator(parms, " ");
758
759         e = find(world, targetname, argv(0));
760         if(!e)
761                 return CMD_STATUS_ERROR;
762
763         v = e.origin + (e.mins + e.maxs) * 0.5;
764
765         if(tokens==1)
766         {
767                 self.v_angle = vectoangles(v - (self.origin + self.view_ofs));
768                 self.v_angle_x = -self.v_angle_x;
769                 return CMD_STATUS_FINISHED;
770         }
771
772         if(tokens<1||tokens>2)
773                 return CMD_STATUS_ERROR;
774
775         step = stof(argv(1));
776
777         self.bot_cmd_aim_begin = self.v_angle;
778         self.bot_cmd_aim_end = vectoangles(v - (self.origin + self.view_ofs));
779         self.bot_cmd_aim_end_x = -self.bot_cmd_aim_end_x;
780
781         self.bot_cmd_aim_begintime = time;
782         self.bot_cmd_aim_endtime = time + step;
783
784         return CMD_STATUS_EXECUTING;
785 }
786
787 .float bot_cmd_keys;
788
789 #define BOT_CMD_KEY_NONE        0
790 #define BOT_CMD_KEY_FORWARD     1
791 #define BOT_CMD_KEY_BACKWARD    2
792 #define BOT_CMD_KEY_RIGHT       4
793 #define BOT_CMD_KEY_LEFT        8
794 #define BOT_CMD_KEY_JUMP        16
795 #define BOT_CMD_KEY_ATTACK1     32
796 #define BOT_CMD_KEY_ATTACK2     64
797 #define BOT_CMD_KEY_USE         128
798 #define BOT_CMD_KEY_HOOK        256
799 #define BOT_CMD_KEY_CROUCH      512
800 #define BOT_CMD_KEY_CHAT        1024
801
802 float bot_presskeys()
803 {
804         self.movement = '0 0 0';
805
806         if(self.bot_cmd_keys == BOT_CMD_KEY_NONE)
807                 return FALSE;
808
809         if(self.bot_cmd_keys & BOT_CMD_KEY_FORWARD)
810                 self.movement_x = cvar("sv_maxspeed");
811         else if(self.bot_cmd_keys & BOT_CMD_KEY_BACKWARD)
812                 self.movement_x = -cvar("sv_maxspeed");
813
814         if(self.bot_cmd_keys & BOT_CMD_KEY_RIGHT)
815                 self.movement_y = cvar("sv_maxspeed");
816         else if(self.bot_cmd_keys & BOT_CMD_KEY_LEFT)
817                 self.movement_y = -cvar("sv_maxspeed");
818
819         if(self.bot_cmd_keys & BOT_CMD_KEY_JUMP)
820                 self.BUTTON_JUMP = TRUE;
821
822         if(self.bot_cmd_keys & BOT_CMD_KEY_CROUCH)
823                 self.BUTTON_CROUCH = TRUE;
824
825         if(self.bot_cmd_keys & BOT_CMD_KEY_ATTACK1)
826                 self.BUTTON_ATCK = TRUE;
827
828         if(self.bot_cmd_keys & BOT_CMD_KEY_ATTACK2)
829                 self.BUTTON_ATCK2 = TRUE;
830
831         if(self.bot_cmd_keys & BOT_CMD_KEY_USE)
832                 self.BUTTON_USE = TRUE;
833
834         if(self.bot_cmd_keys & BOT_CMD_KEY_HOOK)
835                 self.BUTTON_HOOK = TRUE;
836
837         if(self.bot_cmd_keys & BOT_CMD_KEY_CHAT)
838                 self.BUTTON_CHAT = TRUE;
839
840         return TRUE;
841 }
842
843
844 float bot_cmd_keypress_handler(string key, float enabled)
845 {
846         switch(key)
847         {
848                 case "all":
849                         if(enabled)
850                                 self.bot_cmd_keys = power2of(20) - 1; // >:)
851                         else
852                                 self.bot_cmd_keys = BOT_CMD_KEY_NONE;
853                 case "forward":
854                         if(enabled)
855                         {
856                                 self.bot_cmd_keys |= BOT_CMD_KEY_FORWARD;
857                                 self.bot_cmd_keys &~= BOT_CMD_KEY_BACKWARD;
858                         }
859                         else
860                                 self.bot_cmd_keys &~= BOT_CMD_KEY_FORWARD;
861                         break;
862                 case "backward":
863                         if(enabled)
864                         {
865                                 self.bot_cmd_keys |= BOT_CMD_KEY_BACKWARD;
866                                 self.bot_cmd_keys &~= BOT_CMD_KEY_FORWARD;
867                         }
868                         else
869                                 self.bot_cmd_keys &~= BOT_CMD_KEY_BACKWARD;
870                         break;
871                 case "left":
872                         if(enabled)
873                         {
874                                 self.bot_cmd_keys |= BOT_CMD_KEY_LEFT;
875                                 self.bot_cmd_keys &~= BOT_CMD_KEY_RIGHT;
876                         }
877                         else
878                                 self.bot_cmd_keys &~= BOT_CMD_KEY_LEFT;
879                         break;
880                 case "right":
881                         if(enabled)
882                         {
883                                 self.bot_cmd_keys |= BOT_CMD_KEY_RIGHT;
884                                 self.bot_cmd_keys &~= BOT_CMD_KEY_LEFT;
885                         }
886                         else
887                                 self.bot_cmd_keys &~= BOT_CMD_KEY_RIGHT;
888                         break;
889                 case "jump":
890                         if(enabled)
891                                 self.bot_cmd_keys |= BOT_CMD_KEY_JUMP;
892                         else
893                                 self.bot_cmd_keys &~= BOT_CMD_KEY_JUMP;
894                         break;
895                 case "crouch":
896                         if(enabled)
897                                 self.bot_cmd_keys |= BOT_CMD_KEY_CROUCH;
898                         else
899                                 self.bot_cmd_keys &~= BOT_CMD_KEY_CROUCH;
900                         break;
901                 case "attack1":
902                         if(enabled)
903                                 self.bot_cmd_keys |= BOT_CMD_KEY_ATTACK1;
904                         else
905                                 self.bot_cmd_keys &~= BOT_CMD_KEY_ATTACK1;
906                         break;
907                 case "attack2":
908                         if(enabled)
909                                 self.bot_cmd_keys |= BOT_CMD_KEY_ATTACK2;
910                         else
911                                 self.bot_cmd_keys &~= BOT_CMD_KEY_ATTACK2;
912                         break;
913                 case "use":
914                         if(enabled)
915                                 self.bot_cmd_keys |= BOT_CMD_KEY_USE;
916                         else
917                                 self.bot_cmd_keys &~= BOT_CMD_KEY_USE;
918                         break;
919                 case "hook":
920                         if(enabled)
921                                 self.bot_cmd_keys |= BOT_CMD_KEY_HOOK;
922                         else
923                                 self.bot_cmd_keys &~= BOT_CMD_KEY_HOOK;
924                         break;
925                 case "chat":
926                         if(enabled)
927                                 self.bot_cmd_keys |= BOT_CMD_KEY_CHAT;
928                         else
929                                 self.bot_cmd_keys &~= BOT_CMD_KEY_CHAT;
930                         break;
931                 default:
932                         break;
933         }
934
935         return CMD_STATUS_FINISHED;
936 }
937
938 float bot_cmd_presskey()
939 {
940         local string key;
941
942         key = bot_cmd.bot_cmd_parm_string;
943
944         bot_cmd_keypress_handler(key,TRUE);
945
946         return CMD_STATUS_FINISHED;
947 }
948
949 float bot_cmd_releasekey()
950 {
951         local string key;
952
953         key = bot_cmd.bot_cmd_parm_string;
954
955         return bot_cmd_keypress_handler(key,FALSE);
956 }
957
958 float bot_cmd_pause()
959 {
960         self.button1        = 0;
961         self.button8        = 0;
962         self.BUTTON_USE     = 0;
963         self.BUTTON_ATCK    = 0;
964         self.BUTTON_JUMP    = 0;
965         self.BUTTON_HOOK    = 0;
966         self.BUTTON_CHAT    = 0;
967         self.BUTTON_ATCK2   = 0;
968         self.BUTTON_CROUCH  = 0;
969
970         self.movement = '0 0 0';
971         self.bot_cmd_keys = BOT_CMD_KEY_NONE;
972
973         self.bot_exec_status = self.bot_exec_status | BOT_EXEC_STATUS_PAUSED;
974         return CMD_STATUS_FINISHED;
975 }
976
977 float bot_cmd_moveto()
978 {
979         return self.cmd_moveto(bot_cmd.bot_cmd_parm_vector);
980 }
981
982 float bot_cmd_movetotarget()
983 {
984         entity e;
985         e = find(world, targetname, bot_cmd.bot_cmd_parm_string);
986         if(!e)
987                 return CMD_STATUS_ERROR;
988         return self.cmd_moveto(e.origin + (e.mins + e.maxs) * 0.5);
989 }
990
991 float bot_cmd_resetgoal()
992 {
993         return self.cmd_resetgoal();
994 }
995
996
997
998
999
1000
1001
1002
1003
1004 void bot_command_executed(float rm)
1005 {
1006         entity cmd;
1007
1008         cmd = bot_cmd;
1009
1010         if(rm)
1011                 bot_dequeuecommand(self, self.bot_cmd_execution_index);
1012
1013         self.bot_cmd_execution_index++;
1014 }
1015
1016 void bot_setcurrentcommand()
1017 {
1018         bot_cmd = world;
1019
1020         if(self.bot_cmd_execution_index == 0)
1021                 self.bot_cmd_execution_index = 1;
1022         
1023         if(!self.bot_cmd_current)
1024         {
1025                 self.bot_cmd_current = spawn();
1026                 self.bot_cmd_current.classname = "bot_cmd";
1027                 self.bot_cmd_current.is_bot_cmd = 1;
1028                 self.bot_cmd_current.bot_cmd_index = 0;
1029         }
1030
1031         bot_cmd = self.bot_cmd_current;
1032         if(bot_cmd.bot_cmd_index != self.bot_cmd_execution_index)
1033         {
1034                 if(bot_havecommand(self, self.bot_cmd_execution_index))
1035                 {
1036                         string cmdstring;
1037                         cmdstring = bot_readcommand(self, self.bot_cmd_execution_index);
1038                         if(bot_decodecommand(cmdstring))
1039                         {
1040                                 bot_cmd.owner = self;
1041                                 bot_cmd.bot_cmd_index = self.bot_cmd_execution_index;
1042                         }
1043                         else
1044                                 bot_cmd = world;
1045                 }
1046                 else
1047                         bot_cmd = world;
1048         }
1049 }
1050
1051 void bot_resetqueues()
1052 {
1053         entity cl;
1054
1055         FOR_EACH_CLIENT(cl) if(cl.isbot)
1056         {
1057                 bot_clearqueue(cl);
1058                 // also, cancel all barriers
1059                 FOR_EACH_CLIENT(cl) if(cl.isbot)
1060                 {
1061                         cl.bot_barrier = 0;
1062                 }
1063         }
1064
1065         bot_barriertime = time;
1066 }
1067
1068 // This function should be (the only) called directly from the bot ai loop
1069 // It maps commands to functions and deal with complex interactions between commands and execution states
1070 // NOTE: Of course you need to include your commands here too :)
1071 float bot_execute_commands()
1072 {
1073         local float status, ispressingkey;
1074
1075         do
1076         {
1077
1078                 if(self.deadflag!=DEAD_NO)
1079                         return FALSE;
1080
1081                 // Find command
1082                 bot_setcurrentcommand();
1083
1084                 // Keep pressing keys raised by the "presskey" command
1085                 ispressingkey = bot_presskeys();
1086
1087                 if(bot_cmd==world)
1088                         return ispressingkey;
1089
1090                 // Ignore all commands except continue when the bot is paused
1091                 if(self.bot_exec_status & BOT_EXEC_STATUS_PAUSED)
1092                 if(bot_cmd.bot_cmd_type!=BOT_CMD_CONTINUE)
1093                 {
1094                         if(bot_cmd.bot_cmd_type!=BOT_CMD_NULL)
1095                         {
1096                                 bot_command_executed(TRUE);
1097                                 print( "WARNING: Commands are ignored while the bot is paused. Use the command 'continue' instead.\n");
1098                         }
1099                         return TRUE;
1100                 }
1101
1102                 // Handle conditions
1103                 if not(bot_cmd.bot_cmd_type==BOT_CMD_FI||bot_cmd.bot_cmd_type==BOT_CMD_ELSE)
1104                 if(self.bot_cmd_condition_status & CMD_CONDITION_TRUE && self.bot_cmd_condition_status & CMD_CONDITION_FALSE_BLOCK)
1105                 {
1106                         bot_command_executed(TRUE);
1107                         continue;
1108                 }
1109                 else if(self.bot_cmd_condition_status & CMD_CONDITION_FALSE && self.bot_cmd_condition_status & CMD_CONDITION_TRUE_BLOCK)
1110                 {
1111                         bot_command_executed(TRUE);
1112                         continue;
1113                 }
1114
1115                 // Map commands to functions
1116                 switch(bot_cmd.bot_cmd_type)
1117                 {
1118                         case BOT_CMD_NULL:
1119                                 return ispressingkey;
1120                                 break;
1121                         case BOT_CMD_PAUSE:
1122                                 status = bot_cmd_pause();
1123                                 break;
1124                         case BOT_CMD_CONTINUE:
1125                                 status = bot_cmd_continue();
1126                                 break;
1127                         case BOT_CMD_WAIT:
1128                                 status = bot_cmd_wait();
1129                                 break;
1130                         case BOT_CMD_WAIT_UNTIL:
1131                                 status = bot_cmd_wait_until();
1132                                 break;
1133                         case BOT_CMD_TURN:
1134                                 status = bot_cmd_turn();
1135                                 break;
1136                         case BOT_CMD_MOVETO:
1137                                 status = bot_cmd_moveto();
1138                                 break;
1139                         case BOT_CMD_MOVETOTARGET:
1140                                 status = bot_cmd_movetotarget();
1141                                 break;
1142                         case BOT_CMD_RESETGOAL:
1143                                 status = bot_cmd_resetgoal();
1144                                 break;
1145                         case BOT_CMD_CC:
1146                                 status = bot_cmd_cc();
1147                                 break;
1148                         case BOT_CMD_IF:
1149                                 status = bot_cmd_if();
1150                                 break;
1151                         case BOT_CMD_ELSE:
1152                                 status = bot_cmd_else();
1153                                 break;
1154                         case BOT_CMD_FI:
1155                                 status = bot_cmd_fi();
1156                                 break;
1157                         case BOT_CMD_RESETAIM:
1158                                 status = bot_cmd_resetaim();
1159                                 break;
1160                         case BOT_CMD_AIM:
1161                                 status = bot_cmd_aim();
1162                                 break;
1163                         case BOT_CMD_AIMTARGET:
1164                                 status = bot_cmd_aimtarget();
1165                                 break;
1166                         case BOT_CMD_PRESSKEY:
1167                                 status = bot_cmd_presskey();
1168                                 break;
1169                         case BOT_CMD_RELEASEKEY:
1170                                 status = bot_cmd_releasekey();
1171                                 break;
1172                         case BOT_CMD_SELECTWEAPON:
1173                                 status = bot_cmd_select_weapon();
1174                                 break;
1175                         case BOT_CMD_IMPULSE:
1176                                 status = bot_cmd_impulse();
1177                                 break;
1178                         case BOT_CMD_BARRIER:
1179                                 status = bot_cmd_barrier();
1180                                 break;
1181                         case BOT_CMD_CONSOLE:
1182                                 localcmd(strcat(bot_cmd.bot_cmd_parm_string, "\n"));
1183                                 status = CMD_STATUS_FINISHED;
1184                                 break;
1185                         default:
1186                                 print(strcat("ERROR: Invalid command on queue with id '",ftos(bot_cmd.bot_cmd_type),"'\n"));
1187                                 return FALSE;
1188                 }
1189
1190                 if (status==CMD_STATUS_ERROR)
1191                         print(strcat("ERROR: The command '",bot_cmd_string[bot_cmd.bot_cmd_type],"' returned an error status\n"));
1192
1193                 // Move execution pointer
1194                 if(status==CMD_STATUS_EXECUTING)
1195                 {
1196                         return TRUE;
1197                 }
1198                 else
1199                 {
1200                         if(cvar("g_debug_bot_commands"))
1201                         {
1202                                 local string parms;
1203
1204                                 switch(bot_cmd_parm_type[bot_cmd.bot_cmd_type])
1205                                 {
1206                                         case BOT_CMD_PARAMETER_FLOAT:
1207                                                 parms = ftos(bot_cmd.bot_cmd_parm_float);
1208                                                 break;
1209                                         case BOT_CMD_PARAMETER_STRING:
1210                                                 parms = bot_cmd.bot_cmd_parm_string;
1211                                                 break;
1212                                         case BOT_CMD_PARAMETER_VECTOR:
1213                                                 parms = vtos(bot_cmd.bot_cmd_parm_vector);
1214                                                 break;
1215                                         default:
1216                                                 parms = "";
1217                                                 break;
1218                                 }
1219                                 clientcommand(self,strcat("say ^7", bot_cmd_string[bot_cmd.bot_cmd_type]," ",parms,"\n"));
1220                         }
1221
1222                         bot_command_executed(TRUE);
1223                 }
1224
1225         } while(status==CMD_STATUS_FINISHED);
1226
1227         return status;
1228 }