1 .float bot_cmdqueuebuf_allocated;
2 .float bot_cmdqueuebuf;
3 .float bot_cmdqueuebuf_start;
4 .float bot_cmdqueuebuf_end;
6 void bot_clearqueue(entity bot)
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");
15 void bot_queuecommand(entity bot, string cmdstring)
17 if(!bot.bot_cmdqueuebuf_allocated)
19 bot.bot_cmdqueuebuf = buf_create();
20 bot.bot_cmdqueuebuf_start = 0;
21 bot.bot_cmdqueuebuf_end = 0;
22 bot.bot_cmdqueuebuf_allocated = TRUE;
25 bufstr_set(bot.bot_cmdqueuebuf, bot.bot_cmdqueuebuf_end, cmdstring);
26 bot.bot_cmdqueuebuf_end += 1;
29 void bot_dequeuecommand(entity bot, float idx)
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)
44 string bot_readcommand(entity bot, float idx)
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);
55 float bot_havecommand(entity bot, float idx)
57 if(!bot.bot_cmdqueuebuf_allocated)
59 if(idx < bot.bot_cmdqueuebuf_start)
61 if(idx >= bot.bot_cmdqueuebuf_end)
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)
73 if(substring(placename, 0, 1) == "@")
76 placename = substring(placename, 1, -1);
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);
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");
91 e = find(world, targetname, s);
93 print("invalid place ", s, "\n");
94 if(i < MAX_BOT_PLACES)
96 self.(bot_placenames[i]) = strzone(placename);
97 self.(bot_places[i]) = e;
98 self.bot_places_count += 1;
104 e = find(world, targetname, placename);
106 print("invalid place ", s, "\n");
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
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
139 #define BOT_CMD_COUNTER 22 // Update this value if you add/remove a command
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;
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
152 float bot_cmds_initialized;
153 float bot_cmd_parm_type[BOT_CMD_COUNTER];
154 string bot_cmd_string[BOT_CMD_COUNTER];
156 // Bots command queue
157 entity bot_cmd; // global current command
158 .entity bot_cmd_current; // current command of this bot
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
167 float bot_barriertime;
170 .float bot_cmd_execution_index; // Position in the queue of the command to be executed
172 // Initialize global commands list
173 // NOTE: New commands should be initialized here
174 void bot_commands_init()
176 bot_cmd_string[BOT_CMD_NULL] = "";
177 bot_cmd_parm_type[BOT_CMD_NULL] = BOT_CMD_PARAMETER_NONE;
179 bot_cmd_string[BOT_CMD_PAUSE] = "pause";
180 bot_cmd_parm_type[BOT_CMD_PAUSE] = BOT_CMD_PARAMETER_NONE;
182 bot_cmd_string[BOT_CMD_CONTINUE] = "continue";
183 bot_cmd_parm_type[BOT_CMD_CONTINUE] = BOT_CMD_PARAMETER_NONE;
185 bot_cmd_string[BOT_CMD_WAIT] = "wait";
186 bot_cmd_parm_type[BOT_CMD_WAIT] = BOT_CMD_PARAMETER_FLOAT;
188 bot_cmd_string[BOT_CMD_TURN] = "turn";
189 bot_cmd_parm_type[BOT_CMD_TURN] = BOT_CMD_PARAMETER_FLOAT;
191 bot_cmd_string[BOT_CMD_MOVETO] = "moveto";
192 bot_cmd_parm_type[BOT_CMD_MOVETO] = BOT_CMD_PARAMETER_VECTOR;
194 bot_cmd_string[BOT_CMD_MOVETOTARGET] = "movetotarget";
195 bot_cmd_parm_type[BOT_CMD_MOVETOTARGET] = BOT_CMD_PARAMETER_STRING;
197 bot_cmd_string[BOT_CMD_RESETGOAL] = "resetgoal";
198 bot_cmd_parm_type[BOT_CMD_RESETGOAL] = BOT_CMD_PARAMETER_NONE;
200 bot_cmd_string[BOT_CMD_CC] = "cc";
201 bot_cmd_parm_type[BOT_CMD_CC] = BOT_CMD_PARAMETER_STRING;
203 bot_cmd_string[BOT_CMD_IF] = "if";
204 bot_cmd_parm_type[BOT_CMD_IF] = BOT_CMD_PARAMETER_STRING;
206 bot_cmd_string[BOT_CMD_ELSE] = "else";
207 bot_cmd_parm_type[BOT_CMD_ELSE] = BOT_CMD_PARAMETER_NONE;
209 bot_cmd_string[BOT_CMD_FI] = "fi";
210 bot_cmd_parm_type[BOT_CMD_FI] = BOT_CMD_PARAMETER_NONE;
212 bot_cmd_string[BOT_CMD_RESETAIM] = "resetaim";
213 bot_cmd_parm_type[BOT_CMD_RESETAIM] = BOT_CMD_PARAMETER_NONE;
215 bot_cmd_string[BOT_CMD_AIM] = "aim";
216 bot_cmd_parm_type[BOT_CMD_AIM] = BOT_CMD_PARAMETER_STRING;
218 bot_cmd_string[BOT_CMD_AIMTARGET] = "aimtarget";
219 bot_cmd_parm_type[BOT_CMD_AIMTARGET] = BOT_CMD_PARAMETER_STRING;
221 bot_cmd_string[BOT_CMD_PRESSKEY] = "presskey";
222 bot_cmd_parm_type[BOT_CMD_PRESSKEY] = BOT_CMD_PARAMETER_STRING;
224 bot_cmd_string[BOT_CMD_RELEASEKEY] = "releasekey";
225 bot_cmd_parm_type[BOT_CMD_RELEASEKEY] = BOT_CMD_PARAMETER_STRING;
227 bot_cmd_string[BOT_CMD_SELECTWEAPON] = "selectweapon";
228 bot_cmd_parm_type[BOT_CMD_SELECTWEAPON] = BOT_CMD_PARAMETER_FLOAT;
230 bot_cmd_string[BOT_CMD_IMPULSE] = "impulse";
231 bot_cmd_parm_type[BOT_CMD_IMPULSE] = BOT_CMD_PARAMETER_FLOAT;
233 bot_cmd_string[BOT_CMD_WAIT_UNTIL] = "wait_until";
234 bot_cmd_parm_type[BOT_CMD_WAIT_UNTIL] = BOT_CMD_PARAMETER_FLOAT;
236 bot_cmd_string[BOT_CMD_BARRIER] = "barrier";
237 bot_cmd_parm_type[BOT_CMD_BARRIER] = BOT_CMD_PARAMETER_NONE;
239 bot_cmd_string[BOT_CMD_CONSOLE] = "console";
240 bot_cmd_parm_type[BOT_CMD_CONSOLE] = BOT_CMD_PARAMETER_STRING;
242 bot_cmds_initialized = TRUE;
245 // Returns first bot with matching name
246 entity find_bot_by_name(string name)
250 bot = findchainflags(flags, FL_CLIENT);
253 if(clienttype(bot) == CLIENTTYPE_BOT)
254 if(bot.netname==name)
263 // Returns a bot by number on list
264 entity find_bot_by_number(float number)
272 bot = findchainflags(flags, FL_CLIENT);
275 if(clienttype(bot) == CLIENTTYPE_BOT)
286 float bot_decodecommand(string cmdstring)
288 local float cmd_parm_type, i;
292 sp = strstrofs(cmdstring, " ", 0);
299 parm = substring(cmdstring, sp + 1, -1);
300 cmdstring = substring(cmdstring, 0, sp);
303 if(!bot_cmds_initialized)
306 for(i=1;i<BOT_CMD_COUNTER;++i)
308 if(bot_cmd_string[i]!=cmdstring)
311 cmd_parm_type = bot_cmd_parm_type[i];
313 if(cmd_parm_type!=BOT_CMD_PARAMETER_NONE&&parm=="")
315 print("ERROR: A parameter is required for this command\n");
319 // Load command into queue
320 bot_cmd.bot_cmd_type = i;
323 switch(cmd_parm_type)
325 case BOT_CMD_PARAMETER_FLOAT:
326 bot_cmd.bot_cmd_parm_float = stof(parm);
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);
333 case BOT_CMD_PARAMETER_VECTOR:
334 bot_cmd.bot_cmd_parm_vector = stov(parm);
341 print("ERROR: No such command '", cmdstring, "'\n");
345 void bot_cmdhelp(string scmd)
347 local float i, ntype;
350 if(!bot_cmds_initialized)
353 for(i=1;i<BOT_CMD_COUNTER;++i)
355 if(bot_cmd_string[i]!=scmd)
358 ntype = bot_cmd_parm_type[i];
362 case BOT_CMD_PARAMETER_FLOAT:
363 stype = "float number";
365 case BOT_CMD_PARAMETER_STRING:
368 case BOT_CMD_PARAMETER_VECTOR:
376 print(strcat("Command: ",bot_cmd_string[i],"\nParameter: <",stype,"> \n"));
378 print("Description: ");
382 print("Stops the bot completely. Any command other than 'continue' will be ignored.");
384 case BOT_CMD_CONTINUE:
385 print("Disable paused status");
388 print("Pause command parsing and bot ai for N seconds. Pressed key will remain pressed");
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");
393 case BOT_CMD_BARRIER:
394 print("Waits till all bots that have a command queue reach this command. Pressed key will remain pressed");
397 print("Look to the right or left N degrees. For turning to the left use positive numbers.");
400 print("Walk to an specific coordinate on the map. Usage: moveto \"x y z\"");
402 case BOT_CMD_MOVETOTARGET:
403 print("Walk to the specific target on the map");
405 case BOT_CMD_RESETGOAL:
406 print("Resets the goal stack");
409 print("Execute client command. Examples: cc \"say something\"; cc god; cc \"name newnickname\"; cc kill;");
412 print("Perform simple conditional execution.\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;");
426 case BOT_CMD_RESETAIM:
427 print("Points the aim to the coordinates x,y 0,0");
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");
435 case BOT_CMD_AIMTARGET:
436 print("Points the aim to given target");
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");
443 case BOT_CMD_RELEASEKEY:
444 print("Release previoulsy used keys. Use the parameter \"all\" to release all keys");
447 print("This command has no description yet.");
454 void bot_list_commands()
459 if(!bot_cmds_initialized)
462 print("List of all available commands:\n");
463 print(" Command\t\t\t\tParameter Type\n");
465 for(i=1;i<BOT_CMD_COUNTER;++i)
467 switch(bot_cmd_parm_type[i])
469 case BOT_CMD_PARAMETER_FLOAT:
470 ptype = "float number";
472 case BOT_CMD_PARAMETER_STRING:
475 case BOT_CMD_PARAMETER_VECTOR:
482 print(strcat(" ",bot_cmd_string[i],"\t\t\t\t<",ptype,"> \n"));
487 .float bot_exec_status;
489 #define BOT_EXEC_STATUS_IDLE 0
490 #define BOT_EXEC_STATUS_PAUSED 1
491 #define BOT_EXEC_STATUS_WAITING 2
493 #define CMD_STATUS_EXECUTING 0
494 #define CMD_STATUS_FINISHED 1
495 #define CMD_STATUS_ERROR 2
497 void SV_ParseClientCommand(string s);
500 SV_ParseClientCommand(bot_cmd.bot_cmd_parm_string);
501 return CMD_STATUS_FINISHED;
504 float bot_cmd_impulse()
506 self.impulse = bot_cmd.bot_cmd_parm_float;
507 return CMD_STATUS_FINISHED;
510 float bot_cmd_continue()
512 self.bot_exec_status &~= BOT_EXEC_STATUS_PAUSED;
513 return CMD_STATUS_FINISHED;
516 .float bot_cmd_wait_time;
519 if(self.bot_exec_status & BOT_EXEC_STATUS_WAITING)
521 if(time>=self.bot_cmd_wait_time)
523 self.bot_exec_status &~= BOT_EXEC_STATUS_WAITING;
524 return CMD_STATUS_FINISHED;
527 return CMD_STATUS_EXECUTING;
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;
535 float bot_cmd_wait_until()
537 if(time < bot_cmd.bot_cmd_parm_float + bot_barriertime)
539 self.bot_exec_status |= BOT_EXEC_STATUS_WAITING;
540 return CMD_STATUS_EXECUTING;
542 self.bot_exec_status &~= BOT_EXEC_STATUS_WAITING;
543 return CMD_STATUS_FINISHED;
546 float bot_cmd_barrier()
550 // 0 = no barrier, 1 = waiting, 2 = waiting finished
552 if(self.bot_barrier == 0) // initialization
554 self.bot_barrier = 1;
556 //self.colormod = '4 4 0';
559 if(self.bot_barrier == 1) // find other bots
561 FOR_EACH_CLIENT(cl) if(cl.isbot)
563 if(cl.bot_cmdqueuebuf_allocated)
564 if(cl.bot_barrier != 1)
565 return CMD_STATUS_EXECUTING; // not all are at the barrier yet
568 // all bots hit the barrier!
569 FOR_EACH_CLIENT(cl) if(cl.isbot)
571 cl.bot_barrier = 2; // acknowledge barrier
574 bot_barriertime = time;
577 // if we get here, the barrier is finished
579 self.bot_barrier = 0;
580 //self.colormod = '0 0 0';
582 return CMD_STATUS_FINISHED;
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;
592 float bot_cmd_select_weapon()
596 id = bot_cmd.bot_cmd_parm_float;
598 if(id < WEP_FIRST || id > WEP_LAST)
599 return CMD_STATUS_ERROR;
601 if(client_hasweapon(self, id, TRUE, FALSE))
602 self.switchweapon = id;
604 return CMD_STATUS_ERROR;
606 return CMD_STATUS_FINISHED;
609 .float bot_cmd_condition_status;
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
617 float bot_cmd_eval(string expr)
619 // Search for numbers
620 if(strstrofs("0123456789", substring(expr, 0, 1), 0) >= 0)
626 if(substring(expr, 0, 5)=="cvar.")
628 return cvar(substring(expr, 5, strlen(expr)));
637 return vlen(self.velocity);
639 return ((self.flagcarried!=world));
642 print(strcat("ERROR: Unable to convert the expression '",expr,"' into a numeric value\n"));
648 local string expr, val_a, val_b;
651 if(self.bot_cmd_condition_status != CMD_CONDITION_NONE)
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;
659 self.bot_cmd_condition_status |= CMD_CONDITION_TRUE_BLOCK;
661 // search for operators
662 expr = bot_cmd.bot_cmd_parm_string;
664 cmpofs = strstrofs(expr,"=",0);
668 val_a = substring(expr,0,cmpofs);
669 val_b = substring(expr,cmpofs+1,strlen(expr));
671 if(bot_cmd_eval(val_a)==bot_cmd_eval(val_b))
672 self.bot_cmd_condition_status |= CMD_CONDITION_TRUE;
674 self.bot_cmd_condition_status |= CMD_CONDITION_FALSE;
676 return CMD_STATUS_FINISHED;
679 cmpofs = strstrofs(expr,">",0);
683 val_a = substring(expr,0,cmpofs);
684 val_b = substring(expr,cmpofs+1,strlen(expr));
686 if(bot_cmd_eval(val_a)>bot_cmd_eval(val_b))
687 self.bot_cmd_condition_status |= CMD_CONDITION_TRUE;
689 self.bot_cmd_condition_status |= CMD_CONDITION_FALSE;
691 return CMD_STATUS_FINISHED;
694 cmpofs = strstrofs(expr,"<",0);
698 val_a = substring(expr,0,cmpofs);
699 val_b = substring(expr,cmpofs+1,strlen(expr));
701 if(bot_cmd_eval(val_a)<bot_cmd_eval(val_b))
702 self.bot_cmd_condition_status |= CMD_CONDITION_TRUE;
704 self.bot_cmd_condition_status |= CMD_CONDITION_FALSE;
706 return CMD_STATUS_FINISHED;
709 if(bot_cmd_eval(expr))
710 self.bot_cmd_condition_status |= CMD_CONDITION_TRUE;
712 self.bot_cmd_condition_status |= CMD_CONDITION_FALSE;
714 return CMD_STATUS_FINISHED;
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;
726 self.bot_cmd_condition_status = CMD_CONDITION_NONE;
727 return CMD_STATUS_FINISHED;
730 float bot_cmd_resetaim()
732 self.v_angle = '0 0 0';
733 return CMD_STATUS_FINISHED;
736 .float bot_cmd_aim_begintime;
737 .float bot_cmd_aim_endtime;
738 .vector bot_cmd_aim_begin;
739 .vector bot_cmd_aim_end;
744 if(self.bot_cmd_aim_endtime)
746 local float progress;
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);
751 if(time>=self.bot_cmd_aim_endtime)
753 self.bot_cmd_aim_endtime = 0;
754 return CMD_STATUS_FINISHED;
757 return CMD_STATUS_EXECUTING;
760 // New aiming direction
762 local float tokens, step;
764 parms = bot_cmd.bot_cmd_parm_string;
766 tokens = tokenizebyseparator(parms, " ");
770 self.v_angle_x -= stof(argv(1));
771 self.v_angle_y += stof(argv(0));
772 return CMD_STATUS_FINISHED;
775 if(tokens<2||tokens>3)
776 return CMD_STATUS_ERROR;
778 step = stof(argv(2));
780 self.bot_cmd_aim_begin = self.v_angle;
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;
786 self.bot_cmd_aim_begintime = time;
787 self.bot_cmd_aim_endtime = time + step;
789 return CMD_STATUS_EXECUTING;
792 float bot_cmd_aimtarget()
794 if(self.bot_cmd_aim_endtime)
796 return bot_cmd_aim();
802 local float tokens, step;
804 parms = bot_cmd.bot_cmd_parm_string;
806 tokens = tokenizebyseparator(parms, " ");
808 e = bot_getplace(argv(0));
810 return CMD_STATUS_ERROR;
812 v = e.origin + (e.mins + e.maxs) * 0.5;
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;
821 if(tokens<1||tokens>2)
822 return CMD_STATUS_ERROR;
824 step = stof(argv(1));
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;
830 self.bot_cmd_aim_begintime = time;
831 self.bot_cmd_aim_endtime = time + step;
833 return CMD_STATUS_EXECUTING;
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
851 float bot_presskeys()
853 self.movement = '0 0 0';
855 if(self.bot_cmd_keys == BOT_CMD_KEY_NONE)
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");
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");
868 if(self.bot_cmd_keys & BOT_CMD_KEY_JUMP)
869 self.BUTTON_JUMP = TRUE;
871 if(self.bot_cmd_keys & BOT_CMD_KEY_CROUCH)
872 self.BUTTON_CROUCH = TRUE;
874 if(self.bot_cmd_keys & BOT_CMD_KEY_ATTACK1)
875 self.BUTTON_ATCK = TRUE;
877 if(self.bot_cmd_keys & BOT_CMD_KEY_ATTACK2)
878 self.BUTTON_ATCK2 = TRUE;
880 if(self.bot_cmd_keys & BOT_CMD_KEY_USE)
881 self.BUTTON_USE = TRUE;
883 if(self.bot_cmd_keys & BOT_CMD_KEY_HOOK)
884 self.BUTTON_HOOK = TRUE;
886 if(self.bot_cmd_keys & BOT_CMD_KEY_CHAT)
887 self.BUTTON_CHAT = TRUE;
893 float bot_cmd_keypress_handler(string key, float enabled)
899 self.bot_cmd_keys = power2of(20) - 1; // >:)
901 self.bot_cmd_keys = BOT_CMD_KEY_NONE;
905 self.bot_cmd_keys |= BOT_CMD_KEY_FORWARD;
906 self.bot_cmd_keys &~= BOT_CMD_KEY_BACKWARD;
909 self.bot_cmd_keys &~= BOT_CMD_KEY_FORWARD;
914 self.bot_cmd_keys |= BOT_CMD_KEY_BACKWARD;
915 self.bot_cmd_keys &~= BOT_CMD_KEY_FORWARD;
918 self.bot_cmd_keys &~= BOT_CMD_KEY_BACKWARD;
923 self.bot_cmd_keys |= BOT_CMD_KEY_LEFT;
924 self.bot_cmd_keys &~= BOT_CMD_KEY_RIGHT;
927 self.bot_cmd_keys &~= BOT_CMD_KEY_LEFT;
932 self.bot_cmd_keys |= BOT_CMD_KEY_RIGHT;
933 self.bot_cmd_keys &~= BOT_CMD_KEY_LEFT;
936 self.bot_cmd_keys &~= BOT_CMD_KEY_RIGHT;
940 self.bot_cmd_keys |= BOT_CMD_KEY_JUMP;
942 self.bot_cmd_keys &~= BOT_CMD_KEY_JUMP;
946 self.bot_cmd_keys |= BOT_CMD_KEY_CROUCH;
948 self.bot_cmd_keys &~= BOT_CMD_KEY_CROUCH;
952 self.bot_cmd_keys |= BOT_CMD_KEY_ATTACK1;
954 self.bot_cmd_keys &~= BOT_CMD_KEY_ATTACK1;
958 self.bot_cmd_keys |= BOT_CMD_KEY_ATTACK2;
960 self.bot_cmd_keys &~= BOT_CMD_KEY_ATTACK2;
964 self.bot_cmd_keys |= BOT_CMD_KEY_USE;
966 self.bot_cmd_keys &~= BOT_CMD_KEY_USE;
970 self.bot_cmd_keys |= BOT_CMD_KEY_HOOK;
972 self.bot_cmd_keys &~= BOT_CMD_KEY_HOOK;
976 self.bot_cmd_keys |= BOT_CMD_KEY_CHAT;
978 self.bot_cmd_keys &~= BOT_CMD_KEY_CHAT;
984 return CMD_STATUS_FINISHED;
987 float bot_cmd_presskey()
991 key = bot_cmd.bot_cmd_parm_string;
993 bot_cmd_keypress_handler(key,TRUE);
995 return CMD_STATUS_FINISHED;
998 float bot_cmd_releasekey()
1002 key = bot_cmd.bot_cmd_parm_string;
1004 return bot_cmd_keypress_handler(key,FALSE);
1007 float bot_cmd_pause()
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;
1019 self.movement = '0 0 0';
1020 self.bot_cmd_keys = BOT_CMD_KEY_NONE;
1022 self.bot_exec_status = self.bot_exec_status | BOT_EXEC_STATUS_PAUSED;
1023 return CMD_STATUS_FINISHED;
1026 float bot_cmd_moveto()
1028 return self.cmd_moveto(bot_cmd.bot_cmd_parm_vector);
1031 float bot_cmd_movetotarget()
1034 e = bot_getplace(bot_cmd.bot_cmd_parm_string);
1036 return CMD_STATUS_ERROR;
1037 return self.cmd_moveto(e.origin + (e.mins + e.maxs) * 0.5);
1040 float bot_cmd_resetgoal()
1042 return self.cmd_resetgoal();
1053 void bot_command_executed(float rm)
1060 bot_dequeuecommand(self, self.bot_cmd_execution_index);
1062 self.bot_cmd_execution_index++;
1065 void bot_setcurrentcommand()
1069 if(self.bot_cmd_execution_index == 0)
1070 self.bot_cmd_execution_index = 1;
1072 if(!self.bot_cmd_current)
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;
1080 bot_cmd = self.bot_cmd_current;
1081 if(bot_cmd.bot_cmd_index != self.bot_cmd_execution_index)
1083 if(bot_havecommand(self, self.bot_cmd_execution_index))
1086 cmdstring = bot_readcommand(self, self.bot_cmd_execution_index);
1087 if(bot_decodecommand(cmdstring))
1089 bot_cmd.owner = self;
1090 bot_cmd.bot_cmd_index = self.bot_cmd_execution_index;
1100 void bot_resetqueues()
1105 FOR_EACH_CLIENT(cl) if(cl.isbot)
1107 if(cl.bot_cmdqueuebuf_allocated)
1109 // also, cancel all barriers
1111 for(i = 0; i < cl.bot_places_count; ++i)
1113 strunzone(cl.(bot_placenames[i]));
1114 cl.(bot_placenames[i]) = string_null;
1116 cl.bot_places_count = 0;
1119 bot_barriertime = time;
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()
1127 local float status, ispressingkey;
1132 if(self.deadflag!=DEAD_NO)
1136 bot_setcurrentcommand();
1138 // Keep pressing keys raised by the "presskey" command
1139 ispressingkey = bot_presskeys();
1142 return ispressingkey;
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)
1148 if(bot_cmd.bot_cmd_type!=BOT_CMD_NULL)
1150 bot_command_executed(TRUE);
1151 print( "WARNING: Commands are ignored while the bot is paused. Use the command 'continue' instead.\n");
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)
1160 bot_command_executed(TRUE);
1163 else if(self.bot_cmd_condition_status & CMD_CONDITION_FALSE && self.bot_cmd_condition_status & CMD_CONDITION_TRUE_BLOCK)
1165 bot_command_executed(TRUE);
1169 // Map commands to functions
1170 switch(bot_cmd.bot_cmd_type)
1173 return ispressingkey;
1176 status = bot_cmd_pause();
1178 case BOT_CMD_CONTINUE:
1179 status = bot_cmd_continue();
1182 status = bot_cmd_wait();
1184 case BOT_CMD_WAIT_UNTIL:
1185 status = bot_cmd_wait_until();
1188 status = bot_cmd_turn();
1190 case BOT_CMD_MOVETO:
1191 status = bot_cmd_moveto();
1193 case BOT_CMD_MOVETOTARGET:
1194 status = bot_cmd_movetotarget();
1196 case BOT_CMD_RESETGOAL:
1197 status = bot_cmd_resetgoal();
1200 status = bot_cmd_cc();
1203 status = bot_cmd_if();
1206 status = bot_cmd_else();
1209 status = bot_cmd_fi();
1211 case BOT_CMD_RESETAIM:
1212 status = bot_cmd_resetaim();
1215 status = bot_cmd_aim();
1217 case BOT_CMD_AIMTARGET:
1218 status = bot_cmd_aimtarget();
1220 case BOT_CMD_PRESSKEY:
1221 status = bot_cmd_presskey();
1223 case BOT_CMD_RELEASEKEY:
1224 status = bot_cmd_releasekey();
1226 case BOT_CMD_SELECTWEAPON:
1227 status = bot_cmd_select_weapon();
1229 case BOT_CMD_IMPULSE:
1230 status = bot_cmd_impulse();
1232 case BOT_CMD_BARRIER:
1233 status = bot_cmd_barrier();
1235 case BOT_CMD_CONSOLE:
1236 localcmd(strcat(bot_cmd.bot_cmd_parm_string, "\n"));
1237 status = CMD_STATUS_FINISHED;
1240 print(strcat("ERROR: Invalid command on queue with id '",ftos(bot_cmd.bot_cmd_type),"'\n"));
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"));
1247 // Move execution pointer
1248 if(status==CMD_STATUS_EXECUTING)
1254 if(cvar("g_debug_bot_commands"))
1258 switch(bot_cmd_parm_type[bot_cmd.bot_cmd_type])
1260 case BOT_CMD_PARAMETER_FLOAT:
1261 parms = ftos(bot_cmd.bot_cmd_parm_float);
1263 case BOT_CMD_PARAMETER_STRING:
1264 parms = bot_cmd.bot_cmd_parm_string;
1266 case BOT_CMD_PARAMETER_VECTOR:
1267 parms = vtos(bot_cmd.bot_cmd_parm_vector);
1273 clientcommand(self,strcat("say ^7", bot_cmd_string[bot_cmd.bot_cmd_type]," ",parms,"\n"));
1276 bot_command_executed(TRUE);
1279 } while(status==CMD_STATUS_FINISHED);