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;
23 bufstr_set(bot.bot_cmdqueuebuf, bot.bot_cmdqueuebuf_end, cmdstring);
25 // if the command was a "sound" command, precache the sound NOW
26 // this prevents lagging!
32 sp = strstrofs(cmdstr, " ", 0);
39 parm = substring(cmdstr, sp + 1, -1);
40 cmdstr = substring(cmdstr, 0, sp);
43 precache_sound(cmdstr);
46 bot.bot_cmdqueuebuf_end += 1;
49 void bot_dequeuecommand(entity bot, float idx)
51 if(!bot.bot_cmdqueuebuf_allocated)
52 error("dequeuecommand but no queue allocated");
53 if(idx < bot.bot_cmdqueuebuf_start)
54 error("dequeueing a command in the past");
55 if(idx >= bot.bot_cmdqueuebuf_end)
56 error("dequeueing a command in the future");
57 bufstr_set(bot.bot_cmdqueuebuf, idx, "");
58 if(idx == bot.bot_cmdqueuebuf_start)
59 bot.bot_cmdqueuebuf_start += 1;
60 if(bot.bot_cmdqueuebuf_start >= bot.bot_cmdqueuebuf_end)
64 string bot_readcommand(entity bot, float idx)
66 if(!bot.bot_cmdqueuebuf_allocated)
67 error("readcommand but no queue allocated");
68 if(idx < bot.bot_cmdqueuebuf_start)
69 error("reading a command in the past");
70 if(idx >= bot.bot_cmdqueuebuf_end)
71 error("reading a command in the future");
72 return bufstr_get(bot.bot_cmdqueuebuf, idx);
75 float bot_havecommand(entity bot, float idx)
77 if(!bot.bot_cmdqueuebuf_allocated)
79 if(idx < bot.bot_cmdqueuebuf_start)
81 if(idx >= bot.bot_cmdqueuebuf_end)
86 #define MAX_BOT_PLACES 4
87 .float bot_places_count;
88 .entity bot_places[MAX_BOT_PLACES];
89 .string bot_placenames[MAX_BOT_PLACES];
90 entity bot_getplace(string placename)
93 if(substring(placename, 0, 1) == "@")
96 placename = substring(placename, 1, -1);
98 for(i = 0; i < self.bot_places_count; ++i)
99 if(self.(bot_placenames[i]) == placename)
100 return self.(bot_places[i]);
101 // now: i == self.bot_places_count
102 s = s2 = cvar_string(placename);
103 p = strstrofs(s2, " ", 0);
106 s = substring(s2, 0, p);
107 //print("places: ", placename, " -> ", cvar_string(placename), "\n");
108 cvar_set(placename, strcat(substring(s2, p+1, -1), " ", s));
109 //print("places: ", placename, " := ", cvar_string(placename), "\n");
111 e = find(world, targetname, s);
113 print("invalid place ", s, "\n");
114 if(i < MAX_BOT_PLACES)
116 self.(bot_placenames[i]) = strzone(placename);
117 self.(bot_places[i]) = e;
118 self.bot_places_count += 1;
124 e = find(world, targetname, placename);
126 print("invalid place ", placename, "\n");
132 // NOTE: New commands should be added here. Do not forget to update BOT_CMD_COUNTER
133 #define BOT_CMD_NULL 0
134 #define BOT_CMD_PAUSE 1
135 #define BOT_CMD_CONTINUE 2
136 #define BOT_CMD_WAIT 3
137 #define BOT_CMD_TURN 4
138 #define BOT_CMD_MOVETO 5
139 #define BOT_CMD_RESETGOAL 6 // Not implemented yet
142 #define BOT_CMD_ELSE 9
143 #define BOT_CMD_FI 10
144 #define BOT_CMD_RESETAIM 11
145 #define BOT_CMD_AIM 12
146 #define BOT_CMD_PRESSKEY 13
147 #define BOT_CMD_RELEASEKEY 14
148 #define BOT_CMD_SELECTWEAPON 15
149 #define BOT_CMD_IMPULSE 16
150 #define BOT_CMD_WAIT_UNTIL 17
151 #define BOT_CMD_MOVETOTARGET 18
152 #define BOT_CMD_AIMTARGET 19
153 #define BOT_CMD_BARRIER 20
154 #define BOT_CMD_CONSOLE 21
155 #define BOT_CMD_SOUND 22
156 #define BOT_CMD_WHILE 23 // TODO: Not implemented yet
157 #define BOT_CMD_WEND 24 // TODO: Not implemented yet
158 #define BOT_CMD_CHASE 25 // TODO: Not implemented yet
160 #define BOT_CMD_COUNTER 23 // Update this value if you add/remove a command
162 // NOTE: Following commands should be implemented on the bot ai
163 // If a new command should be handled by the target ai(s) please declare it here
164 .float(vector) cmd_moveto;
165 .float() cmd_resetgoal;
168 #define BOT_CMD_PARAMETER_NONE 0
169 #define BOT_CMD_PARAMETER_FLOAT 1
170 #define BOT_CMD_PARAMETER_STRING 2
171 #define BOT_CMD_PARAMETER_VECTOR 3
173 float bot_cmds_initialized;
174 float bot_cmd_parm_type[BOT_CMD_COUNTER];
175 string bot_cmd_string[BOT_CMD_COUNTER];
177 // Bots command queue
178 entity bot_cmd; // global current command
179 .entity bot_cmd_current; // current command of this bot
181 .float is_bot_cmd; // Tells if the entity is a bot command
182 .float bot_cmd_index; // Position of the command in the queue
183 .float bot_cmd_type; // If of command (see the BOT_CMD_* defines)
184 .float bot_cmd_parm_float; // Field to store a float parameter
185 .string bot_cmd_parm_string; // Field to store a string parameter
186 .vector bot_cmd_parm_vector; // Field to store a vector parameter
188 float bot_barriertime;
191 .float bot_cmd_execution_index; // Position in the queue of the command to be executed
193 // Initialize global commands list
194 // NOTE: New commands should be initialized here
195 void bot_commands_init()
197 bot_cmd_string[BOT_CMD_NULL] = "";
198 bot_cmd_parm_type[BOT_CMD_NULL] = BOT_CMD_PARAMETER_NONE;
200 bot_cmd_string[BOT_CMD_PAUSE] = "pause";
201 bot_cmd_parm_type[BOT_CMD_PAUSE] = BOT_CMD_PARAMETER_NONE;
203 bot_cmd_string[BOT_CMD_CONTINUE] = "continue";
204 bot_cmd_parm_type[BOT_CMD_CONTINUE] = BOT_CMD_PARAMETER_NONE;
206 bot_cmd_string[BOT_CMD_WAIT] = "wait";
207 bot_cmd_parm_type[BOT_CMD_WAIT] = BOT_CMD_PARAMETER_FLOAT;
209 bot_cmd_string[BOT_CMD_TURN] = "turn";
210 bot_cmd_parm_type[BOT_CMD_TURN] = BOT_CMD_PARAMETER_FLOAT;
212 bot_cmd_string[BOT_CMD_MOVETO] = "moveto";
213 bot_cmd_parm_type[BOT_CMD_MOVETO] = BOT_CMD_PARAMETER_VECTOR;
215 bot_cmd_string[BOT_CMD_MOVETOTARGET] = "movetotarget";
216 bot_cmd_parm_type[BOT_CMD_MOVETOTARGET] = BOT_CMD_PARAMETER_STRING;
218 bot_cmd_string[BOT_CMD_RESETGOAL] = "resetgoal";
219 bot_cmd_parm_type[BOT_CMD_RESETGOAL] = BOT_CMD_PARAMETER_NONE;
221 bot_cmd_string[BOT_CMD_CC] = "cc";
222 bot_cmd_parm_type[BOT_CMD_CC] = BOT_CMD_PARAMETER_STRING;
224 bot_cmd_string[BOT_CMD_IF] = "if";
225 bot_cmd_parm_type[BOT_CMD_IF] = BOT_CMD_PARAMETER_STRING;
227 bot_cmd_string[BOT_CMD_ELSE] = "else";
228 bot_cmd_parm_type[BOT_CMD_ELSE] = BOT_CMD_PARAMETER_NONE;
230 bot_cmd_string[BOT_CMD_FI] = "fi";
231 bot_cmd_parm_type[BOT_CMD_FI] = BOT_CMD_PARAMETER_NONE;
233 bot_cmd_string[BOT_CMD_RESETAIM] = "resetaim";
234 bot_cmd_parm_type[BOT_CMD_RESETAIM] = BOT_CMD_PARAMETER_NONE;
236 bot_cmd_string[BOT_CMD_AIM] = "aim";
237 bot_cmd_parm_type[BOT_CMD_AIM] = BOT_CMD_PARAMETER_STRING;
239 bot_cmd_string[BOT_CMD_AIMTARGET] = "aimtarget";
240 bot_cmd_parm_type[BOT_CMD_AIMTARGET] = BOT_CMD_PARAMETER_STRING;
242 bot_cmd_string[BOT_CMD_PRESSKEY] = "presskey";
243 bot_cmd_parm_type[BOT_CMD_PRESSKEY] = BOT_CMD_PARAMETER_STRING;
245 bot_cmd_string[BOT_CMD_RELEASEKEY] = "releasekey";
246 bot_cmd_parm_type[BOT_CMD_RELEASEKEY] = BOT_CMD_PARAMETER_STRING;
248 bot_cmd_string[BOT_CMD_SELECTWEAPON] = "selectweapon";
249 bot_cmd_parm_type[BOT_CMD_SELECTWEAPON] = BOT_CMD_PARAMETER_FLOAT;
251 bot_cmd_string[BOT_CMD_IMPULSE] = "impulse";
252 bot_cmd_parm_type[BOT_CMD_IMPULSE] = BOT_CMD_PARAMETER_FLOAT;
254 bot_cmd_string[BOT_CMD_WAIT_UNTIL] = "wait_until";
255 bot_cmd_parm_type[BOT_CMD_WAIT_UNTIL] = BOT_CMD_PARAMETER_FLOAT;
257 bot_cmd_string[BOT_CMD_BARRIER] = "barrier";
258 bot_cmd_parm_type[BOT_CMD_BARRIER] = BOT_CMD_PARAMETER_NONE;
260 bot_cmd_string[BOT_CMD_CONSOLE] = "console";
261 bot_cmd_parm_type[BOT_CMD_CONSOLE] = BOT_CMD_PARAMETER_STRING;
263 bot_cmd_string[BOT_CMD_SOUND] = "sound";
264 bot_cmd_parm_type[BOT_CMD_SOUND] = BOT_CMD_PARAMETER_STRING;
266 bot_cmds_initialized = TRUE;
269 // Returns first bot with matching name
270 entity find_bot_by_name(string name)
274 bot = findchainflags(flags, FL_CLIENT);
277 if(clienttype(bot) == CLIENTTYPE_BOT)
278 if(bot.netname==name)
287 // Returns a bot by number on list
288 entity find_bot_by_number(float number)
296 bot = findchainflags(flags, FL_CLIENT);
299 if(clienttype(bot) == CLIENTTYPE_BOT)
310 float bot_decodecommand(string cmdstring)
312 local float cmd_parm_type, i;
316 sp = strstrofs(cmdstring, " ", 0);
323 parm = substring(cmdstring, sp + 1, -1);
324 cmdstring = substring(cmdstring, 0, sp);
327 if(!bot_cmds_initialized)
330 for(i=1;i<BOT_CMD_COUNTER;++i)
332 if(bot_cmd_string[i]!=cmdstring)
335 cmd_parm_type = bot_cmd_parm_type[i];
337 if(cmd_parm_type!=BOT_CMD_PARAMETER_NONE&&parm=="")
339 print("ERROR: A parameter is required for this command\n");
343 // Load command into queue
344 bot_cmd.bot_cmd_type = i;
347 switch(cmd_parm_type)
349 case BOT_CMD_PARAMETER_FLOAT:
350 bot_cmd.bot_cmd_parm_float = stof(parm);
352 case BOT_CMD_PARAMETER_STRING:
353 if(bot_cmd.bot_cmd_parm_string)
354 strunzone(bot_cmd.bot_cmd_parm_string);
355 bot_cmd.bot_cmd_parm_string = strzone(parm);
357 case BOT_CMD_PARAMETER_VECTOR:
358 bot_cmd.bot_cmd_parm_vector = stov(parm);
365 print("ERROR: No such command '", cmdstring, "'\n");
369 void bot_cmdhelp(string scmd)
371 local float i, ntype;
374 if(!bot_cmds_initialized)
377 for(i=1;i<BOT_CMD_COUNTER;++i)
379 if(bot_cmd_string[i]!=scmd)
382 ntype = bot_cmd_parm_type[i];
386 case BOT_CMD_PARAMETER_FLOAT:
387 stype = "float number";
389 case BOT_CMD_PARAMETER_STRING:
392 case BOT_CMD_PARAMETER_VECTOR:
400 print(strcat("Command: ",bot_cmd_string[i],"\nParameter: <",stype,"> \n"));
402 print("Description: ");
406 print("Stops the bot completely. Any command other than 'continue' will be ignored.");
408 case BOT_CMD_CONTINUE:
409 print("Disable paused status");
412 print("Pause command parsing and bot ai for N seconds. Pressed key will remain pressed");
414 case BOT_CMD_WAIT_UNTIL:
415 print("Pause command parsing and bot ai until time is N from the last barrier. Pressed key will remain pressed");
417 case BOT_CMD_BARRIER:
418 print("Waits till all bots that have a command queue reach this command. Pressed key will remain pressed");
421 print("Look to the right or left N degrees. For turning to the left use positive numbers.");
424 print("Walk to an specific coordinate on the map. Usage: moveto \"x y z\"");
426 case BOT_CMD_MOVETOTARGET:
427 print("Walk to the specific target on the map");
429 case BOT_CMD_RESETGOAL:
430 print("Resets the goal stack");
433 print("Execute client command. Examples: cc \"say something\"; cc god; cc \"name newnickname\"; cc kill;");
436 print("Perform simple conditional execution.\n");
438 print(" sv_cmd .. if \"condition\"\n");
439 print(" sv_cmd .. <instruction if true>\n");
440 print(" sv_cmd .. <instruction if true>\n");
441 print(" sv_cmd .. else\n");
442 print(" sv_cmd .. <instruction if false>\n");
443 print(" sv_cmd .. <instruction if false>\n");
444 print(" sv_cmd .. fi\n");
445 print("Conditions: a=b, a>b, a<b, a\t\t(spaces not allowed)\n");
446 print(" Values in conditions can be numbers, cvars in the form cvar.cvar_string or special fields\n");
447 print("Fields: health, speed, flagcarrier\n");
448 print("Examples: if health>50; if health>cvar.g_balance_laser_primary_damage; if flagcarrier;");
450 case BOT_CMD_RESETAIM:
451 print("Points the aim to the coordinates x,y 0,0");
454 print("Move the aim x/y (horizontal/vertical) degrees relatives to the bot\n");
455 print("There is a 3rd optional parameter telling in how many seconds the aim has to reach the new position\n");
456 print("Examples: aim \"90 0\" // Turn 90 degrees inmediately (positive numbers move to the left/up)\n");
457 print(" aim \"0 90 2\" // Will gradually look to the sky in the next two seconds");
459 case BOT_CMD_AIMTARGET:
460 print("Points the aim to given target");
462 case BOT_CMD_PRESSKEY:
463 print("Press one of the following keys: forward, backward, left, right, jump, crouch, attack1, attack2, use\n");
464 print("Multiple keys can be pressed at time (with many presskey calls) and it will remain pressed until the command \"releasekey\" is called");
465 print("Note: The script will not return the control to the bot ai until all keys are released");
467 case BOT_CMD_RELEASEKEY:
468 print("Release previoulsy used keys. Use the parameter \"all\" to release all keys");
471 print("play sound file at bot location");
474 print("This command has no description yet.");
481 void bot_list_commands()
486 if(!bot_cmds_initialized)
489 print("List of all available commands:\n");
490 print(" Command\t\t\t\tParameter Type\n");
492 for(i=1;i<BOT_CMD_COUNTER;++i)
494 switch(bot_cmd_parm_type[i])
496 case BOT_CMD_PARAMETER_FLOAT:
497 ptype = "float number";
499 case BOT_CMD_PARAMETER_STRING:
502 case BOT_CMD_PARAMETER_VECTOR:
509 print(strcat(" ",bot_cmd_string[i],"\t\t\t\t<",ptype,"> \n"));
514 .float bot_exec_status;
516 #define BOT_EXEC_STATUS_IDLE 0
517 #define BOT_EXEC_STATUS_PAUSED 1
518 #define BOT_EXEC_STATUS_WAITING 2
520 #define CMD_STATUS_EXECUTING 0
521 #define CMD_STATUS_FINISHED 1
522 #define CMD_STATUS_ERROR 2
524 void SV_ParseClientCommand(string s);
527 SV_ParseClientCommand(bot_cmd.bot_cmd_parm_string);
528 return CMD_STATUS_FINISHED;
531 float bot_cmd_impulse()
533 self.impulse = bot_cmd.bot_cmd_parm_float;
534 return CMD_STATUS_FINISHED;
537 float bot_cmd_continue()
539 self.bot_exec_status &~= BOT_EXEC_STATUS_PAUSED;
540 return CMD_STATUS_FINISHED;
543 .float bot_cmd_wait_time;
546 if(self.bot_exec_status & BOT_EXEC_STATUS_WAITING)
548 if(time>=self.bot_cmd_wait_time)
550 self.bot_exec_status &~= BOT_EXEC_STATUS_WAITING;
551 return CMD_STATUS_FINISHED;
554 return CMD_STATUS_EXECUTING;
557 self.bot_cmd_wait_time = time + bot_cmd.bot_cmd_parm_float;
558 self.bot_exec_status |= BOT_EXEC_STATUS_WAITING;
559 return CMD_STATUS_EXECUTING;
562 float bot_cmd_wait_until()
564 if(time < bot_cmd.bot_cmd_parm_float + bot_barriertime)
566 self.bot_exec_status |= BOT_EXEC_STATUS_WAITING;
567 return CMD_STATUS_EXECUTING;
569 self.bot_exec_status &~= BOT_EXEC_STATUS_WAITING;
570 return CMD_STATUS_FINISHED;
573 float bot_cmd_barrier()
577 // 0 = no barrier, 1 = waiting, 2 = waiting finished
579 if(self.bot_barrier == 0) // initialization
581 self.bot_barrier = 1;
583 //self.colormod = '4 4 0';
586 if(self.bot_barrier == 1) // find other bots
588 FOR_EACH_CLIENT(cl) if(cl.isbot)
590 if(cl.bot_cmdqueuebuf_allocated)
591 if(cl.bot_barrier != 1)
592 return CMD_STATUS_EXECUTING; // not all are at the barrier yet
595 // all bots hit the barrier!
596 FOR_EACH_CLIENT(cl) if(cl.isbot)
598 cl.bot_barrier = 2; // acknowledge barrier
601 bot_barriertime = time;
604 // if we get here, the barrier is finished
606 self.bot_barrier = 0;
607 //self.colormod = '0 0 0';
609 return CMD_STATUS_FINISHED;
614 self.v_angle_y = self.v_angle_y + bot_cmd.bot_cmd_parm_float;
615 self.v_angle_y = self.v_angle_y - floor(self.v_angle_y / 360) * 360;
616 return CMD_STATUS_FINISHED;
619 float bot_cmd_select_weapon()
623 id = bot_cmd.bot_cmd_parm_float;
625 if(id < WEP_FIRST || id > WEP_LAST)
626 return CMD_STATUS_ERROR;
628 if(client_hasweapon(self, id, TRUE, FALSE))
629 self.switchweapon = id;
631 return CMD_STATUS_ERROR;
633 return CMD_STATUS_FINISHED;
636 .float bot_cmd_condition_status;
638 #define CMD_CONDITION_NONE 0
639 #define CMD_CONDITION_TRUE 1
640 #define CMD_CONDITION_FALSE 2
641 #define CMD_CONDITION_TRUE_BLOCK 4
642 #define CMD_CONDITION_FALSE_BLOCK 8
644 float bot_cmd_eval(string expr)
646 // Search for numbers
647 if(strstrofs("0123456789", substring(expr, 0, 1), 0) >= 0)
653 if(substring(expr, 0, 5)=="cvar.")
655 return cvar(substring(expr, 5, strlen(expr)));
664 return vlen(self.velocity);
666 return ((self.flagcarried!=world));
669 print(strcat("ERROR: Unable to convert the expression '",expr,"' into a numeric value\n"));
675 local string expr, val_a, val_b;
678 if(self.bot_cmd_condition_status != CMD_CONDITION_NONE)
680 // Only one "if" block is allowed at time
681 print("ERROR: Only one conditional block can be processed at time");
682 bot_clearqueue(self);
683 return CMD_STATUS_ERROR;
686 self.bot_cmd_condition_status |= CMD_CONDITION_TRUE_BLOCK;
688 // search for operators
689 expr = bot_cmd.bot_cmd_parm_string;
691 cmpofs = strstrofs(expr,"=",0);
695 val_a = substring(expr,0,cmpofs);
696 val_b = substring(expr,cmpofs+1,strlen(expr));
698 if(bot_cmd_eval(val_a)==bot_cmd_eval(val_b))
699 self.bot_cmd_condition_status |= CMD_CONDITION_TRUE;
701 self.bot_cmd_condition_status |= CMD_CONDITION_FALSE;
703 return CMD_STATUS_FINISHED;
706 cmpofs = strstrofs(expr,">",0);
710 val_a = substring(expr,0,cmpofs);
711 val_b = substring(expr,cmpofs+1,strlen(expr));
713 if(bot_cmd_eval(val_a)>bot_cmd_eval(val_b))
714 self.bot_cmd_condition_status |= CMD_CONDITION_TRUE;
716 self.bot_cmd_condition_status |= CMD_CONDITION_FALSE;
718 return CMD_STATUS_FINISHED;
721 cmpofs = strstrofs(expr,"<",0);
725 val_a = substring(expr,0,cmpofs);
726 val_b = substring(expr,cmpofs+1,strlen(expr));
728 if(bot_cmd_eval(val_a)<bot_cmd_eval(val_b))
729 self.bot_cmd_condition_status |= CMD_CONDITION_TRUE;
731 self.bot_cmd_condition_status |= CMD_CONDITION_FALSE;
733 return CMD_STATUS_FINISHED;
736 if(bot_cmd_eval(expr))
737 self.bot_cmd_condition_status |= CMD_CONDITION_TRUE;
739 self.bot_cmd_condition_status |= CMD_CONDITION_FALSE;
741 return CMD_STATUS_FINISHED;
746 self.bot_cmd_condition_status &~= CMD_CONDITION_TRUE_BLOCK;
747 self.bot_cmd_condition_status |= CMD_CONDITION_FALSE_BLOCK;
748 return CMD_STATUS_FINISHED;
753 self.bot_cmd_condition_status = CMD_CONDITION_NONE;
754 return CMD_STATUS_FINISHED;
757 float bot_cmd_resetaim()
759 self.v_angle = '0 0 0';
760 return CMD_STATUS_FINISHED;
763 .float bot_cmd_aim_begintime;
764 .float bot_cmd_aim_endtime;
765 .vector bot_cmd_aim_begin;
766 .vector bot_cmd_aim_end;
771 if(self.bot_cmd_aim_endtime)
773 local float progress;
775 progress = min(1 - (self.bot_cmd_aim_endtime - time) / (self.bot_cmd_aim_endtime - self.bot_cmd_aim_begintime),1);
776 self.v_angle = self.bot_cmd_aim_begin + ((self.bot_cmd_aim_end - self.bot_cmd_aim_begin) * progress);
778 if(time>=self.bot_cmd_aim_endtime)
780 self.bot_cmd_aim_endtime = 0;
781 return CMD_STATUS_FINISHED;
784 return CMD_STATUS_EXECUTING;
787 // New aiming direction
789 local float tokens, step;
791 parms = bot_cmd.bot_cmd_parm_string;
793 tokens = tokenizebyseparator(parms, " ");
797 self.v_angle_x -= stof(argv(1));
798 self.v_angle_y += stof(argv(0));
799 return CMD_STATUS_FINISHED;
802 if(tokens<2||tokens>3)
803 return CMD_STATUS_ERROR;
805 step = stof(argv(2));
807 self.bot_cmd_aim_begin = self.v_angle;
809 self.bot_cmd_aim_end_x = self.v_angle_x - stof(argv(1));
810 self.bot_cmd_aim_end_y = self.v_angle_y + stof(argv(0));
811 self.bot_cmd_aim_end_z = 0;
813 self.bot_cmd_aim_begintime = time;
814 self.bot_cmd_aim_endtime = time + step;
816 return CMD_STATUS_EXECUTING;
819 float bot_cmd_aimtarget()
821 if(self.bot_cmd_aim_endtime)
823 return bot_cmd_aim();
829 local float tokens, step;
831 parms = bot_cmd.bot_cmd_parm_string;
833 tokens = tokenizebyseparator(parms, " ");
835 e = bot_getplace(argv(0));
837 return CMD_STATUS_ERROR;
839 v = e.origin + (e.mins + e.maxs) * 0.5;
843 self.v_angle = vectoangles(v - (self.origin + self.view_ofs));
844 self.v_angle_x = -self.v_angle_x;
845 return CMD_STATUS_FINISHED;
848 if(tokens<1||tokens>2)
849 return CMD_STATUS_ERROR;
851 step = stof(argv(1));
853 self.bot_cmd_aim_begin = self.v_angle;
854 self.bot_cmd_aim_end = vectoangles(v - (self.origin + self.view_ofs));
855 self.bot_cmd_aim_end_x = -self.bot_cmd_aim_end_x;
857 self.bot_cmd_aim_begintime = time;
858 self.bot_cmd_aim_endtime = time + step;
860 return CMD_STATUS_EXECUTING;
865 #define BOT_CMD_KEY_NONE 0
866 #define BOT_CMD_KEY_FORWARD 1
867 #define BOT_CMD_KEY_BACKWARD 2
868 #define BOT_CMD_KEY_RIGHT 4
869 #define BOT_CMD_KEY_LEFT 8
870 #define BOT_CMD_KEY_JUMP 16
871 #define BOT_CMD_KEY_ATTACK1 32
872 #define BOT_CMD_KEY_ATTACK2 64
873 #define BOT_CMD_KEY_USE 128
874 #define BOT_CMD_KEY_HOOK 256
875 #define BOT_CMD_KEY_CROUCH 512
876 #define BOT_CMD_KEY_CHAT 1024
878 float bot_presskeys()
880 self.movement = '0 0 0';
881 self.BUTTON_JUMP = FALSE;
882 self.BUTTON_CROUCH = FALSE;
883 self.BUTTON_ATCK = FALSE;
884 self.BUTTON_ATCK2 = FALSE;
885 self.BUTTON_USE = FALSE;
886 self.BUTTON_HOOK = FALSE;
887 self.BUTTON_CHAT = FALSE;
889 if(self.bot_cmd_keys == BOT_CMD_KEY_NONE)
892 if(self.bot_cmd_keys & BOT_CMD_KEY_FORWARD)
893 self.movement_x = autocvar_sv_maxspeed;
894 else if(self.bot_cmd_keys & BOT_CMD_KEY_BACKWARD)
895 self.movement_x = -autocvar_sv_maxspeed;
897 if(self.bot_cmd_keys & BOT_CMD_KEY_RIGHT)
898 self.movement_y = autocvar_sv_maxspeed;
899 else if(self.bot_cmd_keys & BOT_CMD_KEY_LEFT)
900 self.movement_y = -autocvar_sv_maxspeed;
902 if(self.bot_cmd_keys & BOT_CMD_KEY_JUMP)
903 self.BUTTON_JUMP = TRUE;
905 if(self.bot_cmd_keys & BOT_CMD_KEY_CROUCH)
906 self.BUTTON_CROUCH = TRUE;
908 if(self.bot_cmd_keys & BOT_CMD_KEY_ATTACK1)
909 self.BUTTON_ATCK = TRUE;
911 if(self.bot_cmd_keys & BOT_CMD_KEY_ATTACK2)
912 self.BUTTON_ATCK2 = TRUE;
914 if(self.bot_cmd_keys & BOT_CMD_KEY_USE)
915 self.BUTTON_USE = TRUE;
917 if(self.bot_cmd_keys & BOT_CMD_KEY_HOOK)
918 self.BUTTON_HOOK = TRUE;
920 if(self.bot_cmd_keys & BOT_CMD_KEY_CHAT)
921 self.BUTTON_CHAT = TRUE;
927 float bot_cmd_keypress_handler(string key, float enabled)
933 self.bot_cmd_keys = power2of(20) - 1; // >:)
935 self.bot_cmd_keys = BOT_CMD_KEY_NONE;
939 self.bot_cmd_keys |= BOT_CMD_KEY_FORWARD;
940 self.bot_cmd_keys &~= BOT_CMD_KEY_BACKWARD;
943 self.bot_cmd_keys &~= BOT_CMD_KEY_FORWARD;
948 self.bot_cmd_keys |= BOT_CMD_KEY_BACKWARD;
949 self.bot_cmd_keys &~= BOT_CMD_KEY_FORWARD;
952 self.bot_cmd_keys &~= BOT_CMD_KEY_BACKWARD;
957 self.bot_cmd_keys |= BOT_CMD_KEY_LEFT;
958 self.bot_cmd_keys &~= BOT_CMD_KEY_RIGHT;
961 self.bot_cmd_keys &~= BOT_CMD_KEY_LEFT;
966 self.bot_cmd_keys |= BOT_CMD_KEY_RIGHT;
967 self.bot_cmd_keys &~= BOT_CMD_KEY_LEFT;
970 self.bot_cmd_keys &~= BOT_CMD_KEY_RIGHT;
974 self.bot_cmd_keys |= BOT_CMD_KEY_JUMP;
976 self.bot_cmd_keys &~= BOT_CMD_KEY_JUMP;
980 self.bot_cmd_keys |= BOT_CMD_KEY_CROUCH;
982 self.bot_cmd_keys &~= BOT_CMD_KEY_CROUCH;
986 self.bot_cmd_keys |= BOT_CMD_KEY_ATTACK1;
988 self.bot_cmd_keys &~= BOT_CMD_KEY_ATTACK1;
992 self.bot_cmd_keys |= BOT_CMD_KEY_ATTACK2;
994 self.bot_cmd_keys &~= BOT_CMD_KEY_ATTACK2;
998 self.bot_cmd_keys |= BOT_CMD_KEY_USE;
1000 self.bot_cmd_keys &~= BOT_CMD_KEY_USE;
1004 self.bot_cmd_keys |= BOT_CMD_KEY_HOOK;
1006 self.bot_cmd_keys &~= BOT_CMD_KEY_HOOK;
1010 self.bot_cmd_keys |= BOT_CMD_KEY_CHAT;
1012 self.bot_cmd_keys &~= BOT_CMD_KEY_CHAT;
1018 return CMD_STATUS_FINISHED;
1021 float bot_cmd_presskey()
1025 key = bot_cmd.bot_cmd_parm_string;
1027 bot_cmd_keypress_handler(key,TRUE);
1029 return CMD_STATUS_FINISHED;
1032 float bot_cmd_releasekey()
1036 key = bot_cmd.bot_cmd_parm_string;
1038 return bot_cmd_keypress_handler(key,FALSE);
1041 float bot_cmd_pause()
1045 self.BUTTON_USE = 0;
1046 self.BUTTON_ATCK = 0;
1047 self.BUTTON_JUMP = 0;
1048 self.BUTTON_HOOK = 0;
1049 self.BUTTON_CHAT = 0;
1050 self.BUTTON_ATCK2 = 0;
1051 self.BUTTON_CROUCH = 0;
1053 self.movement = '0 0 0';
1054 self.bot_cmd_keys = BOT_CMD_KEY_NONE;
1056 self.bot_exec_status |= BOT_EXEC_STATUS_PAUSED;
1057 return CMD_STATUS_FINISHED;
1060 float bot_cmd_moveto()
1062 return self.cmd_moveto(bot_cmd.bot_cmd_parm_vector);
1065 float bot_cmd_movetotarget()
1068 e = bot_getplace(bot_cmd.bot_cmd_parm_string);
1070 return CMD_STATUS_ERROR;
1071 return self.cmd_moveto(e.origin + (e.mins + e.maxs) * 0.5);
1074 float bot_cmd_resetgoal()
1076 return self.cmd_resetgoal();
1080 float bot_cmd_sound()
1083 f = bot_cmd.bot_cmd_parm_string;
1086 sound(self, CH_WEAPON_B, f, VOL_BASE, ATTN_MIN);
1088 return CMD_STATUS_FINISHED;
1093 void bot_command_executed(float rm)
1100 bot_dequeuecommand(self, self.bot_cmd_execution_index);
1102 self.bot_cmd_execution_index++;
1105 void bot_setcurrentcommand()
1109 if(!self.bot_cmd_current)
1111 self.bot_cmd_current = spawn();
1112 self.bot_cmd_current.classname = "bot_cmd";
1113 self.bot_cmd_current.is_bot_cmd = 1;
1116 bot_cmd = self.bot_cmd_current;
1117 if(bot_cmd.bot_cmd_index != self.bot_cmd_execution_index || self.bot_cmd_execution_index == 0)
1119 if(bot_havecommand(self, self.bot_cmd_execution_index))
1122 cmdstring = bot_readcommand(self, self.bot_cmd_execution_index);
1123 if(bot_decodecommand(cmdstring))
1125 bot_cmd.owner = self;
1126 bot_cmd.bot_cmd_index = self.bot_cmd_execution_index;
1130 // Invalid command, remove from queue
1132 bot_dequeuecommand(self, self.bot_cmd_execution_index);
1133 self.bot_cmd_execution_index++;
1141 void bot_resetqueues()
1146 FOR_EACH_CLIENT(cl) if(cl.isbot)
1148 if(cl.bot_cmdqueuebuf_allocated)
1150 // also, cancel all barriers
1152 for(i = 0; i < cl.bot_places_count; ++i)
1154 strunzone(cl.(bot_placenames[i]));
1155 cl.(bot_placenames[i]) = string_null;
1157 cl.bot_places_count = 0;
1160 bot_barriertime = time;
1163 // Here we map commands to functions and deal with complex interactions between commands and execution states
1164 // NOTE: Of course you need to include your commands here too :)
1165 float bot_execute_commands_once()
1167 local float status, ispressingkey;
1169 if(self.deadflag!=DEAD_NO)
1173 bot_setcurrentcommand();
1175 // Ignore all commands except continue when the bot is paused
1176 if(self.bot_exec_status & BOT_EXEC_STATUS_PAUSED)
1177 if(bot_cmd.bot_cmd_type!=BOT_CMD_CONTINUE)
1179 if(bot_cmd.bot_cmd_type!=BOT_CMD_NULL)
1181 bot_command_executed(TRUE);
1182 print( "WARNING: Commands are ignored while the bot is paused. Use the command 'continue' instead.\n");
1187 // Keep pressing keys raised by the "presskey" command
1188 ispressingkey = !!bot_presskeys();
1191 return ispressingkey;
1193 // Handle conditions
1194 if not(bot_cmd.bot_cmd_type==BOT_CMD_FI||bot_cmd.bot_cmd_type==BOT_CMD_ELSE)
1195 if(self.bot_cmd_condition_status & CMD_CONDITION_TRUE && self.bot_cmd_condition_status & CMD_CONDITION_FALSE_BLOCK)
1197 bot_command_executed(TRUE);
1200 else if(self.bot_cmd_condition_status & CMD_CONDITION_FALSE && self.bot_cmd_condition_status & CMD_CONDITION_TRUE_BLOCK)
1202 bot_command_executed(TRUE);
1206 // Map commands to functions
1207 switch(bot_cmd.bot_cmd_type)
1210 return ispressingkey;
1213 status = bot_cmd_pause();
1215 case BOT_CMD_CONTINUE:
1216 status = bot_cmd_continue();
1219 status = bot_cmd_wait();
1221 case BOT_CMD_WAIT_UNTIL:
1222 status = bot_cmd_wait_until();
1225 status = bot_cmd_turn();
1227 case BOT_CMD_MOVETO:
1228 status = bot_cmd_moveto();
1230 case BOT_CMD_MOVETOTARGET:
1231 status = bot_cmd_movetotarget();
1233 case BOT_CMD_RESETGOAL:
1234 status = bot_cmd_resetgoal();
1237 status = bot_cmd_cc();
1240 status = bot_cmd_if();
1243 status = bot_cmd_else();
1246 status = bot_cmd_fi();
1248 case BOT_CMD_RESETAIM:
1249 status = bot_cmd_resetaim();
1252 status = bot_cmd_aim();
1254 case BOT_CMD_AIMTARGET:
1255 status = bot_cmd_aimtarget();
1257 case BOT_CMD_PRESSKEY:
1258 status = bot_cmd_presskey();
1260 case BOT_CMD_RELEASEKEY:
1261 status = bot_cmd_releasekey();
1263 case BOT_CMD_SELECTWEAPON:
1264 status = bot_cmd_select_weapon();
1266 case BOT_CMD_IMPULSE:
1267 status = bot_cmd_impulse();
1269 case BOT_CMD_BARRIER:
1270 status = bot_cmd_barrier();
1272 case BOT_CMD_CONSOLE:
1273 localcmd(strcat(bot_cmd.bot_cmd_parm_string, "\n"));
1274 status = CMD_STATUS_FINISHED;
1277 status = bot_cmd_sound();
1280 print(strcat("ERROR: Invalid command on queue with id '",ftos(bot_cmd.bot_cmd_type),"'\n"));
1284 if (status==CMD_STATUS_ERROR)
1285 print(strcat("ERROR: The command '",bot_cmd_string[bot_cmd.bot_cmd_type],"' returned an error status\n"));
1287 // Move execution pointer
1288 if(status==CMD_STATUS_EXECUTING)
1294 if(autocvar_g_debug_bot_commands)
1298 switch(bot_cmd_parm_type[bot_cmd.bot_cmd_type])
1300 case BOT_CMD_PARAMETER_FLOAT:
1301 parms = ftos(bot_cmd.bot_cmd_parm_float);
1303 case BOT_CMD_PARAMETER_STRING:
1304 parms = bot_cmd.bot_cmd_parm_string;
1306 case BOT_CMD_PARAMETER_VECTOR:
1307 parms = vtos(bot_cmd.bot_cmd_parm_vector);
1313 clientcommand(self,strcat("say ^7", bot_cmd_string[bot_cmd.bot_cmd_type]," ",parms,"\n"));
1316 bot_command_executed(TRUE);
1319 if(status == CMD_STATUS_FINISHED)
1322 return CMD_STATUS_ERROR;
1325 // This function should be (the only) called directly from the bot ai loop
1326 float bot_execute_commands()
1331 f = bot_execute_commands_once();