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