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