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);
24 bot.bot_cmdqueuebuf_end += 1;
27 void bot_dequeuecommand(entity bot, float idx)
29 if(!bot.bot_cmdqueuebuf_allocated)
30 error("dequeuecommand but no queue allocated");
31 if(idx < bot.bot_cmdqueuebuf_start)
32 error("dequeueing a command in the past");
33 if(idx >= bot.bot_cmdqueuebuf_end)
34 error("dequeueing a command in the future");
35 bufstr_set(bot.bot_cmdqueuebuf, idx, "");
36 if(idx == bot.bot_cmdqueuebuf_start)
37 bot.bot_cmdqueuebuf_start += 1;
38 if(bot.bot_cmdqueuebuf_start >= bot.bot_cmdqueuebuf_end)
42 string bot_readcommand(entity bot, float idx)
44 if(!bot.bot_cmdqueuebuf_allocated)
45 error("readcommand but no queue allocated");
46 if(idx < bot.bot_cmdqueuebuf_start)
47 error("reading a command in the past");
48 if(idx >= bot.bot_cmdqueuebuf_end)
49 error("reading a command in the future");
50 return bufstr_get(bot.bot_cmdqueuebuf, idx);
53 float bot_havecommand(entity bot, float idx)
55 if(!bot.bot_cmdqueuebuf_allocated)
57 if(idx < bot.bot_cmdqueuebuf_start)
59 if(idx >= bot.bot_cmdqueuebuf_end)
64 #define MAX_BOT_PLACES 4
65 .float bot_places_count;
66 .entity bot_places[MAX_BOT_PLACES]; FTEQCC_YOU_SUCK_THIS_IS_NOT_UNREFERENCED(bot_places);
67 .string bot_placenames[MAX_BOT_PLACES]; FTEQCC_YOU_SUCK_THIS_IS_NOT_UNREFERENCED(bot_placenames);
68 entity bot_getplace(string placename)
71 if(substring(placename, 0, 1) == "@")
74 placename = substring(placename, 1, -1);
76 for(i = 0; i < self.bot_places_count; ++i)
77 if(self.(bot_placenames[i]) == placename)
78 return self.(bot_places[i]);
79 // now: i == self.bot_places_count
80 s = s2 = cvar_string(placename);
81 p = strstrofs(s2, " ", 0);
84 s = substring(s2, 0, p);
85 //print("places: ", placename, " -> ", cvar_string(placename), "\n");
86 cvar_set(placename, strcat(substring(s2, p+1, -1), " ", s));
87 //print("places: ", placename, " := ", cvar_string(placename), "\n");
89 e = find(world, targetname, s);
91 print("invalid place ", s, "\n");
92 if(i < MAX_BOT_PLACES)
94 self.(bot_placenames[i]) = strzone(placename);
95 self.(bot_places[i]) = e;
96 self.bot_places_count += 1;
102 e = find(world, targetname, placename);
104 print("invalid place ", placename, "\n");
110 // NOTE: New commands should be added here. Do not forget to update BOT_CMD_COUNTER
111 #define BOT_CMD_NULL 0
112 #define BOT_CMD_PAUSE 1
113 #define BOT_CMD_CONTINUE 2
114 #define BOT_CMD_WAIT 3
115 #define BOT_CMD_TURN 4
116 #define BOT_CMD_MOVETO 5
117 #define BOT_CMD_RESETGOAL 6 // Not implemented yet
120 #define BOT_CMD_ELSE 9
121 #define BOT_CMD_FI 10
122 #define BOT_CMD_RESETAIM 11
123 #define BOT_CMD_AIM 12
124 #define BOT_CMD_PRESSKEY 13
125 #define BOT_CMD_RELEASEKEY 14
126 #define BOT_CMD_SELECTWEAPON 15
127 #define BOT_CMD_IMPULSE 16
128 #define BOT_CMD_WAIT_UNTIL 17
129 #define BOT_CMD_MOVETOTARGET 18
130 #define BOT_CMD_AIMTARGET 19
131 #define BOT_CMD_BARRIER 20
132 #define BOT_CMD_CONSOLE 21
133 #define BOT_CMD_SOUND 22
134 #define BOT_CMD_WHILE 23 // TODO: Not implemented yet
135 #define BOT_CMD_WEND 24 // TODO: Not implemented yet
136 #define BOT_CMD_CHASE 25 // TODO: Not implemented yet
138 #define BOT_CMD_COUNTER 23 // Update this value if you add/remove a command
140 // NOTE: Following commands should be implemented on the bot ai
141 // If a new command should be handled by the target ai(s) please declare it here
142 .float(vector) cmd_moveto;
143 .float() cmd_resetgoal;
146 #define BOT_CMD_PARAMETER_NONE 0
147 #define BOT_CMD_PARAMETER_FLOAT 1
148 #define BOT_CMD_PARAMETER_STRING 2
149 #define BOT_CMD_PARAMETER_VECTOR 3
151 float bot_cmds_initialized;
152 float bot_cmd_parm_type[BOT_CMD_COUNTER];
153 string bot_cmd_string[BOT_CMD_COUNTER];
155 // Bots command queue
156 entity bot_cmd; // global current command
157 .entity bot_cmd_current; // current command of this bot
159 .float is_bot_cmd; // Tells if the entity is a bot command
160 .float bot_cmd_index; // Position of the command in the queue
161 .float bot_cmd_type; // If of command (see the BOT_CMD_* defines)
162 .float bot_cmd_parm_float; // Field to store a float parameter
163 .string bot_cmd_parm_string; // Field to store a string parameter
164 .vector bot_cmd_parm_vector; // Field to store a vector parameter
166 float bot_barriertime;
169 .float bot_cmd_execution_index; // Position in the queue of the command to be executed
171 // Initialize global commands list
172 // NOTE: New commands should be initialized here
173 void bot_commands_init()
175 bot_cmd_string[BOT_CMD_NULL] = "";
176 bot_cmd_parm_type[BOT_CMD_NULL] = BOT_CMD_PARAMETER_NONE;
178 bot_cmd_string[BOT_CMD_PAUSE] = "pause";
179 bot_cmd_parm_type[BOT_CMD_PAUSE] = BOT_CMD_PARAMETER_NONE;
181 bot_cmd_string[BOT_CMD_CONTINUE] = "continue";
182 bot_cmd_parm_type[BOT_CMD_CONTINUE] = BOT_CMD_PARAMETER_NONE;
184 bot_cmd_string[BOT_CMD_WAIT] = "wait";
185 bot_cmd_parm_type[BOT_CMD_WAIT] = BOT_CMD_PARAMETER_FLOAT;
187 bot_cmd_string[BOT_CMD_TURN] = "turn";
188 bot_cmd_parm_type[BOT_CMD_TURN] = BOT_CMD_PARAMETER_FLOAT;
190 bot_cmd_string[BOT_CMD_MOVETO] = "moveto";
191 bot_cmd_parm_type[BOT_CMD_MOVETO] = BOT_CMD_PARAMETER_VECTOR;
193 bot_cmd_string[BOT_CMD_MOVETOTARGET] = "movetotarget";
194 bot_cmd_parm_type[BOT_CMD_MOVETOTARGET] = BOT_CMD_PARAMETER_STRING;
196 bot_cmd_string[BOT_CMD_RESETGOAL] = "resetgoal";
197 bot_cmd_parm_type[BOT_CMD_RESETGOAL] = BOT_CMD_PARAMETER_NONE;
199 bot_cmd_string[BOT_CMD_CC] = "cc";
200 bot_cmd_parm_type[BOT_CMD_CC] = BOT_CMD_PARAMETER_STRING;
202 bot_cmd_string[BOT_CMD_IF] = "if";
203 bot_cmd_parm_type[BOT_CMD_IF] = BOT_CMD_PARAMETER_STRING;
205 bot_cmd_string[BOT_CMD_ELSE] = "else";
206 bot_cmd_parm_type[BOT_CMD_ELSE] = BOT_CMD_PARAMETER_NONE;
208 bot_cmd_string[BOT_CMD_FI] = "fi";
209 bot_cmd_parm_type[BOT_CMD_FI] = BOT_CMD_PARAMETER_NONE;
211 bot_cmd_string[BOT_CMD_RESETAIM] = "resetaim";
212 bot_cmd_parm_type[BOT_CMD_RESETAIM] = BOT_CMD_PARAMETER_NONE;
214 bot_cmd_string[BOT_CMD_AIM] = "aim";
215 bot_cmd_parm_type[BOT_CMD_AIM] = BOT_CMD_PARAMETER_STRING;
217 bot_cmd_string[BOT_CMD_AIMTARGET] = "aimtarget";
218 bot_cmd_parm_type[BOT_CMD_AIMTARGET] = BOT_CMD_PARAMETER_STRING;
220 bot_cmd_string[BOT_CMD_PRESSKEY] = "presskey";
221 bot_cmd_parm_type[BOT_CMD_PRESSKEY] = BOT_CMD_PARAMETER_STRING;
223 bot_cmd_string[BOT_CMD_RELEASEKEY] = "releasekey";
224 bot_cmd_parm_type[BOT_CMD_RELEASEKEY] = BOT_CMD_PARAMETER_STRING;
226 bot_cmd_string[BOT_CMD_SELECTWEAPON] = "selectweapon";
227 bot_cmd_parm_type[BOT_CMD_SELECTWEAPON] = BOT_CMD_PARAMETER_FLOAT;
229 bot_cmd_string[BOT_CMD_IMPULSE] = "impulse";
230 bot_cmd_parm_type[BOT_CMD_IMPULSE] = BOT_CMD_PARAMETER_FLOAT;
232 bot_cmd_string[BOT_CMD_WAIT_UNTIL] = "wait_until";
233 bot_cmd_parm_type[BOT_CMD_WAIT_UNTIL] = BOT_CMD_PARAMETER_FLOAT;
235 bot_cmd_string[BOT_CMD_BARRIER] = "barrier";
236 bot_cmd_parm_type[BOT_CMD_BARRIER] = BOT_CMD_PARAMETER_NONE;
238 bot_cmd_string[BOT_CMD_CONSOLE] = "console";
239 bot_cmd_parm_type[BOT_CMD_CONSOLE] = BOT_CMD_PARAMETER_STRING;
241 bot_cmd_string[BOT_CMD_SOUND] = "sound";
242 bot_cmd_parm_type[BOT_CMD_SOUND] = BOT_CMD_PARAMETER_STRING;
244 bot_cmds_initialized = TRUE;
247 // Returns first bot with matching name
248 entity find_bot_by_name(string name)
252 bot = findchainflags(flags, FL_CLIENT);
255 if(clienttype(bot) == CLIENTTYPE_BOT)
256 if(bot.netname==name)
265 // Returns a bot by number on list
266 entity find_bot_by_number(float number)
274 bot = findchainflags(flags, FL_CLIENT);
277 if(clienttype(bot) == CLIENTTYPE_BOT)
288 float bot_decodecommand(string cmdstring)
290 local float cmd_parm_type, i;
294 sp = strstrofs(cmdstring, " ", 0);
301 parm = substring(cmdstring, sp + 1, -1);
302 cmdstring = substring(cmdstring, 0, sp);
305 if(!bot_cmds_initialized)
308 for(i=1;i<BOT_CMD_COUNTER;++i)
310 if(bot_cmd_string[i]!=cmdstring)
313 cmd_parm_type = bot_cmd_parm_type[i];
315 if(cmd_parm_type!=BOT_CMD_PARAMETER_NONE&&parm=="")
317 print("ERROR: A parameter is required for this command\n");
321 // Load command into queue
322 bot_cmd.bot_cmd_type = i;
325 switch(cmd_parm_type)
327 case BOT_CMD_PARAMETER_FLOAT:
328 bot_cmd.bot_cmd_parm_float = stof(parm);
330 case BOT_CMD_PARAMETER_STRING:
331 if(bot_cmd.bot_cmd_parm_string)
332 strunzone(bot_cmd.bot_cmd_parm_string);
333 bot_cmd.bot_cmd_parm_string = strzone(parm);
335 case BOT_CMD_PARAMETER_VECTOR:
336 bot_cmd.bot_cmd_parm_vector = stov(parm);
343 print("ERROR: No such command '", cmdstring, "'\n");
347 void bot_cmdhelp(string scmd)
349 local float i, ntype;
352 if(!bot_cmds_initialized)
355 for(i=1;i<BOT_CMD_COUNTER;++i)
357 if(bot_cmd_string[i]!=scmd)
360 ntype = bot_cmd_parm_type[i];
364 case BOT_CMD_PARAMETER_FLOAT:
365 stype = "float number";
367 case BOT_CMD_PARAMETER_STRING:
370 case BOT_CMD_PARAMETER_VECTOR:
378 print(strcat("Command: ",bot_cmd_string[i],"\nParameter: <",stype,"> \n"));
380 print("Description: ");
384 print("Stops the bot completely. Any command other than 'continue' will be ignored.");
386 case BOT_CMD_CONTINUE:
387 print("Disable paused status");
390 print("Pause command parsing and bot ai for N seconds. Pressed key will remain pressed");
392 case BOT_CMD_WAIT_UNTIL:
393 print("Pause command parsing and bot ai until time is N from the last barrier. Pressed key will remain pressed");
395 case BOT_CMD_BARRIER:
396 print("Waits till all bots that have a command queue reach this command. Pressed key will remain pressed");
399 print("Look to the right or left N degrees. For turning to the left use positive numbers.");
402 print("Walk to an specific coordinate on the map. Usage: moveto \"x y z\"");
404 case BOT_CMD_MOVETOTARGET:
405 print("Walk to the specific target on the map");
407 case BOT_CMD_RESETGOAL:
408 print("Resets the goal stack");
411 print("Execute client command. Examples: cc \"say something\"; cc god; cc \"name newnickname\"; cc kill;");
414 print("Perform simple conditional execution.\n");
416 print(" sv_cmd .. if \"condition\"\n");
417 print(" sv_cmd .. <instruction if true>\n");
418 print(" sv_cmd .. <instruction if true>\n");
419 print(" sv_cmd .. else\n");
420 print(" sv_cmd .. <instruction if false>\n");
421 print(" sv_cmd .. <instruction if false>\n");
422 print(" sv_cmd .. fi\n");
423 print("Conditions: a=b, a>b, a<b, a\t\t(spaces not allowed)\n");
424 print(" Values in conditions can be numbers, cvars in the form cvar.cvar_string or special fields\n");
425 print("Fields: health, speed, flagcarrier\n");
426 print("Examples: if health>50; if health>cvar.g_balance_laser_primary_damage; if flagcarrier;");
428 case BOT_CMD_RESETAIM:
429 print("Points the aim to the coordinates x,y 0,0");
432 print("Move the aim x/y (horizontal/vertical) degrees relatives to the bot\n");
433 print("There is a 3rd optional parameter telling in how many seconds the aim has to reach the new position\n");
434 print("Examples: aim \"90 0\" // Turn 90 degrees inmediately (positive numbers move to the left/up)\n");
435 print(" aim \"0 90 2\" // Will gradually look to the sky in the next two seconds");
437 case BOT_CMD_AIMTARGET:
438 print("Points the aim to given target");
440 case BOT_CMD_PRESSKEY:
441 print("Press one of the following keys: forward, backward, left, right, jump, crouch, attack1, attack2, use\n");
442 print("Multiple keys can be pressed at time (with many presskey calls) and it will remain pressed until the command \"releasekey\" is called");
443 print("Note: The script will not return the control to the bot ai until all keys are released");
445 case BOT_CMD_RELEASEKEY:
446 print("Release previoulsy used keys. Use the parameter \"all\" to release all keys");
449 print("play sound file at bot location");
452 print("This command has no description yet.");
459 void bot_list_commands()
464 if(!bot_cmds_initialized)
467 print("List of all available commands:\n");
468 print(" Command\t\t\t\tParameter Type\n");
470 for(i=1;i<BOT_CMD_COUNTER;++i)
472 switch(bot_cmd_parm_type[i])
474 case BOT_CMD_PARAMETER_FLOAT:
475 ptype = "float number";
477 case BOT_CMD_PARAMETER_STRING:
480 case BOT_CMD_PARAMETER_VECTOR:
487 print(strcat(" ",bot_cmd_string[i],"\t\t\t\t<",ptype,"> \n"));
492 .float bot_exec_status;
494 #define BOT_EXEC_STATUS_IDLE 0
495 #define BOT_EXEC_STATUS_PAUSED 1
496 #define BOT_EXEC_STATUS_WAITING 2
498 #define CMD_STATUS_EXECUTING 0
499 #define CMD_STATUS_FINISHED 1
500 #define CMD_STATUS_ERROR 2
502 void SV_ParseClientCommand(string s);
505 SV_ParseClientCommand(bot_cmd.bot_cmd_parm_string);
506 return CMD_STATUS_FINISHED;
509 float bot_cmd_impulse()
511 self.impulse = bot_cmd.bot_cmd_parm_float;
512 return CMD_STATUS_FINISHED;
515 float bot_cmd_continue()
517 self.bot_exec_status &~= BOT_EXEC_STATUS_PAUSED;
518 return CMD_STATUS_FINISHED;
521 .float bot_cmd_wait_time;
524 if(self.bot_exec_status & BOT_EXEC_STATUS_WAITING)
526 if(time>=self.bot_cmd_wait_time)
528 self.bot_exec_status &~= BOT_EXEC_STATUS_WAITING;
529 return CMD_STATUS_FINISHED;
532 return CMD_STATUS_EXECUTING;
535 self.bot_cmd_wait_time = time + bot_cmd.bot_cmd_parm_float;
536 self.bot_exec_status |= BOT_EXEC_STATUS_WAITING;
537 return CMD_STATUS_EXECUTING;
540 float bot_cmd_wait_until()
542 if(time < bot_cmd.bot_cmd_parm_float + bot_barriertime)
544 self.bot_exec_status |= BOT_EXEC_STATUS_WAITING;
545 return CMD_STATUS_EXECUTING;
547 self.bot_exec_status &~= BOT_EXEC_STATUS_WAITING;
548 return CMD_STATUS_FINISHED;
551 float bot_cmd_barrier()
555 // 0 = no barrier, 1 = waiting, 2 = waiting finished
557 if(self.bot_barrier == 0) // initialization
559 self.bot_barrier = 1;
561 //self.colormod = '4 4 0';
564 if(self.bot_barrier == 1) // find other bots
566 FOR_EACH_CLIENT(cl) if(cl.isbot)
568 if(cl.bot_cmdqueuebuf_allocated)
569 if(cl.bot_barrier != 1)
570 return CMD_STATUS_EXECUTING; // not all are at the barrier yet
573 // all bots hit the barrier!
574 FOR_EACH_CLIENT(cl) if(cl.isbot)
576 cl.bot_barrier = 2; // acknowledge barrier
579 bot_barriertime = time;
582 // if we get here, the barrier is finished
584 self.bot_barrier = 0;
585 //self.colormod = '0 0 0';
587 return CMD_STATUS_FINISHED;
592 self.v_angle_y = self.v_angle_y + bot_cmd.bot_cmd_parm_float;
593 self.v_angle_y = self.v_angle_y - floor(self.v_angle_y / 360) * 360;
594 return CMD_STATUS_FINISHED;
597 float bot_cmd_select_weapon()
601 id = bot_cmd.bot_cmd_parm_float;
603 if(id < WEP_FIRST || id > WEP_LAST)
604 return CMD_STATUS_ERROR;
606 if(client_hasweapon(self, id, TRUE, FALSE))
607 self.switchweapon = id;
609 return CMD_STATUS_ERROR;
611 return CMD_STATUS_FINISHED;
614 .float bot_cmd_condition_status;
616 #define CMD_CONDITION_NONE 0
617 #define CMD_CONDITION_TRUE 1
618 #define CMD_CONDITION_FALSE 2
619 #define CMD_CONDITION_TRUE_BLOCK 4
620 #define CMD_CONDITION_FALSE_BLOCK 8
622 float bot_cmd_eval(string expr)
624 // Search for numbers
625 if(strstrofs("0123456789", substring(expr, 0, 1), 0) >= 0)
631 if(substring(expr, 0, 5)=="cvar.")
633 return cvar(substring(expr, 5, strlen(expr)));
642 return vlen(self.velocity);
644 return ((self.flagcarried!=world));
647 print(strcat("ERROR: Unable to convert the expression '",expr,"' into a numeric value\n"));
653 local string expr, val_a, val_b;
656 if(self.bot_cmd_condition_status != CMD_CONDITION_NONE)
658 // Only one "if" block is allowed at time
659 print("ERROR: Only one conditional block can be processed at time");
660 bot_clearqueue(self);
661 return CMD_STATUS_ERROR;
664 self.bot_cmd_condition_status |= CMD_CONDITION_TRUE_BLOCK;
666 // search for operators
667 expr = bot_cmd.bot_cmd_parm_string;
669 cmpofs = strstrofs(expr,"=",0);
673 val_a = substring(expr,0,cmpofs);
674 val_b = substring(expr,cmpofs+1,strlen(expr));
676 if(bot_cmd_eval(val_a)==bot_cmd_eval(val_b))
677 self.bot_cmd_condition_status |= CMD_CONDITION_TRUE;
679 self.bot_cmd_condition_status |= CMD_CONDITION_FALSE;
681 return CMD_STATUS_FINISHED;
684 cmpofs = strstrofs(expr,">",0);
688 val_a = substring(expr,0,cmpofs);
689 val_b = substring(expr,cmpofs+1,strlen(expr));
691 if(bot_cmd_eval(val_a)>bot_cmd_eval(val_b))
692 self.bot_cmd_condition_status |= CMD_CONDITION_TRUE;
694 self.bot_cmd_condition_status |= CMD_CONDITION_FALSE;
696 return CMD_STATUS_FINISHED;
699 cmpofs = strstrofs(expr,"<",0);
703 val_a = substring(expr,0,cmpofs);
704 val_b = substring(expr,cmpofs+1,strlen(expr));
706 if(bot_cmd_eval(val_a)<bot_cmd_eval(val_b))
707 self.bot_cmd_condition_status |= CMD_CONDITION_TRUE;
709 self.bot_cmd_condition_status |= CMD_CONDITION_FALSE;
711 return CMD_STATUS_FINISHED;
714 if(bot_cmd_eval(expr))
715 self.bot_cmd_condition_status |= CMD_CONDITION_TRUE;
717 self.bot_cmd_condition_status |= CMD_CONDITION_FALSE;
719 return CMD_STATUS_FINISHED;
724 self.bot_cmd_condition_status &~= CMD_CONDITION_TRUE_BLOCK;
725 self.bot_cmd_condition_status |= CMD_CONDITION_FALSE_BLOCK;
726 return CMD_STATUS_FINISHED;
731 self.bot_cmd_condition_status = CMD_CONDITION_NONE;
732 return CMD_STATUS_FINISHED;
735 float bot_cmd_resetaim()
737 self.v_angle = '0 0 0';
738 return CMD_STATUS_FINISHED;
741 .float bot_cmd_aim_begintime;
742 .float bot_cmd_aim_endtime;
743 .vector bot_cmd_aim_begin;
744 .vector bot_cmd_aim_end;
749 if(self.bot_cmd_aim_endtime)
751 local float progress;
753 progress = min(1 - (self.bot_cmd_aim_endtime - time) / (self.bot_cmd_aim_endtime - self.bot_cmd_aim_begintime),1);
754 self.v_angle = self.bot_cmd_aim_begin + ((self.bot_cmd_aim_end - self.bot_cmd_aim_begin) * progress);
756 if(time>=self.bot_cmd_aim_endtime)
758 self.bot_cmd_aim_endtime = 0;
759 return CMD_STATUS_FINISHED;
762 return CMD_STATUS_EXECUTING;
765 // New aiming direction
767 local float tokens, step;
769 parms = bot_cmd.bot_cmd_parm_string;
771 tokens = tokenizebyseparator(parms, " ");
775 self.v_angle_x -= stof(argv(1));
776 self.v_angle_y += stof(argv(0));
777 return CMD_STATUS_FINISHED;
780 if(tokens<2||tokens>3)
781 return CMD_STATUS_ERROR;
783 step = stof(argv(2));
785 self.bot_cmd_aim_begin = self.v_angle;
787 self.bot_cmd_aim_end_x = self.v_angle_x - stof(argv(1));
788 self.bot_cmd_aim_end_y = self.v_angle_y + stof(argv(0));
789 self.bot_cmd_aim_end_z = 0;
791 self.bot_cmd_aim_begintime = time;
792 self.bot_cmd_aim_endtime = time + step;
794 return CMD_STATUS_EXECUTING;
797 float bot_cmd_aimtarget()
799 if(self.bot_cmd_aim_endtime)
801 return bot_cmd_aim();
807 local float tokens, step;
809 parms = bot_cmd.bot_cmd_parm_string;
811 tokens = tokenizebyseparator(parms, " ");
813 e = bot_getplace(argv(0));
815 return CMD_STATUS_ERROR;
817 v = e.origin + (e.mins + e.maxs) * 0.5;
821 self.v_angle = vectoangles(v - (self.origin + self.view_ofs));
822 self.v_angle_x = -self.v_angle_x;
823 return CMD_STATUS_FINISHED;
826 if(tokens<1||tokens>2)
827 return CMD_STATUS_ERROR;
829 step = stof(argv(1));
831 self.bot_cmd_aim_begin = self.v_angle;
832 self.bot_cmd_aim_end = vectoangles(v - (self.origin + self.view_ofs));
833 self.bot_cmd_aim_end_x = -self.bot_cmd_aim_end_x;
835 self.bot_cmd_aim_begintime = time;
836 self.bot_cmd_aim_endtime = time + step;
838 return CMD_STATUS_EXECUTING;
843 #define BOT_CMD_KEY_NONE 0
844 #define BOT_CMD_KEY_FORWARD 1
845 #define BOT_CMD_KEY_BACKWARD 2
846 #define BOT_CMD_KEY_RIGHT 4
847 #define BOT_CMD_KEY_LEFT 8
848 #define BOT_CMD_KEY_JUMP 16
849 #define BOT_CMD_KEY_ATTACK1 32
850 #define BOT_CMD_KEY_ATTACK2 64
851 #define BOT_CMD_KEY_USE 128
852 #define BOT_CMD_KEY_HOOK 256
853 #define BOT_CMD_KEY_CROUCH 512
854 #define BOT_CMD_KEY_CHAT 1024
856 float bot_presskeys()
858 self.movement = '0 0 0';
859 self.BUTTON_JUMP = FALSE;
860 self.BUTTON_CROUCH = FALSE;
861 self.BUTTON_ATCK = FALSE;
862 self.BUTTON_ATCK2 = FALSE;
863 self.BUTTON_USE = FALSE;
864 self.BUTTON_HOOK = FALSE;
865 self.BUTTON_CHAT = FALSE;
867 if(self.bot_cmd_keys == BOT_CMD_KEY_NONE)
870 if(self.bot_cmd_keys & BOT_CMD_KEY_FORWARD)
871 self.movement_x = cvar("sv_maxspeed");
872 else if(self.bot_cmd_keys & BOT_CMD_KEY_BACKWARD)
873 self.movement_x = -cvar("sv_maxspeed");
875 if(self.bot_cmd_keys & BOT_CMD_KEY_RIGHT)
876 self.movement_y = cvar("sv_maxspeed");
877 else if(self.bot_cmd_keys & BOT_CMD_KEY_LEFT)
878 self.movement_y = -cvar("sv_maxspeed");
880 if(self.bot_cmd_keys & BOT_CMD_KEY_JUMP)
881 self.BUTTON_JUMP = TRUE;
883 if(self.bot_cmd_keys & BOT_CMD_KEY_CROUCH)
884 self.BUTTON_CROUCH = TRUE;
886 if(self.bot_cmd_keys & BOT_CMD_KEY_ATTACK1)
887 self.BUTTON_ATCK = TRUE;
889 if(self.bot_cmd_keys & BOT_CMD_KEY_ATTACK2)
890 self.BUTTON_ATCK2 = TRUE;
892 if(self.bot_cmd_keys & BOT_CMD_KEY_USE)
893 self.BUTTON_USE = TRUE;
895 if(self.bot_cmd_keys & BOT_CMD_KEY_HOOK)
896 self.BUTTON_HOOK = TRUE;
898 if(self.bot_cmd_keys & BOT_CMD_KEY_CHAT)
899 self.BUTTON_CHAT = TRUE;
905 float bot_cmd_keypress_handler(string key, float enabled)
911 self.bot_cmd_keys = power2of(20) - 1; // >:)
913 self.bot_cmd_keys = BOT_CMD_KEY_NONE;
917 self.bot_cmd_keys |= BOT_CMD_KEY_FORWARD;
918 self.bot_cmd_keys &~= BOT_CMD_KEY_BACKWARD;
921 self.bot_cmd_keys &~= BOT_CMD_KEY_FORWARD;
926 self.bot_cmd_keys |= BOT_CMD_KEY_BACKWARD;
927 self.bot_cmd_keys &~= BOT_CMD_KEY_FORWARD;
930 self.bot_cmd_keys &~= BOT_CMD_KEY_BACKWARD;
935 self.bot_cmd_keys |= BOT_CMD_KEY_LEFT;
936 self.bot_cmd_keys &~= BOT_CMD_KEY_RIGHT;
939 self.bot_cmd_keys &~= BOT_CMD_KEY_LEFT;
944 self.bot_cmd_keys |= BOT_CMD_KEY_RIGHT;
945 self.bot_cmd_keys &~= BOT_CMD_KEY_LEFT;
948 self.bot_cmd_keys &~= BOT_CMD_KEY_RIGHT;
952 self.bot_cmd_keys |= BOT_CMD_KEY_JUMP;
954 self.bot_cmd_keys &~= BOT_CMD_KEY_JUMP;
958 self.bot_cmd_keys |= BOT_CMD_KEY_CROUCH;
960 self.bot_cmd_keys &~= BOT_CMD_KEY_CROUCH;
964 self.bot_cmd_keys |= BOT_CMD_KEY_ATTACK1;
966 self.bot_cmd_keys &~= BOT_CMD_KEY_ATTACK1;
970 self.bot_cmd_keys |= BOT_CMD_KEY_ATTACK2;
972 self.bot_cmd_keys &~= BOT_CMD_KEY_ATTACK2;
976 self.bot_cmd_keys |= BOT_CMD_KEY_USE;
978 self.bot_cmd_keys &~= BOT_CMD_KEY_USE;
982 self.bot_cmd_keys |= BOT_CMD_KEY_HOOK;
984 self.bot_cmd_keys &~= BOT_CMD_KEY_HOOK;
988 self.bot_cmd_keys |= BOT_CMD_KEY_CHAT;
990 self.bot_cmd_keys &~= BOT_CMD_KEY_CHAT;
996 return CMD_STATUS_FINISHED;
999 float bot_cmd_presskey()
1003 key = bot_cmd.bot_cmd_parm_string;
1005 bot_cmd_keypress_handler(key,TRUE);
1007 return CMD_STATUS_FINISHED;
1010 float bot_cmd_releasekey()
1014 key = bot_cmd.bot_cmd_parm_string;
1016 return bot_cmd_keypress_handler(key,FALSE);
1019 float bot_cmd_pause()
1023 self.BUTTON_USE = 0;
1024 self.BUTTON_ATCK = 0;
1025 self.BUTTON_JUMP = 0;
1026 self.BUTTON_HOOK = 0;
1027 self.BUTTON_CHAT = 0;
1028 self.BUTTON_ATCK2 = 0;
1029 self.BUTTON_CROUCH = 0;
1031 self.movement = '0 0 0';
1032 self.bot_cmd_keys = BOT_CMD_KEY_NONE;
1034 self.bot_exec_status |= BOT_EXEC_STATUS_PAUSED;
1035 return CMD_STATUS_FINISHED;
1038 float bot_cmd_moveto()
1040 return self.cmd_moveto(bot_cmd.bot_cmd_parm_vector);
1043 float bot_cmd_movetotarget()
1046 e = bot_getplace(bot_cmd.bot_cmd_parm_string);
1048 return CMD_STATUS_ERROR;
1049 return self.cmd_moveto(e.origin + (e.mins + e.maxs) * 0.5);
1052 float bot_cmd_resetgoal()
1054 return self.cmd_resetgoal();
1058 float bot_cmd_sound()
1061 f = bot_cmd.bot_cmd_parm_string;
1064 sound(self, CHAN_WEAPON2, f, VOL_BASE, ATTN_MIN);
1066 return CMD_STATUS_FINISHED;
1071 void bot_command_executed(float rm)
1078 bot_dequeuecommand(self, self.bot_cmd_execution_index);
1080 self.bot_cmd_execution_index++;
1083 void bot_setcurrentcommand()
1087 if(!self.bot_cmd_current)
1089 self.bot_cmd_current = spawn();
1090 self.bot_cmd_current.classname = "bot_cmd";
1091 self.bot_cmd_current.is_bot_cmd = 1;
1094 bot_cmd = self.bot_cmd_current;
1095 if(bot_cmd.bot_cmd_index != self.bot_cmd_execution_index || self.bot_cmd_execution_index == 0)
1097 if(bot_havecommand(self, self.bot_cmd_execution_index))
1100 cmdstring = bot_readcommand(self, self.bot_cmd_execution_index);
1101 if(bot_decodecommand(cmdstring))
1103 bot_cmd.owner = self;
1104 bot_cmd.bot_cmd_index = self.bot_cmd_execution_index;
1108 // Invalid command, remove from queue
1110 bot_dequeuecommand(self, self.bot_cmd_execution_index);
1111 self.bot_cmd_execution_index++;
1119 void bot_resetqueues()
1124 FOR_EACH_CLIENT(cl) if(cl.isbot)
1126 if(cl.bot_cmdqueuebuf_allocated)
1128 // also, cancel all barriers
1130 for(i = 0; i < cl.bot_places_count; ++i)
1132 strunzone(cl.(bot_placenames[i]));
1133 cl.(bot_placenames[i]) = string_null;
1135 cl.bot_places_count = 0;
1138 bot_barriertime = time;
1141 // Here we map commands to functions and deal with complex interactions between commands and execution states
1142 // NOTE: Of course you need to include your commands here too :)
1143 float bot_execute_commands_once()
1145 local float status, ispressingkey;
1147 if(self.deadflag!=DEAD_NO)
1151 bot_setcurrentcommand();
1153 // Ignore all commands except continue when the bot is paused
1154 if(self.bot_exec_status & BOT_EXEC_STATUS_PAUSED)
1155 if(bot_cmd.bot_cmd_type!=BOT_CMD_CONTINUE)
1157 if(bot_cmd.bot_cmd_type!=BOT_CMD_NULL)
1159 bot_command_executed(TRUE);
1160 print( "WARNING: Commands are ignored while the bot is paused. Use the command 'continue' instead.\n");
1165 // Keep pressing keys raised by the "presskey" command
1166 ispressingkey = !!bot_presskeys();
1169 return ispressingkey;
1171 // Handle conditions
1172 if not(bot_cmd.bot_cmd_type==BOT_CMD_FI||bot_cmd.bot_cmd_type==BOT_CMD_ELSE)
1173 if(self.bot_cmd_condition_status & CMD_CONDITION_TRUE && self.bot_cmd_condition_status & CMD_CONDITION_FALSE_BLOCK)
1175 bot_command_executed(TRUE);
1178 else if(self.bot_cmd_condition_status & CMD_CONDITION_FALSE && self.bot_cmd_condition_status & CMD_CONDITION_TRUE_BLOCK)
1180 bot_command_executed(TRUE);
1184 // Map commands to functions
1185 switch(bot_cmd.bot_cmd_type)
1188 return ispressingkey;
1191 status = bot_cmd_pause();
1193 case BOT_CMD_CONTINUE:
1194 status = bot_cmd_continue();
1197 status = bot_cmd_wait();
1199 case BOT_CMD_WAIT_UNTIL:
1200 status = bot_cmd_wait_until();
1203 status = bot_cmd_turn();
1205 case BOT_CMD_MOVETO:
1206 status = bot_cmd_moveto();
1208 case BOT_CMD_MOVETOTARGET:
1209 status = bot_cmd_movetotarget();
1211 case BOT_CMD_RESETGOAL:
1212 status = bot_cmd_resetgoal();
1215 status = bot_cmd_cc();
1218 status = bot_cmd_if();
1221 status = bot_cmd_else();
1224 status = bot_cmd_fi();
1226 case BOT_CMD_RESETAIM:
1227 status = bot_cmd_resetaim();
1230 status = bot_cmd_aim();
1232 case BOT_CMD_AIMTARGET:
1233 status = bot_cmd_aimtarget();
1235 case BOT_CMD_PRESSKEY:
1236 status = bot_cmd_presskey();
1238 case BOT_CMD_RELEASEKEY:
1239 status = bot_cmd_releasekey();
1241 case BOT_CMD_SELECTWEAPON:
1242 status = bot_cmd_select_weapon();
1244 case BOT_CMD_IMPULSE:
1245 status = bot_cmd_impulse();
1247 case BOT_CMD_BARRIER:
1248 status = bot_cmd_barrier();
1250 case BOT_CMD_CONSOLE:
1251 localcmd(strcat(bot_cmd.bot_cmd_parm_string, "\n"));
1252 status = CMD_STATUS_FINISHED;
1255 status = bot_cmd_sound();
1258 print(strcat("ERROR: Invalid command on queue with id '",ftos(bot_cmd.bot_cmd_type),"'\n"));
1262 if (status==CMD_STATUS_ERROR)
1263 print(strcat("ERROR: The command '",bot_cmd_string[bot_cmd.bot_cmd_type],"' returned an error status\n"));
1265 // Move execution pointer
1266 if(status==CMD_STATUS_EXECUTING)
1272 if(cvar("g_debug_bot_commands"))
1276 switch(bot_cmd_parm_type[bot_cmd.bot_cmd_type])
1278 case BOT_CMD_PARAMETER_FLOAT:
1279 parms = ftos(bot_cmd.bot_cmd_parm_float);
1281 case BOT_CMD_PARAMETER_STRING:
1282 parms = bot_cmd.bot_cmd_parm_string;
1284 case BOT_CMD_PARAMETER_VECTOR:
1285 parms = vtos(bot_cmd.bot_cmd_parm_vector);
1291 clientcommand(self,strcat("say ^7", bot_cmd_string[bot_cmd.bot_cmd_type]," ",parms,"\n"));
1294 bot_command_executed(TRUE);
1297 if(status == CMD_STATUS_FINISHED)
1300 return CMD_STATUS_ERROR;
1303 // This function should be (the only) called directly from the bot ai loop
1304 float bot_execute_commands()
1309 f = bot_execute_commands_once();