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)
10 buf_del(bot.bot_cmdqueuebuf);
11 bot.bot_cmdqueuebuf_allocated = false;
12 dprint("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_allocated = true;
21 bot.bot_cmdqueuebuf_start = 0;
22 bot.bot_cmdqueuebuf_end = 0;
25 bufstr_set(bot.bot_cmdqueuebuf, bot.bot_cmdqueuebuf_end, cmdstring);
27 // if the command was a "sound" command, precache the sound NOW
28 // this prevents lagging!
34 sp = strstrofs(cmdstring, " ", 0);
37 parm = substring(cmdstring, sp + 1, -1);
38 cmdstr = substring(cmdstring, 0, sp);
44 sp = strstrofs(parm, " ", 0);
47 parm = substring(parm, sp + 1, -1);
54 bot.bot_cmdqueuebuf_end += 1;
57 void bot_dequeuecommand(entity bot, float idx)
59 if(!bot.bot_cmdqueuebuf_allocated)
60 error("dequeuecommand but no queue allocated");
61 if(idx < bot.bot_cmdqueuebuf_start)
62 error("dequeueing a command in the past");
63 if(idx >= bot.bot_cmdqueuebuf_end)
64 error("dequeueing a command in the future");
65 bufstr_set(bot.bot_cmdqueuebuf, idx, "");
66 if(idx == bot.bot_cmdqueuebuf_start)
67 bot.bot_cmdqueuebuf_start += 1;
68 if(bot.bot_cmdqueuebuf_start >= bot.bot_cmdqueuebuf_end)
72 string bot_readcommand(entity bot, float idx)
74 if(!bot.bot_cmdqueuebuf_allocated)
75 error("readcommand but no queue allocated");
76 if(idx < bot.bot_cmdqueuebuf_start)
77 error("reading a command in the past");
78 if(idx >= bot.bot_cmdqueuebuf_end)
79 error("reading a command in the future");
80 return bufstr_get(bot.bot_cmdqueuebuf, idx);
83 float bot_havecommand(entity bot, float idx)
85 if(!bot.bot_cmdqueuebuf_allocated)
87 if(idx < bot.bot_cmdqueuebuf_start)
89 if(idx >= bot.bot_cmdqueuebuf_end)
94 const int MAX_BOT_PLACES = 4;
95 .float bot_places_count;
96 .entity bot_places[MAX_BOT_PLACES];
97 .string bot_placenames[MAX_BOT_PLACES];
98 entity bot_getplace(string placename)
101 if(substring(placename, 0, 1) == "@")
104 placename = substring(placename, 1, -1);
106 for(i = 0; i < self.bot_places_count; ++i)
107 if(self.(bot_placenames[i]) == placename)
108 return self.(bot_places[i]);
109 // now: i == self.bot_places_count
110 s = s2 = cvar_string(placename);
111 p = strstrofs(s2, " ", 0);
114 s = substring(s2, 0, p);
115 //print("places: ", placename, " -> ", cvar_string(placename), "\n");
116 cvar_set(placename, strcat(substring(s2, p+1, -1), " ", s));
117 //print("places: ", placename, " := ", cvar_string(placename), "\n");
119 e = find(world, targetname, s);
121 print("invalid place ", s, "\n");
122 if(i < MAX_BOT_PLACES)
124 self.(bot_placenames[i]) = strzone(placename);
125 self.(bot_places[i]) = e;
126 self.bot_places_count += 1;
132 e = find(world, targetname, placename);
134 print("invalid place ", placename, "\n");
140 // NOTE: New commands should be added here. Do not forget to update BOT_CMD_COUNTER
141 const int BOT_CMD_NULL = 0;
142 const int BOT_CMD_PAUSE = 1;
143 const int BOT_CMD_CONTINUE = 2;
144 const int BOT_CMD_WAIT = 3;
145 const int BOT_CMD_TURN = 4;
146 const int BOT_CMD_MOVETO = 5;
147 const int BOT_CMD_RESETGOAL = 6; // Not implemented yet
148 const int BOT_CMD_CC = 7;
149 const int BOT_CMD_IF = 8;
150 const int BOT_CMD_ELSE = 9;
151 const int BOT_CMD_FI = 10;
152 const int BOT_CMD_RESETAIM = 11;
153 const int BOT_CMD_AIM = 12;
154 const int BOT_CMD_PRESSKEY = 13;
155 const int BOT_CMD_RELEASEKEY = 14;
156 const int BOT_CMD_SELECTWEAPON = 15;
157 const int BOT_CMD_IMPULSE = 16;
158 const int BOT_CMD_WAIT_UNTIL = 17;
159 const int BOT_CMD_MOVETOTARGET = 18;
160 const int BOT_CMD_AIMTARGET = 19;
161 const int BOT_CMD_BARRIER = 20;
162 const int BOT_CMD_CONSOLE = 21;
163 const int BOT_CMD_SOUND = 22;
164 const int BOT_CMD_DEBUG_ASSERT_CANFIRE = 23;
165 const int BOT_CMD_WHILE = 24; // TODO: Not implemented yet
166 const int BOT_CMD_WEND = 25; // TODO: Not implemented yet
167 const int BOT_CMD_CHASE = 26; // TODO: Not implemented yet
169 const int BOT_CMD_COUNTER = 24; // Update this value if you add/remove a command
171 // NOTE: Following commands should be implemented on the bot ai
172 // If a new command should be handled by the target ai(s) please declare it here
173 .float(vector) cmd_moveto;
174 .float() cmd_resetgoal;
177 const int BOT_CMD_PARAMETER_NONE = 0;
178 const int BOT_CMD_PARAMETER_FLOAT = 1;
179 const int BOT_CMD_PARAMETER_STRING = 2;
180 const int BOT_CMD_PARAMETER_VECTOR = 3;
182 float bot_cmds_initialized;
183 int bot_cmd_parm_type[BOT_CMD_COUNTER];
184 string bot_cmd_string[BOT_CMD_COUNTER];
186 // Bots command queue
187 entity bot_cmd; // global current command
188 .entity bot_cmd_current; // current command of this bot
190 .float is_bot_cmd; // Tells if the entity is a bot command
191 .float bot_cmd_index; // Position of the command in the queue
192 .int bot_cmd_type; // If of command (see the BOT_CMD_* defines)
193 .float bot_cmd_parm_float; // Field to store a float parameter
194 .string bot_cmd_parm_string; // Field to store a string parameter
195 .vector bot_cmd_parm_vector; // Field to store a vector parameter
197 float bot_barriertime;
200 .float bot_cmd_execution_index; // Position in the queue of the command to be executed
202 // Initialize global commands list
203 // NOTE: New commands should be initialized here
204 void bot_commands_init()
206 bot_cmd_string[BOT_CMD_NULL] = "";
207 bot_cmd_parm_type[BOT_CMD_NULL] = BOT_CMD_PARAMETER_NONE;
209 bot_cmd_string[BOT_CMD_PAUSE] = "pause";
210 bot_cmd_parm_type[BOT_CMD_PAUSE] = BOT_CMD_PARAMETER_NONE;
212 bot_cmd_string[BOT_CMD_CONTINUE] = "continue";
213 bot_cmd_parm_type[BOT_CMD_CONTINUE] = BOT_CMD_PARAMETER_NONE;
215 bot_cmd_string[BOT_CMD_WAIT] = "wait";
216 bot_cmd_parm_type[BOT_CMD_WAIT] = BOT_CMD_PARAMETER_FLOAT;
218 bot_cmd_string[BOT_CMD_TURN] = "turn";
219 bot_cmd_parm_type[BOT_CMD_TURN] = BOT_CMD_PARAMETER_FLOAT;
221 bot_cmd_string[BOT_CMD_MOVETO] = "moveto";
222 bot_cmd_parm_type[BOT_CMD_MOVETO] = BOT_CMD_PARAMETER_VECTOR;
224 bot_cmd_string[BOT_CMD_MOVETOTARGET] = "movetotarget";
225 bot_cmd_parm_type[BOT_CMD_MOVETOTARGET] = BOT_CMD_PARAMETER_STRING;
227 bot_cmd_string[BOT_CMD_RESETGOAL] = "resetgoal";
228 bot_cmd_parm_type[BOT_CMD_RESETGOAL] = BOT_CMD_PARAMETER_NONE;
230 bot_cmd_string[BOT_CMD_CC] = "cc";
231 bot_cmd_parm_type[BOT_CMD_CC] = BOT_CMD_PARAMETER_STRING;
233 bot_cmd_string[BOT_CMD_IF] = "if";
234 bot_cmd_parm_type[BOT_CMD_IF] = BOT_CMD_PARAMETER_STRING;
236 bot_cmd_string[BOT_CMD_ELSE] = "else";
237 bot_cmd_parm_type[BOT_CMD_ELSE] = BOT_CMD_PARAMETER_NONE;
239 bot_cmd_string[BOT_CMD_FI] = "fi";
240 bot_cmd_parm_type[BOT_CMD_FI] = BOT_CMD_PARAMETER_NONE;
242 bot_cmd_string[BOT_CMD_RESETAIM] = "resetaim";
243 bot_cmd_parm_type[BOT_CMD_RESETAIM] = BOT_CMD_PARAMETER_NONE;
245 bot_cmd_string[BOT_CMD_AIM] = "aim";
246 bot_cmd_parm_type[BOT_CMD_AIM] = BOT_CMD_PARAMETER_STRING;
248 bot_cmd_string[BOT_CMD_AIMTARGET] = "aimtarget";
249 bot_cmd_parm_type[BOT_CMD_AIMTARGET] = BOT_CMD_PARAMETER_STRING;
251 bot_cmd_string[BOT_CMD_PRESSKEY] = "presskey";
252 bot_cmd_parm_type[BOT_CMD_PRESSKEY] = BOT_CMD_PARAMETER_STRING;
254 bot_cmd_string[BOT_CMD_RELEASEKEY] = "releasekey";
255 bot_cmd_parm_type[BOT_CMD_RELEASEKEY] = BOT_CMD_PARAMETER_STRING;
257 bot_cmd_string[BOT_CMD_SELECTWEAPON] = "selectweapon";
258 bot_cmd_parm_type[BOT_CMD_SELECTWEAPON] = BOT_CMD_PARAMETER_FLOAT;
260 bot_cmd_string[BOT_CMD_IMPULSE] = "impulse";
261 bot_cmd_parm_type[BOT_CMD_IMPULSE] = BOT_CMD_PARAMETER_FLOAT;
263 bot_cmd_string[BOT_CMD_WAIT_UNTIL] = "wait_until";
264 bot_cmd_parm_type[BOT_CMD_WAIT_UNTIL] = BOT_CMD_PARAMETER_FLOAT;
266 bot_cmd_string[BOT_CMD_BARRIER] = "barrier";
267 bot_cmd_parm_type[BOT_CMD_BARRIER] = BOT_CMD_PARAMETER_NONE;
269 bot_cmd_string[BOT_CMD_CONSOLE] = "console";
270 bot_cmd_parm_type[BOT_CMD_CONSOLE] = BOT_CMD_PARAMETER_STRING;
272 bot_cmd_string[BOT_CMD_SOUND] = "sound";
273 bot_cmd_parm_type[BOT_CMD_SOUND] = BOT_CMD_PARAMETER_STRING;
275 bot_cmd_string[BOT_CMD_DEBUG_ASSERT_CANFIRE] = "debug_assert_canfire";
276 bot_cmd_parm_type[BOT_CMD_DEBUG_ASSERT_CANFIRE] = BOT_CMD_PARAMETER_FLOAT;
278 bot_cmds_initialized = true;
281 // Returns first bot with matching name
282 entity find_bot_by_name(string name)
286 bot = findchainflags(flags, FL_CLIENT);
289 if(IS_BOT_CLIENT(bot))
290 if(bot.netname==name)
299 // Returns a bot by number on list
300 entity find_bot_by_number(float number)
308 bot = findchainflags(flags, FL_CLIENT);
311 if(IS_BOT_CLIENT(bot))
322 float bot_decodecommand(string cmdstring)
328 sp = strstrofs(cmdstring, " ", 0);
335 parm = substring(cmdstring, sp + 1, -1);
336 cmdstring = substring(cmdstring, 0, sp);
339 if(!bot_cmds_initialized)
343 for(i=1;i<BOT_CMD_COUNTER;++i)
345 if(bot_cmd_string[i]!=cmdstring)
348 cmd_parm_type = bot_cmd_parm_type[i];
350 if(cmd_parm_type!=BOT_CMD_PARAMETER_NONE&&parm=="")
352 print("ERROR: A parameter is required for this command\n");
356 // Load command into queue
357 bot_cmd.bot_cmd_type = i;
360 switch(cmd_parm_type)
362 case BOT_CMD_PARAMETER_FLOAT:
363 bot_cmd.bot_cmd_parm_float = stof(parm);
365 case BOT_CMD_PARAMETER_STRING:
366 if(bot_cmd.bot_cmd_parm_string)
367 strunzone(bot_cmd.bot_cmd_parm_string);
368 bot_cmd.bot_cmd_parm_string = strzone(parm);
370 case BOT_CMD_PARAMETER_VECTOR:
371 bot_cmd.bot_cmd_parm_vector = stov(parm);
378 print("ERROR: No such command '", cmdstring, "'\n");
382 void bot_cmdhelp(string scmd)
387 if(!bot_cmds_initialized)
390 for(i=1;i<BOT_CMD_COUNTER;++i)
392 if(bot_cmd_string[i]!=scmd)
395 ntype = bot_cmd_parm_type[i];
399 case BOT_CMD_PARAMETER_FLOAT:
400 stype = "float number";
402 case BOT_CMD_PARAMETER_STRING:
405 case BOT_CMD_PARAMETER_VECTOR:
413 print(strcat("Command: ",bot_cmd_string[i],"\nParameter: <",stype,"> \n"));
415 print("Description: ");
419 print("Stops the bot completely. Any command other than 'continue' will be ignored.");
421 case BOT_CMD_CONTINUE:
422 print("Disable paused status");
425 print("Pause command parsing and bot ai for N seconds. Pressed key will remain pressed");
427 case BOT_CMD_WAIT_UNTIL:
428 print("Pause command parsing and bot ai until time is N from the last barrier. Pressed key will remain pressed");
430 case BOT_CMD_BARRIER:
431 print("Waits till all bots that have a command queue reach this command. Pressed key will remain pressed");
434 print("Look to the right or left N degrees. For turning to the left use positive numbers.");
437 print("Walk to an specific coordinate on the map. Usage: moveto \"x y z\"");
439 case BOT_CMD_MOVETOTARGET:
440 print("Walk to the specific target on the map");
442 case BOT_CMD_RESETGOAL:
443 print("Resets the goal stack");
446 print("Execute client command. Examples: cc \"say something\"; cc god; cc \"name newnickname\"; cc kill;");
449 print("Perform simple conditional execution.\n");
451 print(" sv_cmd .. if \"condition\"\n");
452 print(" sv_cmd .. <instruction if true>\n");
453 print(" sv_cmd .. <instruction if true>\n");
454 print(" sv_cmd .. else\n");
455 print(" sv_cmd .. <instruction if false>\n");
456 print(" sv_cmd .. <instruction if false>\n");
457 print(" sv_cmd .. fi\n");
458 print("Conditions: a=b, a>b, a<b, a\t\t(spaces not allowed)\n");
459 print(" Values in conditions can be numbers, cvars in the form cvar.cvar_string or special fields\n");
460 print("Fields: health, speed, flagcarrier\n");
461 print("Examples: if health>50; if health>cvar.g_balance_laser_primary_damage; if flagcarrier;");
463 case BOT_CMD_RESETAIM:
464 print("Points the aim to the coordinates x,y 0,0");
467 print("Move the aim x/y (horizontal/vertical) degrees relatives to the bot\n");
468 print("There is a 3rd optional parameter telling in how many seconds the aim has to reach the new position\n");
469 print("Examples: aim \"90 0\" // Turn 90 degrees inmediately (positive numbers move to the left/up)\n");
470 print(" aim \"0 90 2\" // Will gradually look to the sky in the next two seconds");
472 case BOT_CMD_AIMTARGET:
473 print("Points the aim to given target");
475 case BOT_CMD_PRESSKEY:
476 print("Press one of the following keys: forward, backward, left, right, jump, crouch, attack1, attack2, use\n");
477 print("Multiple keys can be pressed at time (with many presskey calls) and it will remain pressed until the command \"releasekey\" is called");
478 print("Note: The script will not return the control to the bot ai until all keys are released");
480 case BOT_CMD_RELEASEKEY:
481 print("Release previoulsy used keys. Use the parameter \"all\" to release all keys");
484 print("play sound file at bot location");
486 case BOT_CMD_DEBUG_ASSERT_CANFIRE:
487 print("verify the state of the weapon entity");
490 print("This command has no description yet.");
497 void bot_list_commands()
502 if(!bot_cmds_initialized)
505 print("List of all available commands:\n");
506 print(" Command - Parameter Type\n");
508 for(i=1;i<BOT_CMD_COUNTER;++i)
510 switch(bot_cmd_parm_type[i])
512 case BOT_CMD_PARAMETER_FLOAT:
513 ptype = "float number";
515 case BOT_CMD_PARAMETER_STRING:
518 case BOT_CMD_PARAMETER_VECTOR:
525 print(strcat(" ",bot_cmd_string[i]," - <",ptype,"> \n"));
530 .int bot_exec_status;
532 #define BOT_EXEC_STATUS_IDLE 0
533 #define BOT_EXEC_STATUS_PAUSED 1
534 #define BOT_EXEC_STATUS_WAITING 2
536 #define CMD_STATUS_EXECUTING 0
537 #define CMD_STATUS_FINISHED 1
538 #define CMD_STATUS_ERROR 2
540 void SV_ParseClientCommand(string s);
543 SV_ParseClientCommand(bot_cmd.bot_cmd_parm_string);
544 return CMD_STATUS_FINISHED;
547 float bot_cmd_impulse()
549 self.impulse = bot_cmd.bot_cmd_parm_float;
550 return CMD_STATUS_FINISHED;
553 float bot_cmd_continue()
555 self.bot_exec_status &= ~BOT_EXEC_STATUS_PAUSED;
556 return CMD_STATUS_FINISHED;
559 .float bot_cmd_wait_time;
562 if(self.bot_exec_status & BOT_EXEC_STATUS_WAITING)
564 if(time>=self.bot_cmd_wait_time)
566 self.bot_exec_status &= ~BOT_EXEC_STATUS_WAITING;
567 return CMD_STATUS_FINISHED;
570 return CMD_STATUS_EXECUTING;
573 self.bot_cmd_wait_time = time + bot_cmd.bot_cmd_parm_float;
574 self.bot_exec_status |= BOT_EXEC_STATUS_WAITING;
575 return CMD_STATUS_EXECUTING;
578 float bot_cmd_wait_until()
580 if(time < bot_cmd.bot_cmd_parm_float + bot_barriertime)
582 self.bot_exec_status |= BOT_EXEC_STATUS_WAITING;
583 return CMD_STATUS_EXECUTING;
585 self.bot_exec_status &= ~BOT_EXEC_STATUS_WAITING;
586 return CMD_STATUS_FINISHED;
589 float bot_cmd_barrier()
593 // 0 = no barrier, 1 = waiting, 2 = waiting finished
595 if(self.bot_barrier == 0) // initialization
597 self.bot_barrier = 1;
599 //self.colormod = '4 4 0';
602 if(self.bot_barrier == 1) // find other bots
604 FOR_EACH_CLIENT(cl) if(cl.isbot)
606 if(cl.bot_cmdqueuebuf_allocated)
607 if(cl.bot_barrier != 1)
608 return CMD_STATUS_EXECUTING; // not all are at the barrier yet
611 // all bots hit the barrier!
612 FOR_EACH_CLIENT(cl) if(cl.isbot)
614 cl.bot_barrier = 2; // acknowledge barrier
617 bot_barriertime = time;
620 // if we get here, the barrier is finished
622 self.bot_barrier = 0;
623 //self.colormod = '0 0 0';
625 return CMD_STATUS_FINISHED;
630 self.v_angle_y = self.v_angle.y + bot_cmd.bot_cmd_parm_float;
631 self.v_angle_y = self.v_angle.y - floor(self.v_angle.y / 360) * 360;
632 return CMD_STATUS_FINISHED;
635 float bot_cmd_select_weapon()
639 id = bot_cmd.bot_cmd_parm_float;
641 if(id < WEP_FIRST || id > WEP_LAST)
642 return CMD_STATUS_ERROR;
644 if(client_hasweapon(self, id, true, false))
645 self.switchweapon = id;
647 return CMD_STATUS_ERROR;
649 return CMD_STATUS_FINISHED;
652 .int bot_cmd_condition_status;
654 const int CMD_CONDITION_NONE = 0;
655 const int CMD_CONDITION_true = 1;
656 const int CMD_CONDITION_false = 2;
657 const int CMD_CONDITION_true_BLOCK = 4;
658 const int CMD_CONDITION_false_BLOCK = 8;
660 float bot_cmd_eval(string expr)
662 // Search for numbers
663 if(strstrofs("0123456789", substring(expr, 0, 1), 0) >= 0)
669 if(substring(expr, 0, 5)=="cvar.")
671 return cvar(substring(expr, 5, strlen(expr)));
680 return vlen(self.velocity);
682 return ((self.flagcarried!=world));
685 print(strcat("ERROR: Unable to convert the expression '",expr,"' into a numeric value\n"));
691 string expr, val_a, val_b;
694 if(self.bot_cmd_condition_status != CMD_CONDITION_NONE)
696 // Only one "if" block is allowed at time
697 print("ERROR: Only one conditional block can be processed at time");
698 bot_clearqueue(self);
699 return CMD_STATUS_ERROR;
702 self.bot_cmd_condition_status |= CMD_CONDITION_true_BLOCK;
704 // search for operators
705 expr = bot_cmd.bot_cmd_parm_string;
707 cmpofs = strstrofs(expr,"=",0);
711 val_a = substring(expr,0,cmpofs);
712 val_b = substring(expr,cmpofs+1,strlen(expr));
714 if(bot_cmd_eval(val_a)==bot_cmd_eval(val_b))
715 self.bot_cmd_condition_status |= CMD_CONDITION_true;
717 self.bot_cmd_condition_status |= CMD_CONDITION_false;
719 return CMD_STATUS_FINISHED;
722 cmpofs = strstrofs(expr,">",0);
726 val_a = substring(expr,0,cmpofs);
727 val_b = substring(expr,cmpofs+1,strlen(expr));
729 if(bot_cmd_eval(val_a)>bot_cmd_eval(val_b))
730 self.bot_cmd_condition_status |= CMD_CONDITION_true;
732 self.bot_cmd_condition_status |= CMD_CONDITION_false;
734 return CMD_STATUS_FINISHED;
737 cmpofs = strstrofs(expr,"<",0);
741 val_a = substring(expr,0,cmpofs);
742 val_b = substring(expr,cmpofs+1,strlen(expr));
744 if(bot_cmd_eval(val_a)<bot_cmd_eval(val_b))
745 self.bot_cmd_condition_status |= CMD_CONDITION_true;
747 self.bot_cmd_condition_status |= CMD_CONDITION_false;
749 return CMD_STATUS_FINISHED;
752 if(bot_cmd_eval(expr))
753 self.bot_cmd_condition_status |= CMD_CONDITION_true;
755 self.bot_cmd_condition_status |= CMD_CONDITION_false;
757 return CMD_STATUS_FINISHED;
762 self.bot_cmd_condition_status &= ~CMD_CONDITION_true_BLOCK;
763 self.bot_cmd_condition_status |= CMD_CONDITION_false_BLOCK;
764 return CMD_STATUS_FINISHED;
769 self.bot_cmd_condition_status = CMD_CONDITION_NONE;
770 return CMD_STATUS_FINISHED;
773 float bot_cmd_resetaim()
775 self.v_angle = '0 0 0';
776 return CMD_STATUS_FINISHED;
779 .float bot_cmd_aim_begintime;
780 .float bot_cmd_aim_endtime;
781 .vector bot_cmd_aim_begin;
782 .vector bot_cmd_aim_end;
787 if(self.bot_cmd_aim_endtime)
791 progress = min(1 - (self.bot_cmd_aim_endtime - time) / (self.bot_cmd_aim_endtime - self.bot_cmd_aim_begintime),1);
792 self.v_angle = self.bot_cmd_aim_begin + ((self.bot_cmd_aim_end - self.bot_cmd_aim_begin) * progress);
794 if(time>=self.bot_cmd_aim_endtime)
796 self.bot_cmd_aim_endtime = 0;
797 return CMD_STATUS_FINISHED;
800 return CMD_STATUS_EXECUTING;
803 // New aiming direction
807 parms = bot_cmd.bot_cmd_parm_string;
809 tokens = tokenizebyseparator(parms, " ");
811 if(tokens<2||tokens>3)
812 return CMD_STATUS_ERROR;
814 step = (tokens == 3) ? stof(argv(2)) : 0;
818 self.v_angle_x -= stof(argv(1));
819 self.v_angle_y += stof(argv(0));
820 return CMD_STATUS_FINISHED;
823 self.bot_cmd_aim_begin = self.v_angle;
825 self.bot_cmd_aim_end_x = self.v_angle.x - stof(argv(1));
826 self.bot_cmd_aim_end_y = self.v_angle.y + stof(argv(0));
827 self.bot_cmd_aim_end_z = 0;
829 self.bot_cmd_aim_begintime = time;
830 self.bot_cmd_aim_endtime = time + step;
832 return CMD_STATUS_EXECUTING;
835 float bot_cmd_aimtarget()
837 if(self.bot_cmd_aim_endtime)
839 return bot_cmd_aim();
847 parms = bot_cmd.bot_cmd_parm_string;
849 tokens = tokenizebyseparator(parms, " ");
851 e = bot_getplace(argv(0));
853 return CMD_STATUS_ERROR;
855 v = e.origin + (e.mins + e.maxs) * 0.5;
859 self.v_angle = vectoangles(v - (self.origin + self.view_ofs));
860 self.v_angle_x = -self.v_angle.x;
861 return CMD_STATUS_FINISHED;
864 if(tokens<1||tokens>2)
865 return CMD_STATUS_ERROR;
867 step = stof(argv(1));
869 self.bot_cmd_aim_begin = self.v_angle;
870 self.bot_cmd_aim_end = vectoangles(v - (self.origin + self.view_ofs));
871 self.bot_cmd_aim_end_x = -self.bot_cmd_aim_end.x;
873 self.bot_cmd_aim_begintime = time;
874 self.bot_cmd_aim_endtime = time + step;
876 return CMD_STATUS_EXECUTING;
881 const int BOT_CMD_KEY_NONE = 0;
882 const int BOT_CMD_KEY_FORWARD = 1;
883 const int BOT_CMD_KEY_BACKWARD = 2;
884 const int BOT_CMD_KEY_RIGHT = 4;
885 const int BOT_CMD_KEY_LEFT = 8;
886 const int BOT_CMD_KEY_JUMP = 16;
887 const int BOT_CMD_KEY_ATTACK1 = 32;
888 const int BOT_CMD_KEY_ATTACK2 = 64;
889 const int BOT_CMD_KEY_USE = 128;
890 const int BOT_CMD_KEY_HOOK = 256;
891 const int BOT_CMD_KEY_CROUCH = 512;
892 const int BOT_CMD_KEY_CHAT = 1024;
894 float bot_presskeys()
896 self.movement = '0 0 0';
897 self.BUTTON_JUMP = false;
898 self.BUTTON_CROUCH = false;
899 self.BUTTON_ATCK = false;
900 self.BUTTON_ATCK2 = false;
901 self.BUTTON_USE = false;
902 self.BUTTON_HOOK = false;
903 self.BUTTON_CHAT = false;
905 if(self.bot_cmd_keys == BOT_CMD_KEY_NONE)
908 if(self.bot_cmd_keys & BOT_CMD_KEY_FORWARD)
909 self.movement_x = autocvar_sv_maxspeed;
910 else if(self.bot_cmd_keys & BOT_CMD_KEY_BACKWARD)
911 self.movement_x = -autocvar_sv_maxspeed;
913 if(self.bot_cmd_keys & BOT_CMD_KEY_RIGHT)
914 self.movement_y = autocvar_sv_maxspeed;
915 else if(self.bot_cmd_keys & BOT_CMD_KEY_LEFT)
916 self.movement_y = -autocvar_sv_maxspeed;
918 if(self.bot_cmd_keys & BOT_CMD_KEY_JUMP)
919 self.BUTTON_JUMP = true;
921 if(self.bot_cmd_keys & BOT_CMD_KEY_CROUCH)
922 self.BUTTON_CROUCH = true;
924 if(self.bot_cmd_keys & BOT_CMD_KEY_ATTACK1)
925 self.BUTTON_ATCK = true;
927 if(self.bot_cmd_keys & BOT_CMD_KEY_ATTACK2)
928 self.BUTTON_ATCK2 = true;
930 if(self.bot_cmd_keys & BOT_CMD_KEY_USE)
931 self.BUTTON_USE = true;
933 if(self.bot_cmd_keys & BOT_CMD_KEY_HOOK)
934 self.BUTTON_HOOK = true;
936 if(self.bot_cmd_keys & BOT_CMD_KEY_CHAT)
937 self.BUTTON_CHAT = true;
943 float bot_cmd_keypress_handler(string key, float enabled)
949 self.bot_cmd_keys = power2of(20) - 1; // >:)
951 self.bot_cmd_keys = BOT_CMD_KEY_NONE;
955 self.bot_cmd_keys |= BOT_CMD_KEY_FORWARD;
956 self.bot_cmd_keys &= ~BOT_CMD_KEY_BACKWARD;
959 self.bot_cmd_keys &= ~BOT_CMD_KEY_FORWARD;
964 self.bot_cmd_keys |= BOT_CMD_KEY_BACKWARD;
965 self.bot_cmd_keys &= ~BOT_CMD_KEY_FORWARD;
968 self.bot_cmd_keys &= ~BOT_CMD_KEY_BACKWARD;
973 self.bot_cmd_keys |= BOT_CMD_KEY_LEFT;
974 self.bot_cmd_keys &= ~BOT_CMD_KEY_RIGHT;
977 self.bot_cmd_keys &= ~BOT_CMD_KEY_LEFT;
982 self.bot_cmd_keys |= BOT_CMD_KEY_RIGHT;
983 self.bot_cmd_keys &= ~BOT_CMD_KEY_LEFT;
986 self.bot_cmd_keys &= ~BOT_CMD_KEY_RIGHT;
990 self.bot_cmd_keys |= BOT_CMD_KEY_JUMP;
992 self.bot_cmd_keys &= ~BOT_CMD_KEY_JUMP;
996 self.bot_cmd_keys |= BOT_CMD_KEY_CROUCH;
998 self.bot_cmd_keys &= ~BOT_CMD_KEY_CROUCH;
1002 self.bot_cmd_keys |= BOT_CMD_KEY_ATTACK1;
1004 self.bot_cmd_keys &= ~BOT_CMD_KEY_ATTACK1;
1008 self.bot_cmd_keys |= BOT_CMD_KEY_ATTACK2;
1010 self.bot_cmd_keys &= ~BOT_CMD_KEY_ATTACK2;
1014 self.bot_cmd_keys |= BOT_CMD_KEY_USE;
1016 self.bot_cmd_keys &= ~BOT_CMD_KEY_USE;
1020 self.bot_cmd_keys |= BOT_CMD_KEY_HOOK;
1022 self.bot_cmd_keys &= ~BOT_CMD_KEY_HOOK;
1026 self.bot_cmd_keys |= BOT_CMD_KEY_CHAT;
1028 self.bot_cmd_keys &= ~BOT_CMD_KEY_CHAT;
1034 return CMD_STATUS_FINISHED;
1037 float bot_cmd_presskey()
1041 key = bot_cmd.bot_cmd_parm_string;
1043 bot_cmd_keypress_handler(key,true);
1045 return CMD_STATUS_FINISHED;
1048 float bot_cmd_releasekey()
1052 key = bot_cmd.bot_cmd_parm_string;
1054 return bot_cmd_keypress_handler(key,false);
1057 float bot_cmd_pause()
1061 self.BUTTON_USE = 0;
1062 self.BUTTON_ATCK = 0;
1063 self.BUTTON_JUMP = 0;
1064 self.BUTTON_HOOK = 0;
1065 self.BUTTON_CHAT = 0;
1066 self.BUTTON_ATCK2 = 0;
1067 self.BUTTON_CROUCH = 0;
1069 self.movement = '0 0 0';
1070 self.bot_cmd_keys = BOT_CMD_KEY_NONE;
1072 self.bot_exec_status |= BOT_EXEC_STATUS_PAUSED;
1073 return CMD_STATUS_FINISHED;
1076 float bot_cmd_moveto()
1078 return self.cmd_moveto(bot_cmd.bot_cmd_parm_vector);
1081 float bot_cmd_movetotarget()
1084 e = bot_getplace(bot_cmd.bot_cmd_parm_string);
1086 return CMD_STATUS_ERROR;
1087 return self.cmd_moveto(e.origin + (e.mins + e.maxs) * 0.5);
1090 float bot_cmd_resetgoal()
1092 return self.cmd_resetgoal();
1096 float bot_cmd_sound()
1099 f = bot_cmd.bot_cmd_parm_string;
1101 float n = tokenizebyseparator(f, " ");
1104 float chan = CH_WEAPON_B;
1105 float vol = VOL_BASE;
1106 float atten = ATTEN_MIN;
1109 sample = argv(n - 1);
1111 chan = stof(argv(0));
1113 vol = stof(argv(1));
1115 atten = stof(argv(2));
1118 sound(self, chan, sample, vol, atten);
1120 return CMD_STATUS_FINISHED;
1124 float bot_cmd_debug_assert_canfire()
1127 f = bot_cmd.bot_cmd_parm_float;
1129 if(self.weaponentity.state != WS_READY)
1133 self.colormod = '0 8 8';
1134 print("Bot ", self.netname, " using ", self.weaponname, " wants to fire, inhibited by weaponentity state\n");
1137 else if(ATTACK_FINISHED(self) > time)
1141 self.colormod = '8 0 8';
1142 print("Bot ", self.netname, " using ", self.weaponname, " wants to fire, inhibited by ATTACK_FINISHED (", ftos(ATTACK_FINISHED(self) - time), " seconds left)\n");
1145 else if(self.tuba_note)
1149 self.colormod = '8 0 0';
1150 print("Bot ", self.netname, " using ", self.weaponname, " wants to fire, bot still has an active tuba note\n");
1157 self.colormod = '8 8 0';
1158 print("Bot ", self.netname, " using ", self.weaponname, " thinks it has fired, but apparently did not; ATTACK_FINISHED says ", ftos(ATTACK_FINISHED(self) - time), " seconds left\n");
1162 return CMD_STATUS_FINISHED;
1167 void bot_command_executed(float rm)
1174 bot_dequeuecommand(self, self.bot_cmd_execution_index);
1176 self.bot_cmd_execution_index++;
1179 void bot_setcurrentcommand()
1183 if(!self.bot_cmd_current)
1185 self.bot_cmd_current = spawn();
1186 self.bot_cmd_current.classname = "bot_cmd";
1187 self.bot_cmd_current.is_bot_cmd = 1;
1190 bot_cmd = self.bot_cmd_current;
1191 if(bot_cmd.bot_cmd_index != self.bot_cmd_execution_index || self.bot_cmd_execution_index == 0)
1193 if(bot_havecommand(self, self.bot_cmd_execution_index))
1196 cmdstring = bot_readcommand(self, self.bot_cmd_execution_index);
1197 if(bot_decodecommand(cmdstring))
1199 bot_cmd.owner = self;
1200 bot_cmd.bot_cmd_index = self.bot_cmd_execution_index;
1204 // Invalid command, remove from queue
1206 bot_dequeuecommand(self, self.bot_cmd_execution_index);
1207 self.bot_cmd_execution_index++;
1215 void bot_resetqueues()
1220 FOR_EACH_CLIENT(cl) if(cl.isbot)
1222 cl.bot_cmd_execution_index = 0;
1224 // also, cancel all barriers
1226 for(i = 0; i < cl.bot_places_count; ++i)
1228 strunzone(cl.(bot_placenames[i]));
1229 cl.(bot_placenames[i]) = string_null;
1231 cl.bot_places_count = 0;
1234 bot_barriertime = time;
1237 // Here we map commands to functions and deal with complex interactions between commands and execution states
1238 // NOTE: Of course you need to include your commands here too :)
1239 float bot_execute_commands_once()
1241 float status, ispressingkey;
1244 bot_setcurrentcommand();
1246 // if we have no bot command, better return
1247 // old logic kept pressing previously pressed keys, but that has problems
1248 // (namely, it means you cannot make a bot "normal" ever again)
1249 // to keep a bot walking for a while, use the "wait" bot command
1250 if(bot_cmd == world)
1253 // Ignore all commands except continue when the bot is paused
1254 if(self.bot_exec_status & BOT_EXEC_STATUS_PAUSED)
1255 if(bot_cmd.bot_cmd_type!=BOT_CMD_CONTINUE)
1257 if(bot_cmd.bot_cmd_type!=BOT_CMD_NULL)
1259 bot_command_executed(true);
1260 print( "WARNING: Commands are ignored while the bot is paused. Use the command 'continue' instead.\n");
1265 // Keep pressing keys raised by the "presskey" command
1266 ispressingkey = !!bot_presskeys();
1268 // Handle conditions
1269 if (!(bot_cmd.bot_cmd_type==BOT_CMD_FI||bot_cmd.bot_cmd_type==BOT_CMD_ELSE))
1270 if(self.bot_cmd_condition_status & CMD_CONDITION_true && self.bot_cmd_condition_status & CMD_CONDITION_false_BLOCK)
1272 bot_command_executed(true);
1275 else if(self.bot_cmd_condition_status & CMD_CONDITION_false && self.bot_cmd_condition_status & CMD_CONDITION_true_BLOCK)
1277 bot_command_executed(true);
1281 // Map commands to functions
1282 switch(bot_cmd.bot_cmd_type)
1285 return ispressingkey;
1288 status = bot_cmd_pause();
1290 case BOT_CMD_CONTINUE:
1291 status = bot_cmd_continue();
1294 status = bot_cmd_wait();
1296 case BOT_CMD_WAIT_UNTIL:
1297 status = bot_cmd_wait_until();
1300 status = bot_cmd_turn();
1302 case BOT_CMD_MOVETO:
1303 status = bot_cmd_moveto();
1305 case BOT_CMD_MOVETOTARGET:
1306 status = bot_cmd_movetotarget();
1308 case BOT_CMD_RESETGOAL:
1309 status = bot_cmd_resetgoal();
1312 status = bot_cmd_cc();
1315 status = bot_cmd_if();
1318 status = bot_cmd_else();
1321 status = bot_cmd_fi();
1323 case BOT_CMD_RESETAIM:
1324 status = bot_cmd_resetaim();
1327 status = bot_cmd_aim();
1329 case BOT_CMD_AIMTARGET:
1330 status = bot_cmd_aimtarget();
1332 case BOT_CMD_PRESSKEY:
1333 status = bot_cmd_presskey();
1335 case BOT_CMD_RELEASEKEY:
1336 status = bot_cmd_releasekey();
1338 case BOT_CMD_SELECTWEAPON:
1339 status = bot_cmd_select_weapon();
1341 case BOT_CMD_IMPULSE:
1342 status = bot_cmd_impulse();
1344 case BOT_CMD_BARRIER:
1345 status = bot_cmd_barrier();
1347 case BOT_CMD_CONSOLE:
1348 localcmd(strcat(bot_cmd.bot_cmd_parm_string, "\n"));
1349 status = CMD_STATUS_FINISHED;
1352 status = bot_cmd_sound();
1354 case BOT_CMD_DEBUG_ASSERT_CANFIRE:
1355 status = bot_cmd_debug_assert_canfire();
1358 print(strcat("ERROR: Invalid command on queue with id '",ftos(bot_cmd.bot_cmd_type),"'\n"));
1362 if (status==CMD_STATUS_ERROR)
1363 print(strcat("ERROR: The command '",bot_cmd_string[bot_cmd.bot_cmd_type],"' returned an error status\n"));
1365 // Move execution pointer
1366 if(status==CMD_STATUS_EXECUTING)
1372 if(autocvar_g_debug_bot_commands)
1376 switch(bot_cmd_parm_type[bot_cmd.bot_cmd_type])
1378 case BOT_CMD_PARAMETER_FLOAT:
1379 parms = ftos(bot_cmd.bot_cmd_parm_float);
1381 case BOT_CMD_PARAMETER_STRING:
1382 parms = bot_cmd.bot_cmd_parm_string;
1384 case BOT_CMD_PARAMETER_VECTOR:
1385 parms = vtos(bot_cmd.bot_cmd_parm_vector);
1391 clientcommand(self,strcat("say ^7", bot_cmd_string[bot_cmd.bot_cmd_type]," ",parms,"\n"));
1394 bot_command_executed(true);
1397 if(status == CMD_STATUS_FINISHED)
1400 return CMD_STATUS_ERROR;
1403 // This function should be (the only) called directly from the bot ai loop
1404 float bot_execute_commands()
1409 f = bot_execute_commands_once();