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("clearqueue but no queue 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(cmdstr, " ", 0);
41 parm = substring(cmdstr, sp + 1, -1);
42 cmdstr = substring(cmdstr, 0, sp);
44 precache_sound(cmdstr);
48 bot.bot_cmdqueuebuf_end += 1;
51 void bot_dequeuecommand(entity bot, float idx)
53 if(!bot.bot_cmdqueuebuf_allocated)
54 error("dequeuecommand but no queue allocated");
55 if(idx < bot.bot_cmdqueuebuf_start)
56 error("dequeueing a command in the past");
57 if(idx >= bot.bot_cmdqueuebuf_end)
58 error("dequeueing a command in the future");
59 bufstr_set(bot.bot_cmdqueuebuf, idx, "");
60 if(idx == bot.bot_cmdqueuebuf_start)
61 bot.bot_cmdqueuebuf_start += 1;
62 if(bot.bot_cmdqueuebuf_start >= bot.bot_cmdqueuebuf_end)
66 string bot_readcommand(entity bot, float idx)
68 if(!bot.bot_cmdqueuebuf_allocated)
69 error("readcommand but no queue allocated");
70 if(idx < bot.bot_cmdqueuebuf_start)
71 error("reading a command in the past");
72 if(idx >= bot.bot_cmdqueuebuf_end)
73 error("reading a command in the future");
74 return bufstr_get(bot.bot_cmdqueuebuf, idx);
77 float bot_havecommand(entity bot, float idx)
79 if(!bot.bot_cmdqueuebuf_allocated)
81 if(idx < bot.bot_cmdqueuebuf_start)
83 if(idx >= bot.bot_cmdqueuebuf_end)
88 #define MAX_BOT_PLACES 4
89 .float bot_places_count;
90 .entity bot_places[MAX_BOT_PLACES];
91 .string bot_placenames[MAX_BOT_PLACES];
92 entity bot_getplace(string placename)
95 if(substring(placename, 0, 1) == "@")
98 placename = substring(placename, 1, -1);
100 for(i = 0; i < self.bot_places_count; ++i)
101 if(self.(bot_placenames[i]) == placename)
102 return self.(bot_places[i]);
103 // now: i == self.bot_places_count
104 s = s2 = cvar_string(placename);
105 p = strstrofs(s2, " ", 0);
108 s = substring(s2, 0, p);
109 //print("places: ", placename, " -> ", cvar_string(placename), "\n");
110 cvar_set(placename, strcat(substring(s2, p+1, -1), " ", s));
111 //print("places: ", placename, " := ", cvar_string(placename), "\n");
113 e = find(world, targetname, s);
115 print("invalid place ", s, "\n");
116 if(i < MAX_BOT_PLACES)
118 self.(bot_placenames[i]) = strzone(placename);
119 self.(bot_places[i]) = e;
120 self.bot_places_count += 1;
126 e = find(world, targetname, placename);
128 print("invalid place ", placename, "\n");
134 // NOTE: New commands should be added here. Do not forget to update BOT_CMD_COUNTER
135 #define BOT_CMD_NULL 0
136 #define BOT_CMD_PAUSE 1
137 #define BOT_CMD_CONTINUE 2
138 #define BOT_CMD_WAIT 3
139 #define BOT_CMD_TURN 4
140 #define BOT_CMD_MOVETO 5
141 #define BOT_CMD_RESETGOAL 6 // Not implemented yet
144 #define BOT_CMD_ELSE 9
145 #define BOT_CMD_FI 10
146 #define BOT_CMD_RESETAIM 11
147 #define BOT_CMD_AIM 12
148 #define BOT_CMD_PRESSKEY 13
149 #define BOT_CMD_RELEASEKEY 14
150 #define BOT_CMD_SELECTWEAPON 15
151 #define BOT_CMD_IMPULSE 16
152 #define BOT_CMD_WAIT_UNTIL 17
153 #define BOT_CMD_MOVETOTARGET 18
154 #define BOT_CMD_AIMTARGET 19
155 #define BOT_CMD_BARRIER 20
156 #define BOT_CMD_CONSOLE 21
157 #define BOT_CMD_SOUND 22
158 #define BOT_CMD_DEBUG_ASSERT_CANFIRE 23
159 #define BOT_CMD_WHILE 24 // TODO: Not implemented yet
160 #define BOT_CMD_WEND 25 // TODO: Not implemented yet
161 #define BOT_CMD_CHASE 26 // TODO: Not implemented yet
163 #define BOT_CMD_COUNTER 24 // Update this value if you add/remove a command
165 // NOTE: Following commands should be implemented on the bot ai
166 // If a new command should be handled by the target ai(s) please declare it here
167 .float(vector) cmd_moveto;
168 .float() cmd_resetgoal;
171 #define BOT_CMD_PARAMETER_NONE 0
172 #define BOT_CMD_PARAMETER_FLOAT 1
173 #define BOT_CMD_PARAMETER_STRING 2
174 #define BOT_CMD_PARAMETER_VECTOR 3
176 float bot_cmds_initialized;
177 float bot_cmd_parm_type[BOT_CMD_COUNTER];
178 string bot_cmd_string[BOT_CMD_COUNTER];
180 // Bots command queue
181 entity bot_cmd; // global current command
182 .entity bot_cmd_current; // current command of this bot
184 .float is_bot_cmd; // Tells if the entity is a bot command
185 .float bot_cmd_index; // Position of the command in the queue
186 .float bot_cmd_type; // If of command (see the BOT_CMD_* defines)
187 .float bot_cmd_parm_float; // Field to store a float parameter
188 .string bot_cmd_parm_string; // Field to store a string parameter
189 .vector bot_cmd_parm_vector; // Field to store a vector parameter
191 float bot_barriertime;
194 .float bot_cmd_execution_index; // Position in the queue of the command to be executed
196 // Initialize global commands list
197 // NOTE: New commands should be initialized here
198 void bot_commands_init()
200 bot_cmd_string[BOT_CMD_NULL] = "";
201 bot_cmd_parm_type[BOT_CMD_NULL] = BOT_CMD_PARAMETER_NONE;
203 bot_cmd_string[BOT_CMD_PAUSE] = "pause";
204 bot_cmd_parm_type[BOT_CMD_PAUSE] = BOT_CMD_PARAMETER_NONE;
206 bot_cmd_string[BOT_CMD_CONTINUE] = "continue";
207 bot_cmd_parm_type[BOT_CMD_CONTINUE] = BOT_CMD_PARAMETER_NONE;
209 bot_cmd_string[BOT_CMD_WAIT] = "wait";
210 bot_cmd_parm_type[BOT_CMD_WAIT] = BOT_CMD_PARAMETER_FLOAT;
212 bot_cmd_string[BOT_CMD_TURN] = "turn";
213 bot_cmd_parm_type[BOT_CMD_TURN] = BOT_CMD_PARAMETER_FLOAT;
215 bot_cmd_string[BOT_CMD_MOVETO] = "moveto";
216 bot_cmd_parm_type[BOT_CMD_MOVETO] = BOT_CMD_PARAMETER_VECTOR;
218 bot_cmd_string[BOT_CMD_MOVETOTARGET] = "movetotarget";
219 bot_cmd_parm_type[BOT_CMD_MOVETOTARGET] = BOT_CMD_PARAMETER_STRING;
221 bot_cmd_string[BOT_CMD_RESETGOAL] = "resetgoal";
222 bot_cmd_parm_type[BOT_CMD_RESETGOAL] = BOT_CMD_PARAMETER_NONE;
224 bot_cmd_string[BOT_CMD_CC] = "cc";
225 bot_cmd_parm_type[BOT_CMD_CC] = BOT_CMD_PARAMETER_STRING;
227 bot_cmd_string[BOT_CMD_IF] = "if";
228 bot_cmd_parm_type[BOT_CMD_IF] = BOT_CMD_PARAMETER_STRING;
230 bot_cmd_string[BOT_CMD_ELSE] = "else";
231 bot_cmd_parm_type[BOT_CMD_ELSE] = BOT_CMD_PARAMETER_NONE;
233 bot_cmd_string[BOT_CMD_FI] = "fi";
234 bot_cmd_parm_type[BOT_CMD_FI] = BOT_CMD_PARAMETER_NONE;
236 bot_cmd_string[BOT_CMD_RESETAIM] = "resetaim";
237 bot_cmd_parm_type[BOT_CMD_RESETAIM] = BOT_CMD_PARAMETER_NONE;
239 bot_cmd_string[BOT_CMD_AIM] = "aim";
240 bot_cmd_parm_type[BOT_CMD_AIM] = BOT_CMD_PARAMETER_STRING;
242 bot_cmd_string[BOT_CMD_AIMTARGET] = "aimtarget";
243 bot_cmd_parm_type[BOT_CMD_AIMTARGET] = BOT_CMD_PARAMETER_STRING;
245 bot_cmd_string[BOT_CMD_PRESSKEY] = "presskey";
246 bot_cmd_parm_type[BOT_CMD_PRESSKEY] = BOT_CMD_PARAMETER_STRING;
248 bot_cmd_string[BOT_CMD_RELEASEKEY] = "releasekey";
249 bot_cmd_parm_type[BOT_CMD_RELEASEKEY] = BOT_CMD_PARAMETER_STRING;
251 bot_cmd_string[BOT_CMD_SELECTWEAPON] = "selectweapon";
252 bot_cmd_parm_type[BOT_CMD_SELECTWEAPON] = BOT_CMD_PARAMETER_FLOAT;
254 bot_cmd_string[BOT_CMD_IMPULSE] = "impulse";
255 bot_cmd_parm_type[BOT_CMD_IMPULSE] = BOT_CMD_PARAMETER_FLOAT;
257 bot_cmd_string[BOT_CMD_WAIT_UNTIL] = "wait_until";
258 bot_cmd_parm_type[BOT_CMD_WAIT_UNTIL] = BOT_CMD_PARAMETER_FLOAT;
260 bot_cmd_string[BOT_CMD_BARRIER] = "barrier";
261 bot_cmd_parm_type[BOT_CMD_BARRIER] = BOT_CMD_PARAMETER_NONE;
263 bot_cmd_string[BOT_CMD_CONSOLE] = "console";
264 bot_cmd_parm_type[BOT_CMD_CONSOLE] = BOT_CMD_PARAMETER_STRING;
266 bot_cmd_string[BOT_CMD_SOUND] = "sound";
267 bot_cmd_parm_type[BOT_CMD_SOUND] = BOT_CMD_PARAMETER_STRING;
269 bot_cmd_string[BOT_CMD_DEBUG_ASSERT_CANFIRE] = "debug_assert_canfire";
270 bot_cmd_parm_type[BOT_CMD_DEBUG_ASSERT_CANFIRE] = BOT_CMD_PARAMETER_FLOAT;
272 bot_cmds_initialized = TRUE;
275 // Returns first bot with matching name
276 entity find_bot_by_name(string name)
280 bot = findchainflags(flags, FL_CLIENT);
283 if(clienttype(bot) == CLIENTTYPE_BOT)
284 if(bot.netname==name)
293 // Returns a bot by number on list
294 entity find_bot_by_number(float number)
302 bot = findchainflags(flags, FL_CLIENT);
305 if(clienttype(bot) == CLIENTTYPE_BOT)
316 float bot_decodecommand(string cmdstring)
318 float cmd_parm_type, i;
322 sp = strstrofs(cmdstring, " ", 0);
329 parm = substring(cmdstring, sp + 1, -1);
330 cmdstring = substring(cmdstring, 0, sp);
333 if(!bot_cmds_initialized)
336 for(i=1;i<BOT_CMD_COUNTER;++i)
338 if(bot_cmd_string[i]!=cmdstring)
341 cmd_parm_type = bot_cmd_parm_type[i];
343 if(cmd_parm_type!=BOT_CMD_PARAMETER_NONE&&parm=="")
345 print("ERROR: A parameter is required for this command\n");
349 // Load command into queue
350 bot_cmd.bot_cmd_type = i;
353 switch(cmd_parm_type)
355 case BOT_CMD_PARAMETER_FLOAT:
356 bot_cmd.bot_cmd_parm_float = stof(parm);
358 case BOT_CMD_PARAMETER_STRING:
359 if(bot_cmd.bot_cmd_parm_string)
360 strunzone(bot_cmd.bot_cmd_parm_string);
361 bot_cmd.bot_cmd_parm_string = strzone(parm);
363 case BOT_CMD_PARAMETER_VECTOR:
364 bot_cmd.bot_cmd_parm_vector = stov(parm);
371 print("ERROR: No such command '", cmdstring, "'\n");
375 void bot_cmdhelp(string scmd)
380 if(!bot_cmds_initialized)
383 for(i=1;i<BOT_CMD_COUNTER;++i)
385 if(bot_cmd_string[i]!=scmd)
388 ntype = bot_cmd_parm_type[i];
392 case BOT_CMD_PARAMETER_FLOAT:
393 stype = "float number";
395 case BOT_CMD_PARAMETER_STRING:
398 case BOT_CMD_PARAMETER_VECTOR:
406 print(strcat("Command: ",bot_cmd_string[i],"\nParameter: <",stype,"> \n"));
408 print("Description: ");
412 print("Stops the bot completely. Any command other than 'continue' will be ignored.");
414 case BOT_CMD_CONTINUE:
415 print("Disable paused status");
418 print("Pause command parsing and bot ai for N seconds. Pressed key will remain pressed");
420 case BOT_CMD_WAIT_UNTIL:
421 print("Pause command parsing and bot ai until time is N from the last barrier. Pressed key will remain pressed");
423 case BOT_CMD_BARRIER:
424 print("Waits till all bots that have a command queue reach this command. Pressed key will remain pressed");
427 print("Look to the right or left N degrees. For turning to the left use positive numbers.");
430 print("Walk to an specific coordinate on the map. Usage: moveto \"x y z\"");
432 case BOT_CMD_MOVETOTARGET:
433 print("Walk to the specific target on the map");
435 case BOT_CMD_RESETGOAL:
436 print("Resets the goal stack");
439 print("Execute client command. Examples: cc \"say something\"; cc god; cc \"name newnickname\"; cc kill;");
442 print("Perform simple conditional execution.\n");
444 print(" sv_cmd .. if \"condition\"\n");
445 print(" sv_cmd .. <instruction if true>\n");
446 print(" sv_cmd .. <instruction if true>\n");
447 print(" sv_cmd .. else\n");
448 print(" sv_cmd .. <instruction if false>\n");
449 print(" sv_cmd .. <instruction if false>\n");
450 print(" sv_cmd .. fi\n");
451 print("Conditions: a=b, a>b, a<b, a\t\t(spaces not allowed)\n");
452 print(" Values in conditions can be numbers, cvars in the form cvar.cvar_string or special fields\n");
453 print("Fields: health, speed, flagcarrier\n");
454 print("Examples: if health>50; if health>cvar.g_balance_laser_primary_damage; if flagcarrier;");
456 case BOT_CMD_RESETAIM:
457 print("Points the aim to the coordinates x,y 0,0");
460 print("Move the aim x/y (horizontal/vertical) degrees relatives to the bot\n");
461 print("There is a 3rd optional parameter telling in how many seconds the aim has to reach the new position\n");
462 print("Examples: aim \"90 0\" // Turn 90 degrees inmediately (positive numbers move to the left/up)\n");
463 print(" aim \"0 90 2\" // Will gradually look to the sky in the next two seconds");
465 case BOT_CMD_AIMTARGET:
466 print("Points the aim to given target");
468 case BOT_CMD_PRESSKEY:
469 print("Press one of the following keys: forward, backward, left, right, jump, crouch, attack1, attack2, use\n");
470 print("Multiple keys can be pressed at time (with many presskey calls) and it will remain pressed until the command \"releasekey\" is called");
471 print("Note: The script will not return the control to the bot ai until all keys are released");
473 case BOT_CMD_RELEASEKEY:
474 print("Release previoulsy used keys. Use the parameter \"all\" to release all keys");
477 print("play sound file at bot location");
479 case BOT_CMD_DEBUG_ASSERT_CANFIRE:
480 print("verify the state of the weapon entity");
483 print("This command has no description yet.");
490 void bot_list_commands()
495 if(!bot_cmds_initialized)
498 print("List of all available commands:\n");
499 print(" Command - Parameter Type\n");
501 for(i=1;i<BOT_CMD_COUNTER;++i)
503 switch(bot_cmd_parm_type[i])
505 case BOT_CMD_PARAMETER_FLOAT:
506 ptype = "float number";
508 case BOT_CMD_PARAMETER_STRING:
511 case BOT_CMD_PARAMETER_VECTOR:
518 print(strcat(" ",bot_cmd_string[i]," - <",ptype,"> \n"));
523 .float bot_exec_status;
525 #define BOT_EXEC_STATUS_IDLE 0
526 #define BOT_EXEC_STATUS_PAUSED 1
527 #define BOT_EXEC_STATUS_WAITING 2
529 #define CMD_STATUS_EXECUTING 0
530 #define CMD_STATUS_FINISHED 1
531 #define CMD_STATUS_ERROR 2
533 void SV_ParseClientCommand(string s);
536 SV_ParseClientCommand(bot_cmd.bot_cmd_parm_string);
537 return CMD_STATUS_FINISHED;
540 float bot_cmd_impulse()
542 self.impulse = bot_cmd.bot_cmd_parm_float;
543 return CMD_STATUS_FINISHED;
546 float bot_cmd_continue()
548 self.bot_exec_status &~= BOT_EXEC_STATUS_PAUSED;
549 return CMD_STATUS_FINISHED;
552 .float bot_cmd_wait_time;
555 if(self.bot_exec_status & BOT_EXEC_STATUS_WAITING)
557 if(time>=self.bot_cmd_wait_time)
559 self.bot_exec_status &~= BOT_EXEC_STATUS_WAITING;
560 return CMD_STATUS_FINISHED;
563 return CMD_STATUS_EXECUTING;
566 self.bot_cmd_wait_time = time + bot_cmd.bot_cmd_parm_float;
567 self.bot_exec_status |= BOT_EXEC_STATUS_WAITING;
568 return CMD_STATUS_EXECUTING;
571 float bot_cmd_wait_until()
573 if(time < bot_cmd.bot_cmd_parm_float + bot_barriertime)
575 self.bot_exec_status |= BOT_EXEC_STATUS_WAITING;
576 return CMD_STATUS_EXECUTING;
578 self.bot_exec_status &~= BOT_EXEC_STATUS_WAITING;
579 return CMD_STATUS_FINISHED;
582 float bot_cmd_barrier()
586 // 0 = no barrier, 1 = waiting, 2 = waiting finished
588 if(self.bot_barrier == 0) // initialization
590 self.bot_barrier = 1;
592 //self.colormod = '4 4 0';
595 if(self.bot_barrier == 1) // find other bots
597 FOR_EACH_CLIENT(cl) if(cl.isbot)
599 if(cl.bot_cmdqueuebuf_allocated)
600 if(cl.bot_barrier != 1)
601 return CMD_STATUS_EXECUTING; // not all are at the barrier yet
604 // all bots hit the barrier!
605 FOR_EACH_CLIENT(cl) if(cl.isbot)
607 cl.bot_barrier = 2; // acknowledge barrier
610 bot_barriertime = time;
613 // if we get here, the barrier is finished
615 self.bot_barrier = 0;
616 //self.colormod = '0 0 0';
618 return CMD_STATUS_FINISHED;
623 self.v_angle_y = self.v_angle_y + bot_cmd.bot_cmd_parm_float;
624 self.v_angle_y = self.v_angle_y - floor(self.v_angle_y / 360) * 360;
625 return CMD_STATUS_FINISHED;
628 float bot_cmd_select_weapon()
632 id = bot_cmd.bot_cmd_parm_float;
634 if(id < WEP_FIRST || id > WEP_LAST)
635 return CMD_STATUS_ERROR;
637 if(client_hasweapon(self, id, TRUE, FALSE))
638 self.switchweapon = id;
640 return CMD_STATUS_ERROR;
642 return CMD_STATUS_FINISHED;
645 .float bot_cmd_condition_status;
647 #define CMD_CONDITION_NONE 0
648 #define CMD_CONDITION_TRUE 1
649 #define CMD_CONDITION_FALSE 2
650 #define CMD_CONDITION_TRUE_BLOCK 4
651 #define CMD_CONDITION_FALSE_BLOCK 8
653 float bot_cmd_eval(string expr)
655 // Search for numbers
656 if(strstrofs("0123456789", substring(expr, 0, 1), 0) >= 0)
662 if(substring(expr, 0, 5)=="cvar.")
664 return cvar(substring(expr, 5, strlen(expr)));
673 return vlen(self.velocity);
675 return ((self.flagcarried!=world));
678 print(strcat("ERROR: Unable to convert the expression '",expr,"' into a numeric value\n"));
684 string expr, val_a, val_b;
687 if(self.bot_cmd_condition_status != CMD_CONDITION_NONE)
689 // Only one "if" block is allowed at time
690 print("ERROR: Only one conditional block can be processed at time");
691 bot_clearqueue(self);
692 return CMD_STATUS_ERROR;
695 self.bot_cmd_condition_status |= CMD_CONDITION_TRUE_BLOCK;
697 // search for operators
698 expr = bot_cmd.bot_cmd_parm_string;
700 cmpofs = strstrofs(expr,"=",0);
704 val_a = substring(expr,0,cmpofs);
705 val_b = substring(expr,cmpofs+1,strlen(expr));
707 if(bot_cmd_eval(val_a)==bot_cmd_eval(val_b))
708 self.bot_cmd_condition_status |= CMD_CONDITION_TRUE;
710 self.bot_cmd_condition_status |= CMD_CONDITION_FALSE;
712 return CMD_STATUS_FINISHED;
715 cmpofs = strstrofs(expr,">",0);
719 val_a = substring(expr,0,cmpofs);
720 val_b = substring(expr,cmpofs+1,strlen(expr));
722 if(bot_cmd_eval(val_a)>bot_cmd_eval(val_b))
723 self.bot_cmd_condition_status |= CMD_CONDITION_TRUE;
725 self.bot_cmd_condition_status |= CMD_CONDITION_FALSE;
727 return CMD_STATUS_FINISHED;
730 cmpofs = strstrofs(expr,"<",0);
734 val_a = substring(expr,0,cmpofs);
735 val_b = substring(expr,cmpofs+1,strlen(expr));
737 if(bot_cmd_eval(val_a)<bot_cmd_eval(val_b))
738 self.bot_cmd_condition_status |= CMD_CONDITION_TRUE;
740 self.bot_cmd_condition_status |= CMD_CONDITION_FALSE;
742 return CMD_STATUS_FINISHED;
745 if(bot_cmd_eval(expr))
746 self.bot_cmd_condition_status |= CMD_CONDITION_TRUE;
748 self.bot_cmd_condition_status |= CMD_CONDITION_FALSE;
750 return CMD_STATUS_FINISHED;
755 self.bot_cmd_condition_status &~= CMD_CONDITION_TRUE_BLOCK;
756 self.bot_cmd_condition_status |= CMD_CONDITION_FALSE_BLOCK;
757 return CMD_STATUS_FINISHED;
762 self.bot_cmd_condition_status = CMD_CONDITION_NONE;
763 return CMD_STATUS_FINISHED;
766 float bot_cmd_resetaim()
768 self.v_angle = '0 0 0';
769 return CMD_STATUS_FINISHED;
772 .float bot_cmd_aim_begintime;
773 .float bot_cmd_aim_endtime;
774 .vector bot_cmd_aim_begin;
775 .vector bot_cmd_aim_end;
780 if(self.bot_cmd_aim_endtime)
784 progress = min(1 - (self.bot_cmd_aim_endtime - time) / (self.bot_cmd_aim_endtime - self.bot_cmd_aim_begintime),1);
785 self.v_angle = self.bot_cmd_aim_begin + ((self.bot_cmd_aim_end - self.bot_cmd_aim_begin) * progress);
787 if(time>=self.bot_cmd_aim_endtime)
789 self.bot_cmd_aim_endtime = 0;
790 return CMD_STATUS_FINISHED;
793 return CMD_STATUS_EXECUTING;
796 // New aiming direction
800 parms = bot_cmd.bot_cmd_parm_string;
802 tokens = tokenizebyseparator(parms, " ");
804 if(tokens<2||tokens>3)
805 return CMD_STATUS_ERROR;
807 step = (tokens == 3) ? stof(argv(2)) : 0;
811 self.v_angle_x -= stof(argv(1));
812 self.v_angle_y += stof(argv(0));
813 return CMD_STATUS_FINISHED;
816 self.bot_cmd_aim_begin = self.v_angle;
818 self.bot_cmd_aim_end_x = self.v_angle_x - stof(argv(1));
819 self.bot_cmd_aim_end_y = self.v_angle_y + stof(argv(0));
820 self.bot_cmd_aim_end_z = 0;
822 self.bot_cmd_aim_begintime = time;
823 self.bot_cmd_aim_endtime = time + step;
825 return CMD_STATUS_EXECUTING;
828 float bot_cmd_aimtarget()
830 if(self.bot_cmd_aim_endtime)
832 return bot_cmd_aim();
840 parms = bot_cmd.bot_cmd_parm_string;
842 tokens = tokenizebyseparator(parms, " ");
844 e = bot_getplace(argv(0));
846 return CMD_STATUS_ERROR;
848 v = e.origin + (e.mins + e.maxs) * 0.5;
852 self.v_angle = vectoangles(v - (self.origin + self.view_ofs));
853 self.v_angle_x = -self.v_angle_x;
854 return CMD_STATUS_FINISHED;
857 if(tokens<1||tokens>2)
858 return CMD_STATUS_ERROR;
860 step = stof(argv(1));
862 self.bot_cmd_aim_begin = self.v_angle;
863 self.bot_cmd_aim_end = vectoangles(v - (self.origin + self.view_ofs));
864 self.bot_cmd_aim_end_x = -self.bot_cmd_aim_end_x;
866 self.bot_cmd_aim_begintime = time;
867 self.bot_cmd_aim_endtime = time + step;
869 return CMD_STATUS_EXECUTING;
874 #define BOT_CMD_KEY_NONE 0
875 #define BOT_CMD_KEY_FORWARD 1
876 #define BOT_CMD_KEY_BACKWARD 2
877 #define BOT_CMD_KEY_RIGHT 4
878 #define BOT_CMD_KEY_LEFT 8
879 #define BOT_CMD_KEY_JUMP 16
880 #define BOT_CMD_KEY_ATTACK1 32
881 #define BOT_CMD_KEY_ATTACK2 64
882 #define BOT_CMD_KEY_USE 128
883 #define BOT_CMD_KEY_HOOK 256
884 #define BOT_CMD_KEY_CROUCH 512
885 #define BOT_CMD_KEY_CHAT 1024
887 float bot_presskeys()
889 self.movement = '0 0 0';
890 self.BUTTON_JUMP = FALSE;
891 self.BUTTON_CROUCH = FALSE;
892 self.BUTTON_ATCK = FALSE;
893 self.BUTTON_ATCK2 = FALSE;
894 self.BUTTON_USE = FALSE;
895 self.BUTTON_HOOK = FALSE;
896 self.BUTTON_CHAT = FALSE;
898 if(self.bot_cmd_keys == BOT_CMD_KEY_NONE)
901 if(self.bot_cmd_keys & BOT_CMD_KEY_FORWARD)
902 self.movement_x = autocvar_sv_maxspeed;
903 else if(self.bot_cmd_keys & BOT_CMD_KEY_BACKWARD)
904 self.movement_x = -autocvar_sv_maxspeed;
906 if(self.bot_cmd_keys & BOT_CMD_KEY_RIGHT)
907 self.movement_y = autocvar_sv_maxspeed;
908 else if(self.bot_cmd_keys & BOT_CMD_KEY_LEFT)
909 self.movement_y = -autocvar_sv_maxspeed;
911 if(self.bot_cmd_keys & BOT_CMD_KEY_JUMP)
912 self.BUTTON_JUMP = TRUE;
914 if(self.bot_cmd_keys & BOT_CMD_KEY_CROUCH)
915 self.BUTTON_CROUCH = TRUE;
917 if(self.bot_cmd_keys & BOT_CMD_KEY_ATTACK1)
918 self.BUTTON_ATCK = TRUE;
920 if(self.bot_cmd_keys & BOT_CMD_KEY_ATTACK2)
921 self.BUTTON_ATCK2 = TRUE;
923 if(self.bot_cmd_keys & BOT_CMD_KEY_USE)
924 self.BUTTON_USE = TRUE;
926 if(self.bot_cmd_keys & BOT_CMD_KEY_HOOK)
927 self.BUTTON_HOOK = TRUE;
929 if(self.bot_cmd_keys & BOT_CMD_KEY_CHAT)
930 self.BUTTON_CHAT = TRUE;
936 float bot_cmd_keypress_handler(string key, float enabled)
942 self.bot_cmd_keys = power2of(20) - 1; // >:)
944 self.bot_cmd_keys = BOT_CMD_KEY_NONE;
948 self.bot_cmd_keys |= BOT_CMD_KEY_FORWARD;
949 self.bot_cmd_keys &~= BOT_CMD_KEY_BACKWARD;
952 self.bot_cmd_keys &~= BOT_CMD_KEY_FORWARD;
957 self.bot_cmd_keys |= BOT_CMD_KEY_BACKWARD;
958 self.bot_cmd_keys &~= BOT_CMD_KEY_FORWARD;
961 self.bot_cmd_keys &~= BOT_CMD_KEY_BACKWARD;
966 self.bot_cmd_keys |= BOT_CMD_KEY_LEFT;
967 self.bot_cmd_keys &~= BOT_CMD_KEY_RIGHT;
970 self.bot_cmd_keys &~= BOT_CMD_KEY_LEFT;
975 self.bot_cmd_keys |= BOT_CMD_KEY_RIGHT;
976 self.bot_cmd_keys &~= BOT_CMD_KEY_LEFT;
979 self.bot_cmd_keys &~= BOT_CMD_KEY_RIGHT;
983 self.bot_cmd_keys |= BOT_CMD_KEY_JUMP;
985 self.bot_cmd_keys &~= BOT_CMD_KEY_JUMP;
989 self.bot_cmd_keys |= BOT_CMD_KEY_CROUCH;
991 self.bot_cmd_keys &~= BOT_CMD_KEY_CROUCH;
995 self.bot_cmd_keys |= BOT_CMD_KEY_ATTACK1;
997 self.bot_cmd_keys &~= BOT_CMD_KEY_ATTACK1;
1001 self.bot_cmd_keys |= BOT_CMD_KEY_ATTACK2;
1003 self.bot_cmd_keys &~= BOT_CMD_KEY_ATTACK2;
1007 self.bot_cmd_keys |= BOT_CMD_KEY_USE;
1009 self.bot_cmd_keys &~= BOT_CMD_KEY_USE;
1013 self.bot_cmd_keys |= BOT_CMD_KEY_HOOK;
1015 self.bot_cmd_keys &~= BOT_CMD_KEY_HOOK;
1019 self.bot_cmd_keys |= BOT_CMD_KEY_CHAT;
1021 self.bot_cmd_keys &~= BOT_CMD_KEY_CHAT;
1027 return CMD_STATUS_FINISHED;
1030 float bot_cmd_presskey()
1034 key = bot_cmd.bot_cmd_parm_string;
1036 bot_cmd_keypress_handler(key,TRUE);
1038 return CMD_STATUS_FINISHED;
1041 float bot_cmd_releasekey()
1045 key = bot_cmd.bot_cmd_parm_string;
1047 return bot_cmd_keypress_handler(key,FALSE);
1050 float bot_cmd_pause()
1054 self.BUTTON_USE = 0;
1055 self.BUTTON_ATCK = 0;
1056 self.BUTTON_JUMP = 0;
1057 self.BUTTON_HOOK = 0;
1058 self.BUTTON_CHAT = 0;
1059 self.BUTTON_ATCK2 = 0;
1060 self.BUTTON_CROUCH = 0;
1062 self.movement = '0 0 0';
1063 self.bot_cmd_keys = BOT_CMD_KEY_NONE;
1065 self.bot_exec_status |= BOT_EXEC_STATUS_PAUSED;
1066 return CMD_STATUS_FINISHED;
1069 float bot_cmd_moveto()
1071 return self.cmd_moveto(bot_cmd.bot_cmd_parm_vector);
1074 float bot_cmd_movetotarget()
1077 e = bot_getplace(bot_cmd.bot_cmd_parm_string);
1079 return CMD_STATUS_ERROR;
1080 return self.cmd_moveto(e.origin + (e.mins + e.maxs) * 0.5);
1083 float bot_cmd_resetgoal()
1085 return self.cmd_resetgoal();
1089 float bot_cmd_sound()
1092 f = bot_cmd.bot_cmd_parm_string;
1095 sound(self, CH_WEAPON_B, f, VOL_BASE, ATTN_MIN);
1097 return CMD_STATUS_FINISHED;
1101 float bot_cmd_debug_assert_canfire()
1104 f = bot_cmd.bot_cmd_parm_float;
1106 if(self.weaponentity.state != WS_READY)
1110 self.colormod = '0 8 8';
1111 print("Bot ", self.netname, " using ", self.weaponname, " wants to fire, inhibited by weaponentity state\n");
1114 else if(ATTACK_FINISHED(self) > time)
1118 self.colormod = '8 0 8';
1119 print("Bot ", self.netname, " using ", self.weaponname, " wants to fire, inhibited by ATTACK_FINISHED (", ftos(ATTACK_FINISHED(self) - time), " seconds left)\n");
1122 else if(self.tuba_note)
1126 self.colormod = '8 0 0';
1127 print("Bot ", self.netname, " using ", self.weaponname, " wants to fire, bot still has an active tuba note\n");
1134 self.colormod = '8 8 0';
1135 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");
1139 return CMD_STATUS_FINISHED;
1144 void bot_command_executed(float rm)
1151 bot_dequeuecommand(self, self.bot_cmd_execution_index);
1153 self.bot_cmd_execution_index++;
1156 void bot_setcurrentcommand()
1160 if(!self.bot_cmd_current)
1162 self.bot_cmd_current = spawn();
1163 self.bot_cmd_current.classname = "bot_cmd";
1164 self.bot_cmd_current.is_bot_cmd = 1;
1167 bot_cmd = self.bot_cmd_current;
1168 if(bot_cmd.bot_cmd_index != self.bot_cmd_execution_index || self.bot_cmd_execution_index == 0)
1170 if(bot_havecommand(self, self.bot_cmd_execution_index))
1173 cmdstring = bot_readcommand(self, self.bot_cmd_execution_index);
1174 if(bot_decodecommand(cmdstring))
1176 bot_cmd.owner = self;
1177 bot_cmd.bot_cmd_index = self.bot_cmd_execution_index;
1181 // Invalid command, remove from queue
1183 bot_dequeuecommand(self, self.bot_cmd_execution_index);
1184 self.bot_cmd_execution_index++;
1192 void bot_resetqueues()
1197 FOR_EACH_CLIENT(cl) if(cl.isbot)
1199 cl.bot_cmd_execution_index = 0;
1200 if(cl.bot_cmdqueuebuf_allocated)
1202 // also, cancel all barriers
1204 for(i = 0; i < cl.bot_places_count; ++i)
1206 strunzone(cl.(bot_placenames[i]));
1207 cl.(bot_placenames[i]) = string_null;
1209 cl.bot_places_count = 0;
1212 bot_barriertime = time;
1215 // Here we map commands to functions and deal with complex interactions between commands and execution states
1216 // NOTE: Of course you need to include your commands here too :)
1217 float bot_execute_commands_once()
1219 float status, ispressingkey;
1222 bot_setcurrentcommand();
1224 // if we have no bot command, better return
1225 // old logic kept pressing previously pressed keys, but that has problems
1226 // (namely, it means you cannot make a bot "normal" ever again)
1227 // to keep a bot walking for a while, use the "wait" bot command
1228 if(bot_cmd == world)
1231 // Ignore all commands except continue when the bot is paused
1232 if(self.bot_exec_status & BOT_EXEC_STATUS_PAUSED)
1233 if(bot_cmd.bot_cmd_type!=BOT_CMD_CONTINUE)
1235 if(bot_cmd.bot_cmd_type!=BOT_CMD_NULL)
1237 bot_command_executed(TRUE);
1238 print( "WARNING: Commands are ignored while the bot is paused. Use the command 'continue' instead.\n");
1243 // Keep pressing keys raised by the "presskey" command
1244 ispressingkey = !!bot_presskeys();
1246 // Handle conditions
1247 if not(bot_cmd.bot_cmd_type==BOT_CMD_FI||bot_cmd.bot_cmd_type==BOT_CMD_ELSE)
1248 if(self.bot_cmd_condition_status & CMD_CONDITION_TRUE && self.bot_cmd_condition_status & CMD_CONDITION_FALSE_BLOCK)
1250 bot_command_executed(TRUE);
1253 else if(self.bot_cmd_condition_status & CMD_CONDITION_FALSE && self.bot_cmd_condition_status & CMD_CONDITION_TRUE_BLOCK)
1255 bot_command_executed(TRUE);
1259 // Map commands to functions
1260 switch(bot_cmd.bot_cmd_type)
1263 return ispressingkey;
1266 status = bot_cmd_pause();
1268 case BOT_CMD_CONTINUE:
1269 status = bot_cmd_continue();
1272 status = bot_cmd_wait();
1274 case BOT_CMD_WAIT_UNTIL:
1275 status = bot_cmd_wait_until();
1278 status = bot_cmd_turn();
1280 case BOT_CMD_MOVETO:
1281 status = bot_cmd_moveto();
1283 case BOT_CMD_MOVETOTARGET:
1284 status = bot_cmd_movetotarget();
1286 case BOT_CMD_RESETGOAL:
1287 status = bot_cmd_resetgoal();
1290 status = bot_cmd_cc();
1293 status = bot_cmd_if();
1296 status = bot_cmd_else();
1299 status = bot_cmd_fi();
1301 case BOT_CMD_RESETAIM:
1302 status = bot_cmd_resetaim();
1305 status = bot_cmd_aim();
1307 case BOT_CMD_AIMTARGET:
1308 status = bot_cmd_aimtarget();
1310 case BOT_CMD_PRESSKEY:
1311 status = bot_cmd_presskey();
1313 case BOT_CMD_RELEASEKEY:
1314 status = bot_cmd_releasekey();
1316 case BOT_CMD_SELECTWEAPON:
1317 status = bot_cmd_select_weapon();
1319 case BOT_CMD_IMPULSE:
1320 status = bot_cmd_impulse();
1322 case BOT_CMD_BARRIER:
1323 status = bot_cmd_barrier();
1325 case BOT_CMD_CONSOLE:
1326 localcmd(strcat(bot_cmd.bot_cmd_parm_string, "\n"));
1327 status = CMD_STATUS_FINISHED;
1330 status = bot_cmd_sound();
1332 case BOT_CMD_DEBUG_ASSERT_CANFIRE:
1333 status = bot_cmd_debug_assert_canfire();
1336 print(strcat("ERROR: Invalid command on queue with id '",ftos(bot_cmd.bot_cmd_type),"'\n"));
1340 if (status==CMD_STATUS_ERROR)
1341 print(strcat("ERROR: The command '",bot_cmd_string[bot_cmd.bot_cmd_type],"' returned an error status\n"));
1343 // Move execution pointer
1344 if(status==CMD_STATUS_EXECUTING)
1350 if(autocvar_g_debug_bot_commands)
1354 switch(bot_cmd_parm_type[bot_cmd.bot_cmd_type])
1356 case BOT_CMD_PARAMETER_FLOAT:
1357 parms = ftos(bot_cmd.bot_cmd_parm_float);
1359 case BOT_CMD_PARAMETER_STRING:
1360 parms = bot_cmd.bot_cmd_parm_string;
1362 case BOT_CMD_PARAMETER_VECTOR:
1363 parms = vtos(bot_cmd.bot_cmd_parm_vector);
1369 clientcommand(self,strcat("say ^7", bot_cmd_string[bot_cmd.bot_cmd_type]," ",parms,"\n"));
1372 bot_command_executed(TRUE);
1375 if(status == CMD_STATUS_FINISHED)
1378 return CMD_STATUS_ERROR;
1381 // This function should be (the only) called directly from the bot ai loop
1382 float bot_execute_commands()
1387 f = bot_execute_commands_once();