1 #include "scripting.qh"
6 .float bot_cmdqueuebuf_allocated;
7 .float bot_cmdqueuebuf;
8 .float bot_cmdqueuebuf_start;
9 .float bot_cmdqueuebuf_end;
11 void bot_clearqueue(entity bot)
13 if(!bot.bot_cmdqueuebuf_allocated)
15 buf_del(bot.bot_cmdqueuebuf);
16 bot.bot_cmdqueuebuf_allocated = false;
17 dprint("bot ", bot.netname, " queue cleared\n");
20 void bot_queuecommand(entity bot, string cmdstring)
22 if(!bot.bot_cmdqueuebuf_allocated)
24 bot.bot_cmdqueuebuf = buf_create();
25 bot.bot_cmdqueuebuf_allocated = true;
26 bot.bot_cmdqueuebuf_start = 0;
27 bot.bot_cmdqueuebuf_end = 0;
30 bufstr_set(bot.bot_cmdqueuebuf, bot.bot_cmdqueuebuf_end, cmdstring);
32 // if the command was a "sound" command, precache the sound NOW
33 // this prevents lagging!
39 sp = strstrofs(cmdstring, " ", 0);
42 parm = substring(cmdstring, sp + 1, -1);
43 cmdstr = substring(cmdstring, 0, sp);
49 sp = strstrofs(parm, " ", 0);
52 parm = substring(parm, sp + 1, -1);
59 bot.bot_cmdqueuebuf_end += 1;
62 void bot_dequeuecommand(entity bot, float idx)
64 if(!bot.bot_cmdqueuebuf_allocated)
65 error("dequeuecommand but no queue allocated");
66 if(idx < bot.bot_cmdqueuebuf_start)
67 error("dequeueing a command in the past");
68 if(idx >= bot.bot_cmdqueuebuf_end)
69 error("dequeueing a command in the future");
70 bufstr_set(bot.bot_cmdqueuebuf, idx, "");
71 if(idx == bot.bot_cmdqueuebuf_start)
72 bot.bot_cmdqueuebuf_start += 1;
73 if(bot.bot_cmdqueuebuf_start >= bot.bot_cmdqueuebuf_end)
77 string bot_readcommand(entity bot, float idx)
79 if(!bot.bot_cmdqueuebuf_allocated)
80 error("readcommand but no queue allocated");
81 if(idx < bot.bot_cmdqueuebuf_start)
82 error("reading a command in the past");
83 if(idx >= bot.bot_cmdqueuebuf_end)
84 error("reading a command in the future");
85 return bufstr_get(bot.bot_cmdqueuebuf, idx);
88 float bot_havecommand(entity bot, float idx)
90 if(!bot.bot_cmdqueuebuf_allocated)
92 if(idx < bot.bot_cmdqueuebuf_start)
94 if(idx >= bot.bot_cmdqueuebuf_end)
99 const int MAX_BOT_PLACES = 4;
100 .float bot_places_count;
101 .entity bot_places[MAX_BOT_PLACES];
102 .string bot_placenames[MAX_BOT_PLACES];
103 entity bot_getplace(string placename)
106 if(substring(placename, 0, 1) == "@")
109 placename = substring(placename, 1, -1);
111 for(i = 0; i < self.bot_places_count; ++i)
112 if(self.(bot_placenames[i]) == placename)
113 return self.(bot_places[i]);
114 // now: i == self.bot_places_count
115 s = s2 = cvar_string(placename);
116 p = strstrofs(s2, " ", 0);
119 s = substring(s2, 0, p);
120 //print("places: ", placename, " -> ", cvar_string(placename), "\n");
121 cvar_set(placename, strcat(substring(s2, p+1, -1), " ", s));
122 //print("places: ", placename, " := ", cvar_string(placename), "\n");
124 e = find(world, targetname, s);
126 print("invalid place ", s, "\n");
127 if(i < MAX_BOT_PLACES)
129 self.(bot_placenames[i]) = strzone(placename);
130 self.(bot_places[i]) = e;
131 self.bot_places_count += 1;
137 e = find(world, targetname, placename);
139 print("invalid place ", placename, "\n");
145 // Initialize global commands list
146 // NOTE: New commands should be initialized here
147 void bot_commands_init()
149 bot_cmd_string[BOT_CMD_NULL] = "";
150 bot_cmd_parm_type[BOT_CMD_NULL] = BOT_CMD_PARAMETER_NONE;
152 bot_cmd_string[BOT_CMD_PAUSE] = "pause";
153 bot_cmd_parm_type[BOT_CMD_PAUSE] = BOT_CMD_PARAMETER_NONE;
155 bot_cmd_string[BOT_CMD_CONTINUE] = "continue";
156 bot_cmd_parm_type[BOT_CMD_CONTINUE] = BOT_CMD_PARAMETER_NONE;
158 bot_cmd_string[BOT_CMD_WAIT] = "wait";
159 bot_cmd_parm_type[BOT_CMD_WAIT] = BOT_CMD_PARAMETER_FLOAT;
161 bot_cmd_string[BOT_CMD_TURN] = "turn";
162 bot_cmd_parm_type[BOT_CMD_TURN] = BOT_CMD_PARAMETER_FLOAT;
164 bot_cmd_string[BOT_CMD_MOVETO] = "moveto";
165 bot_cmd_parm_type[BOT_CMD_MOVETO] = BOT_CMD_PARAMETER_VECTOR;
167 bot_cmd_string[BOT_CMD_MOVETOTARGET] = "movetotarget";
168 bot_cmd_parm_type[BOT_CMD_MOVETOTARGET] = BOT_CMD_PARAMETER_STRING;
170 bot_cmd_string[BOT_CMD_RESETGOAL] = "resetgoal";
171 bot_cmd_parm_type[BOT_CMD_RESETGOAL] = BOT_CMD_PARAMETER_NONE;
173 bot_cmd_string[BOT_CMD_CC] = "cc";
174 bot_cmd_parm_type[BOT_CMD_CC] = BOT_CMD_PARAMETER_STRING;
176 bot_cmd_string[BOT_CMD_IF] = "if";
177 bot_cmd_parm_type[BOT_CMD_IF] = BOT_CMD_PARAMETER_STRING;
179 bot_cmd_string[BOT_CMD_ELSE] = "else";
180 bot_cmd_parm_type[BOT_CMD_ELSE] = BOT_CMD_PARAMETER_NONE;
182 bot_cmd_string[BOT_CMD_FI] = "fi";
183 bot_cmd_parm_type[BOT_CMD_FI] = BOT_CMD_PARAMETER_NONE;
185 bot_cmd_string[BOT_CMD_RESETAIM] = "resetaim";
186 bot_cmd_parm_type[BOT_CMD_RESETAIM] = BOT_CMD_PARAMETER_NONE;
188 bot_cmd_string[BOT_CMD_AIM] = "aim";
189 bot_cmd_parm_type[BOT_CMD_AIM] = BOT_CMD_PARAMETER_STRING;
191 bot_cmd_string[BOT_CMD_AIMTARGET] = "aimtarget";
192 bot_cmd_parm_type[BOT_CMD_AIMTARGET] = BOT_CMD_PARAMETER_STRING;
194 bot_cmd_string[BOT_CMD_PRESSKEY] = "presskey";
195 bot_cmd_parm_type[BOT_CMD_PRESSKEY] = BOT_CMD_PARAMETER_STRING;
197 bot_cmd_string[BOT_CMD_RELEASEKEY] = "releasekey";
198 bot_cmd_parm_type[BOT_CMD_RELEASEKEY] = BOT_CMD_PARAMETER_STRING;
200 bot_cmd_string[BOT_CMD_SELECTWEAPON] = "selectweapon";
201 bot_cmd_parm_type[BOT_CMD_SELECTWEAPON] = BOT_CMD_PARAMETER_FLOAT;
203 bot_cmd_string[BOT_CMD_IMPULSE] = "impulse";
204 bot_cmd_parm_type[BOT_CMD_IMPULSE] = BOT_CMD_PARAMETER_FLOAT;
206 bot_cmd_string[BOT_CMD_WAIT_UNTIL] = "wait_until";
207 bot_cmd_parm_type[BOT_CMD_WAIT_UNTIL] = BOT_CMD_PARAMETER_FLOAT;
209 bot_cmd_string[BOT_CMD_BARRIER] = "barrier";
210 bot_cmd_parm_type[BOT_CMD_BARRIER] = BOT_CMD_PARAMETER_NONE;
212 bot_cmd_string[BOT_CMD_CONSOLE] = "console";
213 bot_cmd_parm_type[BOT_CMD_CONSOLE] = BOT_CMD_PARAMETER_STRING;
215 bot_cmd_string[BOT_CMD_SOUND] = "sound";
216 bot_cmd_parm_type[BOT_CMD_SOUND] = BOT_CMD_PARAMETER_STRING;
218 bot_cmd_string[BOT_CMD_DEBUG_ASSERT_CANFIRE] = "debug_assert_canfire";
219 bot_cmd_parm_type[BOT_CMD_DEBUG_ASSERT_CANFIRE] = BOT_CMD_PARAMETER_FLOAT;
221 bot_cmds_initialized = true;
224 // Returns first bot with matching name
225 entity find_bot_by_name(string name)
229 bot = findchainflags(flags, FL_CLIENT);
232 if(IS_BOT_CLIENT(bot))
233 if(bot.netname==name)
242 // Returns a bot by number on list
243 entity find_bot_by_number(float number)
251 bot = findchainflags(flags, FL_CLIENT);
254 if(IS_BOT_CLIENT(bot))
265 float bot_decodecommand(string cmdstring)
271 sp = strstrofs(cmdstring, " ", 0);
278 parm = substring(cmdstring, sp + 1, -1);
279 cmdstring = substring(cmdstring, 0, sp);
282 if(!bot_cmds_initialized)
286 for(i=1;i<BOT_CMD_COUNTER;++i)
288 if(bot_cmd_string[i]!=cmdstring)
291 cmd_parm_type = bot_cmd_parm_type[i];
293 if(cmd_parm_type!=BOT_CMD_PARAMETER_NONE&&parm=="")
295 print("ERROR: A parameter is required for this command\n");
299 // Load command into queue
300 bot_cmd.bot_cmd_type = i;
303 switch(cmd_parm_type)
305 case BOT_CMD_PARAMETER_FLOAT:
306 bot_cmd.bot_cmd_parm_float = stof(parm);
308 case BOT_CMD_PARAMETER_STRING:
309 if(bot_cmd.bot_cmd_parm_string)
310 strunzone(bot_cmd.bot_cmd_parm_string);
311 bot_cmd.bot_cmd_parm_string = strzone(parm);
313 case BOT_CMD_PARAMETER_VECTOR:
314 bot_cmd.bot_cmd_parm_vector = stov(parm);
321 print("ERROR: No such command '", cmdstring, "'\n");
325 void bot_cmdhelp(string scmd)
330 if(!bot_cmds_initialized)
333 for(i=1;i<BOT_CMD_COUNTER;++i)
335 if(bot_cmd_string[i]!=scmd)
338 ntype = bot_cmd_parm_type[i];
342 case BOT_CMD_PARAMETER_FLOAT:
343 stype = "float number";
345 case BOT_CMD_PARAMETER_STRING:
348 case BOT_CMD_PARAMETER_VECTOR:
356 print(strcat("Command: ",bot_cmd_string[i],"\nParameter: <",stype,"> \n"));
358 print("Description: ");
362 print("Stops the bot completely. Any command other than 'continue' will be ignored.");
364 case BOT_CMD_CONTINUE:
365 print("Disable paused status");
368 print("Pause command parsing and bot ai for N seconds. Pressed key will remain pressed");
370 case BOT_CMD_WAIT_UNTIL:
371 print("Pause command parsing and bot ai until time is N from the last barrier. Pressed key will remain pressed");
373 case BOT_CMD_BARRIER:
374 print("Waits till all bots that have a command queue reach this command. Pressed key will remain pressed");
377 print("Look to the right or left N degrees. For turning to the left use positive numbers.");
380 print("Walk to an specific coordinate on the map. Usage: moveto \"x y z\"");
382 case BOT_CMD_MOVETOTARGET:
383 print("Walk to the specific target on the map");
385 case BOT_CMD_RESETGOAL:
386 print("Resets the goal stack");
389 print("Execute client command. Examples: cc \"say something\"; cc god; cc \"name newnickname\"; cc kill;");
392 print("Perform simple conditional execution.\n");
394 print(" sv_cmd .. if \"condition\"\n");
395 print(" sv_cmd .. <instruction if true>\n");
396 print(" sv_cmd .. <instruction if true>\n");
397 print(" sv_cmd .. else\n");
398 print(" sv_cmd .. <instruction if false>\n");
399 print(" sv_cmd .. <instruction if false>\n");
400 print(" sv_cmd .. fi\n");
401 print("Conditions: a=b, a>b, a<b, a\t\t(spaces not allowed)\n");
402 print(" Values in conditions can be numbers, cvars in the form cvar.cvar_string or special fields\n");
403 print("Fields: health, speed, flagcarrier\n");
404 print("Examples: if health>50; if health>cvar.g_balance_laser_primary_damage; if flagcarrier;");
406 case BOT_CMD_RESETAIM:
407 print("Points the aim to the coordinates x,y 0,0");
410 print("Move the aim x/y (horizontal/vertical) degrees relatives to the bot\n");
411 print("There is a 3rd optional parameter telling in how many seconds the aim has to reach the new position\n");
412 print("Examples: aim \"90 0\" // Turn 90 degrees inmediately (positive numbers move to the left/up)\n");
413 print(" aim \"0 90 2\" // Will gradually look to the sky in the next two seconds");
415 case BOT_CMD_AIMTARGET:
416 print("Points the aim to given target");
418 case BOT_CMD_PRESSKEY:
419 print("Press one of the following keys: forward, backward, left, right, jump, crouch, attack1, attack2, use\n");
420 print("Multiple keys can be pressed at time (with many presskey calls) and it will remain pressed until the command \"releasekey\" is called");
421 print("Note: The script will not return the control to the bot ai until all keys are released");
423 case BOT_CMD_RELEASEKEY:
424 print("Release previoulsy used keys. Use the parameter \"all\" to release all keys");
427 print("play sound file at bot location");
429 case BOT_CMD_DEBUG_ASSERT_CANFIRE:
430 print("verify the state of the weapon entity");
433 print("This command has no description yet.");
440 void bot_list_commands()
445 if(!bot_cmds_initialized)
448 print("List of all available commands:\n");
449 print(" Command - Parameter Type\n");
451 for(i=1;i<BOT_CMD_COUNTER;++i)
453 switch(bot_cmd_parm_type[i])
455 case BOT_CMD_PARAMETER_FLOAT:
456 ptype = "float number";
458 case BOT_CMD_PARAMETER_STRING:
461 case BOT_CMD_PARAMETER_VECTOR:
468 print(strcat(" ",bot_cmd_string[i]," - <",ptype,"> \n"));
473 .int bot_exec_status;
475 void SV_ParseClientCommand(string s);
478 SV_ParseClientCommand(bot_cmd.bot_cmd_parm_string);
479 return CMD_STATUS_FINISHED;
482 float bot_cmd_impulse()
484 self.impulse = bot_cmd.bot_cmd_parm_float;
485 return CMD_STATUS_FINISHED;
488 float bot_cmd_continue()
490 self.bot_exec_status &= ~BOT_EXEC_STATUS_PAUSED;
491 return CMD_STATUS_FINISHED;
494 .float bot_cmd_wait_time;
497 if(self.bot_exec_status & BOT_EXEC_STATUS_WAITING)
499 if(time>=self.bot_cmd_wait_time)
501 self.bot_exec_status &= ~BOT_EXEC_STATUS_WAITING;
502 return CMD_STATUS_FINISHED;
505 return CMD_STATUS_EXECUTING;
508 self.bot_cmd_wait_time = time + bot_cmd.bot_cmd_parm_float;
509 self.bot_exec_status |= BOT_EXEC_STATUS_WAITING;
510 return CMD_STATUS_EXECUTING;
513 float bot_cmd_wait_until()
515 if(time < bot_cmd.bot_cmd_parm_float + bot_barriertime)
517 self.bot_exec_status |= BOT_EXEC_STATUS_WAITING;
518 return CMD_STATUS_EXECUTING;
520 self.bot_exec_status &= ~BOT_EXEC_STATUS_WAITING;
521 return CMD_STATUS_FINISHED;
524 float bot_cmd_barrier()
528 // 0 = no barrier, 1 = waiting, 2 = waiting finished
530 if(self.bot_barrier == 0) // initialization
532 self.bot_barrier = 1;
534 //self.colormod = '4 4 0';
537 if(self.bot_barrier == 1) // find other bots
539 FOR_EACH_CLIENT(cl) if(cl.isbot)
541 if(cl.bot_cmdqueuebuf_allocated)
542 if(cl.bot_barrier != 1)
543 return CMD_STATUS_EXECUTING; // not all are at the barrier yet
546 // all bots hit the barrier!
547 FOR_EACH_CLIENT(cl) if(cl.isbot)
549 cl.bot_barrier = 2; // acknowledge barrier
552 bot_barriertime = time;
555 // if we get here, the barrier is finished
557 self.bot_barrier = 0;
558 //self.colormod = '0 0 0';
560 return CMD_STATUS_FINISHED;
565 self.v_angle_y = self.v_angle.y + bot_cmd.bot_cmd_parm_float;
566 self.v_angle_y = self.v_angle.y - floor(self.v_angle.y / 360) * 360;
567 return CMD_STATUS_FINISHED;
570 float bot_cmd_select_weapon()
574 id = bot_cmd.bot_cmd_parm_float;
576 if(id < WEP_FIRST || id > WEP_LAST)
577 return CMD_STATUS_ERROR;
579 if(client_hasweapon(self, id, true, false))
580 self.switchweapon = id;
582 return CMD_STATUS_ERROR;
584 return CMD_STATUS_FINISHED;
587 .int bot_cmd_condition_status;
589 const int CMD_CONDITION_NONE = 0;
590 const int CMD_CONDITION_true = 1;
591 const int CMD_CONDITION_false = 2;
592 const int CMD_CONDITION_true_BLOCK = 4;
593 const int CMD_CONDITION_false_BLOCK = 8;
595 float bot_cmd_eval(string expr)
597 // Search for numbers
598 if(strstrofs("0123456789", substring(expr, 0, 1), 0) >= 0)
604 if(substring(expr, 0, 5)=="cvar.")
606 return cvar(substring(expr, 5, strlen(expr)));
615 return vlen(self.velocity);
617 return ((self.flagcarried!=world));
620 print(strcat("ERROR: Unable to convert the expression '",expr,"' into a numeric value\n"));
626 string expr, val_a, val_b;
629 if(self.bot_cmd_condition_status != CMD_CONDITION_NONE)
631 // Only one "if" block is allowed at time
632 print("ERROR: Only one conditional block can be processed at time");
633 bot_clearqueue(self);
634 return CMD_STATUS_ERROR;
637 self.bot_cmd_condition_status |= CMD_CONDITION_true_BLOCK;
639 // search for operators
640 expr = bot_cmd.bot_cmd_parm_string;
642 cmpofs = strstrofs(expr,"=",0);
646 val_a = substring(expr,0,cmpofs);
647 val_b = substring(expr,cmpofs+1,strlen(expr));
649 if(bot_cmd_eval(val_a)==bot_cmd_eval(val_b))
650 self.bot_cmd_condition_status |= CMD_CONDITION_true;
652 self.bot_cmd_condition_status |= CMD_CONDITION_false;
654 return CMD_STATUS_FINISHED;
657 cmpofs = strstrofs(expr,">",0);
661 val_a = substring(expr,0,cmpofs);
662 val_b = substring(expr,cmpofs+1,strlen(expr));
664 if(bot_cmd_eval(val_a)>bot_cmd_eval(val_b))
665 self.bot_cmd_condition_status |= CMD_CONDITION_true;
667 self.bot_cmd_condition_status |= CMD_CONDITION_false;
669 return CMD_STATUS_FINISHED;
672 cmpofs = strstrofs(expr,"<",0);
676 val_a = substring(expr,0,cmpofs);
677 val_b = substring(expr,cmpofs+1,strlen(expr));
679 if(bot_cmd_eval(val_a)<bot_cmd_eval(val_b))
680 self.bot_cmd_condition_status |= CMD_CONDITION_true;
682 self.bot_cmd_condition_status |= CMD_CONDITION_false;
684 return CMD_STATUS_FINISHED;
687 if(bot_cmd_eval(expr))
688 self.bot_cmd_condition_status |= CMD_CONDITION_true;
690 self.bot_cmd_condition_status |= CMD_CONDITION_false;
692 return CMD_STATUS_FINISHED;
697 self.bot_cmd_condition_status &= ~CMD_CONDITION_true_BLOCK;
698 self.bot_cmd_condition_status |= CMD_CONDITION_false_BLOCK;
699 return CMD_STATUS_FINISHED;
704 self.bot_cmd_condition_status = CMD_CONDITION_NONE;
705 return CMD_STATUS_FINISHED;
708 float bot_cmd_resetaim()
710 self.v_angle = '0 0 0';
711 return CMD_STATUS_FINISHED;
714 .float bot_cmd_aim_begintime;
715 .float bot_cmd_aim_endtime;
716 .vector bot_cmd_aim_begin;
717 .vector bot_cmd_aim_end;
722 if(self.bot_cmd_aim_endtime)
726 progress = min(1 - (self.bot_cmd_aim_endtime - time) / (self.bot_cmd_aim_endtime - self.bot_cmd_aim_begintime),1);
727 self.v_angle = self.bot_cmd_aim_begin + ((self.bot_cmd_aim_end - self.bot_cmd_aim_begin) * progress);
729 if(time>=self.bot_cmd_aim_endtime)
731 self.bot_cmd_aim_endtime = 0;
732 return CMD_STATUS_FINISHED;
735 return CMD_STATUS_EXECUTING;
738 // New aiming direction
742 parms = bot_cmd.bot_cmd_parm_string;
744 tokens = tokenizebyseparator(parms, " ");
746 if(tokens<2||tokens>3)
747 return CMD_STATUS_ERROR;
749 step = (tokens == 3) ? stof(argv(2)) : 0;
753 self.v_angle_x -= stof(argv(1));
754 self.v_angle_y += stof(argv(0));
755 return CMD_STATUS_FINISHED;
758 self.bot_cmd_aim_begin = self.v_angle;
760 self.bot_cmd_aim_end_x = self.v_angle.x - stof(argv(1));
761 self.bot_cmd_aim_end_y = self.v_angle.y + stof(argv(0));
762 self.bot_cmd_aim_end_z = 0;
764 self.bot_cmd_aim_begintime = time;
765 self.bot_cmd_aim_endtime = time + step;
767 return CMD_STATUS_EXECUTING;
770 float bot_cmd_aimtarget()
772 if(self.bot_cmd_aim_endtime)
774 return bot_cmd_aim();
782 parms = bot_cmd.bot_cmd_parm_string;
784 tokens = tokenizebyseparator(parms, " ");
786 e = bot_getplace(argv(0));
788 return CMD_STATUS_ERROR;
790 v = e.origin + (e.mins + e.maxs) * 0.5;
794 self.v_angle = vectoangles(v - (self.origin + self.view_ofs));
795 self.v_angle_x = -self.v_angle.x;
796 return CMD_STATUS_FINISHED;
799 if(tokens<1||tokens>2)
800 return CMD_STATUS_ERROR;
802 step = stof(argv(1));
804 self.bot_cmd_aim_begin = self.v_angle;
805 self.bot_cmd_aim_end = vectoangles(v - (self.origin + self.view_ofs));
806 self.bot_cmd_aim_end_x = -self.bot_cmd_aim_end.x;
808 self.bot_cmd_aim_begintime = time;
809 self.bot_cmd_aim_endtime = time + step;
811 return CMD_STATUS_EXECUTING;
816 const int BOT_CMD_KEY_NONE = 0;
817 const int BOT_CMD_KEY_FORWARD = 1;
818 const int BOT_CMD_KEY_BACKWARD = 2;
819 const int BOT_CMD_KEY_RIGHT = 4;
820 const int BOT_CMD_KEY_LEFT = 8;
821 const int BOT_CMD_KEY_JUMP = 16;
822 const int BOT_CMD_KEY_ATTACK1 = 32;
823 const int BOT_CMD_KEY_ATTACK2 = 64;
824 const int BOT_CMD_KEY_USE = 128;
825 const int BOT_CMD_KEY_HOOK = 256;
826 const int BOT_CMD_KEY_CROUCH = 512;
827 const int BOT_CMD_KEY_CHAT = 1024;
829 float bot_presskeys()
831 self.movement = '0 0 0';
832 self.BUTTON_JUMP = false;
833 self.BUTTON_CROUCH = false;
834 self.BUTTON_ATCK = false;
835 self.BUTTON_ATCK2 = false;
836 self.BUTTON_USE = false;
837 self.BUTTON_HOOK = false;
838 self.BUTTON_CHAT = false;
840 if(self.bot_cmd_keys == BOT_CMD_KEY_NONE)
843 if(self.bot_cmd_keys & BOT_CMD_KEY_FORWARD)
844 self.movement_x = autocvar_sv_maxspeed;
845 else if(self.bot_cmd_keys & BOT_CMD_KEY_BACKWARD)
846 self.movement_x = -autocvar_sv_maxspeed;
848 if(self.bot_cmd_keys & BOT_CMD_KEY_RIGHT)
849 self.movement_y = autocvar_sv_maxspeed;
850 else if(self.bot_cmd_keys & BOT_CMD_KEY_LEFT)
851 self.movement_y = -autocvar_sv_maxspeed;
853 if(self.bot_cmd_keys & BOT_CMD_KEY_JUMP)
854 self.BUTTON_JUMP = true;
856 if(self.bot_cmd_keys & BOT_CMD_KEY_CROUCH)
857 self.BUTTON_CROUCH = true;
859 if(self.bot_cmd_keys & BOT_CMD_KEY_ATTACK1)
860 self.BUTTON_ATCK = true;
862 if(self.bot_cmd_keys & BOT_CMD_KEY_ATTACK2)
863 self.BUTTON_ATCK2 = true;
865 if(self.bot_cmd_keys & BOT_CMD_KEY_USE)
866 self.BUTTON_USE = true;
868 if(self.bot_cmd_keys & BOT_CMD_KEY_HOOK)
869 self.BUTTON_HOOK = true;
871 if(self.bot_cmd_keys & BOT_CMD_KEY_CHAT)
872 self.BUTTON_CHAT = true;
878 float bot_cmd_keypress_handler(string key, float enabled)
884 self.bot_cmd_keys = power2of(20) - 1; // >:)
886 self.bot_cmd_keys = BOT_CMD_KEY_NONE;
890 self.bot_cmd_keys |= BOT_CMD_KEY_FORWARD;
891 self.bot_cmd_keys &= ~BOT_CMD_KEY_BACKWARD;
894 self.bot_cmd_keys &= ~BOT_CMD_KEY_FORWARD;
899 self.bot_cmd_keys |= BOT_CMD_KEY_BACKWARD;
900 self.bot_cmd_keys &= ~BOT_CMD_KEY_FORWARD;
903 self.bot_cmd_keys &= ~BOT_CMD_KEY_BACKWARD;
908 self.bot_cmd_keys |= BOT_CMD_KEY_LEFT;
909 self.bot_cmd_keys &= ~BOT_CMD_KEY_RIGHT;
912 self.bot_cmd_keys &= ~BOT_CMD_KEY_LEFT;
917 self.bot_cmd_keys |= BOT_CMD_KEY_RIGHT;
918 self.bot_cmd_keys &= ~BOT_CMD_KEY_LEFT;
921 self.bot_cmd_keys &= ~BOT_CMD_KEY_RIGHT;
925 self.bot_cmd_keys |= BOT_CMD_KEY_JUMP;
927 self.bot_cmd_keys &= ~BOT_CMD_KEY_JUMP;
931 self.bot_cmd_keys |= BOT_CMD_KEY_CROUCH;
933 self.bot_cmd_keys &= ~BOT_CMD_KEY_CROUCH;
937 self.bot_cmd_keys |= BOT_CMD_KEY_ATTACK1;
939 self.bot_cmd_keys &= ~BOT_CMD_KEY_ATTACK1;
943 self.bot_cmd_keys |= BOT_CMD_KEY_ATTACK2;
945 self.bot_cmd_keys &= ~BOT_CMD_KEY_ATTACK2;
949 self.bot_cmd_keys |= BOT_CMD_KEY_USE;
951 self.bot_cmd_keys &= ~BOT_CMD_KEY_USE;
955 self.bot_cmd_keys |= BOT_CMD_KEY_HOOK;
957 self.bot_cmd_keys &= ~BOT_CMD_KEY_HOOK;
961 self.bot_cmd_keys |= BOT_CMD_KEY_CHAT;
963 self.bot_cmd_keys &= ~BOT_CMD_KEY_CHAT;
969 return CMD_STATUS_FINISHED;
972 float bot_cmd_presskey()
976 key = bot_cmd.bot_cmd_parm_string;
978 bot_cmd_keypress_handler(key,true);
980 return CMD_STATUS_FINISHED;
983 float bot_cmd_releasekey()
987 key = bot_cmd.bot_cmd_parm_string;
989 return bot_cmd_keypress_handler(key,false);
992 float bot_cmd_pause()
997 self.BUTTON_ATCK = 0;
998 self.BUTTON_JUMP = 0;
999 self.BUTTON_HOOK = 0;
1000 self.BUTTON_CHAT = 0;
1001 self.BUTTON_ATCK2 = 0;
1002 self.BUTTON_CROUCH = 0;
1004 self.movement = '0 0 0';
1005 self.bot_cmd_keys = BOT_CMD_KEY_NONE;
1007 self.bot_exec_status |= BOT_EXEC_STATUS_PAUSED;
1008 return CMD_STATUS_FINISHED;
1011 float bot_cmd_moveto()
1013 return self.cmd_moveto(bot_cmd.bot_cmd_parm_vector);
1016 float bot_cmd_movetotarget()
1019 e = bot_getplace(bot_cmd.bot_cmd_parm_string);
1021 return CMD_STATUS_ERROR;
1022 return self.cmd_moveto(e.origin + (e.mins + e.maxs) * 0.5);
1025 float bot_cmd_resetgoal()
1027 return self.cmd_resetgoal();
1031 float bot_cmd_sound()
1034 f = bot_cmd.bot_cmd_parm_string;
1036 float n = tokenizebyseparator(f, " ");
1039 float chan = CH_WEAPON_B;
1040 float vol = VOL_BASE;
1041 float atten = ATTEN_MIN;
1044 sample = argv(n - 1);
1046 chan = stof(argv(0));
1048 vol = stof(argv(1));
1050 atten = stof(argv(2));
1053 sound(self, chan, sample, vol, atten);
1055 return CMD_STATUS_FINISHED;
1059 float bot_cmd_debug_assert_canfire()
1062 f = bot_cmd.bot_cmd_parm_float;
1064 if(self.weaponentity.state != WS_READY)
1068 self.colormod = '0 8 8';
1069 print("Bot ", self.netname, " using ", self.weaponname, " wants to fire, inhibited by weaponentity state\n");
1072 else if(ATTACK_FINISHED(self) > time)
1076 self.colormod = '8 0 8';
1077 print("Bot ", self.netname, " using ", self.weaponname, " wants to fire, inhibited by ATTACK_FINISHED (", ftos(ATTACK_FINISHED(self) - time), " seconds left)\n");
1080 else if(self.tuba_note)
1084 self.colormod = '8 0 0';
1085 print("Bot ", self.netname, " using ", self.weaponname, " wants to fire, bot still has an active tuba note\n");
1092 self.colormod = '8 8 0';
1093 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");
1097 return CMD_STATUS_FINISHED;
1102 void bot_command_executed(float rm)
1109 bot_dequeuecommand(self, self.bot_cmd_execution_index);
1111 self.bot_cmd_execution_index++;
1114 void bot_setcurrentcommand()
1118 if(!self.bot_cmd_current)
1120 self.bot_cmd_current = spawn();
1121 self.bot_cmd_current.classname = "bot_cmd";
1122 self.bot_cmd_current.is_bot_cmd = 1;
1125 bot_cmd = self.bot_cmd_current;
1126 if(bot_cmd.bot_cmd_index != self.bot_cmd_execution_index || self.bot_cmd_execution_index == 0)
1128 if(bot_havecommand(self, self.bot_cmd_execution_index))
1131 cmdstring = bot_readcommand(self, self.bot_cmd_execution_index);
1132 if(bot_decodecommand(cmdstring))
1134 bot_cmd.owner = self;
1135 bot_cmd.bot_cmd_index = self.bot_cmd_execution_index;
1139 // Invalid command, remove from queue
1141 bot_dequeuecommand(self, self.bot_cmd_execution_index);
1142 self.bot_cmd_execution_index++;
1150 void bot_resetqueues()
1154 FOR_EACH_CLIENT(cl) if(cl.isbot)
1156 cl.bot_cmd_execution_index = 0;
1158 // also, cancel all barriers
1160 for(int i = 0; i < cl.bot_places_count; ++i)
1162 strunzone(cl.(bot_placenames[i]));
1163 cl.(bot_placenames[i]) = string_null;
1165 cl.bot_places_count = 0;
1168 bot_barriertime = time;
1171 // Here we map commands to functions and deal with complex interactions between commands and execution states
1172 // NOTE: Of course you need to include your commands here too :)
1173 float bot_execute_commands_once()
1175 float status, ispressingkey;
1178 bot_setcurrentcommand();
1180 // if we have no bot command, better return
1181 // old logic kept pressing previously pressed keys, but that has problems
1182 // (namely, it means you cannot make a bot "normal" ever again)
1183 // to keep a bot walking for a while, use the "wait" bot command
1184 if(bot_cmd == world)
1187 // Ignore all commands except continue when the bot is paused
1188 if(self.bot_exec_status & BOT_EXEC_STATUS_PAUSED)
1189 if(bot_cmd.bot_cmd_type!=BOT_CMD_CONTINUE)
1191 if(bot_cmd.bot_cmd_type!=BOT_CMD_NULL)
1193 bot_command_executed(true);
1194 print( "WARNING: Commands are ignored while the bot is paused. Use the command 'continue' instead.\n");
1199 // Keep pressing keys raised by the "presskey" command
1200 ispressingkey = !!bot_presskeys();
1202 // Handle conditions
1203 if (!(bot_cmd.bot_cmd_type==BOT_CMD_FI||bot_cmd.bot_cmd_type==BOT_CMD_ELSE))
1204 if(self.bot_cmd_condition_status & CMD_CONDITION_true && self.bot_cmd_condition_status & CMD_CONDITION_false_BLOCK)
1206 bot_command_executed(true);
1209 else if(self.bot_cmd_condition_status & CMD_CONDITION_false && self.bot_cmd_condition_status & CMD_CONDITION_true_BLOCK)
1211 bot_command_executed(true);
1215 // Map commands to functions
1216 switch(bot_cmd.bot_cmd_type)
1219 return ispressingkey;
1222 status = bot_cmd_pause();
1224 case BOT_CMD_CONTINUE:
1225 status = bot_cmd_continue();
1228 status = bot_cmd_wait();
1230 case BOT_CMD_WAIT_UNTIL:
1231 status = bot_cmd_wait_until();
1234 status = bot_cmd_turn();
1236 case BOT_CMD_MOVETO:
1237 status = bot_cmd_moveto();
1239 case BOT_CMD_MOVETOTARGET:
1240 status = bot_cmd_movetotarget();
1242 case BOT_CMD_RESETGOAL:
1243 status = bot_cmd_resetgoal();
1246 status = bot_cmd_cc();
1249 status = bot_cmd_if();
1252 status = bot_cmd_else();
1255 status = bot_cmd_fi();
1257 case BOT_CMD_RESETAIM:
1258 status = bot_cmd_resetaim();
1261 status = bot_cmd_aim();
1263 case BOT_CMD_AIMTARGET:
1264 status = bot_cmd_aimtarget();
1266 case BOT_CMD_PRESSKEY:
1267 status = bot_cmd_presskey();
1269 case BOT_CMD_RELEASEKEY:
1270 status = bot_cmd_releasekey();
1272 case BOT_CMD_SELECTWEAPON:
1273 status = bot_cmd_select_weapon();
1275 case BOT_CMD_IMPULSE:
1276 status = bot_cmd_impulse();
1278 case BOT_CMD_BARRIER:
1279 status = bot_cmd_barrier();
1281 case BOT_CMD_CONSOLE:
1282 localcmd(strcat(bot_cmd.bot_cmd_parm_string, "\n"));
1283 status = CMD_STATUS_FINISHED;
1286 status = bot_cmd_sound();
1288 case BOT_CMD_DEBUG_ASSERT_CANFIRE:
1289 status = bot_cmd_debug_assert_canfire();
1292 print(strcat("ERROR: Invalid command on queue with id '",ftos(bot_cmd.bot_cmd_type),"'\n"));
1296 if (status==CMD_STATUS_ERROR)
1297 print(strcat("ERROR: The command '",bot_cmd_string[bot_cmd.bot_cmd_type],"' returned an error status\n"));
1299 // Move execution pointer
1300 if(status==CMD_STATUS_EXECUTING)
1306 if(autocvar_g_debug_bot_commands)
1310 switch(bot_cmd_parm_type[bot_cmd.bot_cmd_type])
1312 case BOT_CMD_PARAMETER_FLOAT:
1313 parms = ftos(bot_cmd.bot_cmd_parm_float);
1315 case BOT_CMD_PARAMETER_STRING:
1316 parms = bot_cmd.bot_cmd_parm_string;
1318 case BOT_CMD_PARAMETER_VECTOR:
1319 parms = vtos(bot_cmd.bot_cmd_parm_vector);
1325 clientcommand(self,strcat("say ^7", bot_cmd_string[bot_cmd.bot_cmd_type]," ",parms,"\n"));
1328 bot_command_executed(true);
1331 if(status == CMD_STATUS_FINISHED)
1334 return CMD_STATUS_ERROR;
1337 // This function should be (the only) called directly from the bot ai loop
1338 float bot_execute_commands()
1343 f = bot_execute_commands_once();