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