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("readcommand but no queue allocated");
10 buf_del(bot.bot_cmdqueuebuf);
11 bot.bot_cmdqueuebuf_allocated = FALSE;
12 print("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_start = 0;
21 bot.bot_cmdqueuebuf_end = 0;
22 bot.bot_cmdqueuebuf_allocated = TRUE;
25 bufstr_set(bot.bot_cmdqueuebuf, bot.bot_cmdqueuebuf_end, cmdstring);
26 bot.bot_cmdqueuebuf_end += 1;
29 void bot_dequeuecommand(entity bot, float idx)
31 if(!bot.bot_cmdqueuebuf_allocated)
32 error("dequeuecommand but no queue allocated");
33 if(idx < bot.bot_cmdqueuebuf_start)
34 error("dequeueing a command in the past");
35 if(idx >= bot.bot_cmdqueuebuf_end)
36 error("dequeueing a command in the future");
37 bufstr_set(bot.bot_cmdqueuebuf, idx, "");
38 if(idx == bot.bot_cmdqueuebuf_start)
39 bot.bot_cmdqueuebuf_start += 1;
40 if(bot.bot_cmdqueuebuf_start >= bot.bot_cmdqueuebuf_end)
44 string bot_readcommand(entity bot, float idx)
46 if(!bot.bot_cmdqueuebuf_allocated)
47 error("readcommand but no queue allocated");
48 if(idx < bot.bot_cmdqueuebuf_start)
49 error("reading a command in the past");
50 if(idx >= bot.bot_cmdqueuebuf_end)
51 error("reading a command in the future");
52 return bufstr_get(bot.bot_cmdqueuebuf, idx);
55 float bot_havecommand(entity bot, float idx)
57 if(!bot.bot_cmdqueuebuf_allocated)
59 if(idx < bot.bot_cmdqueuebuf_start)
61 if(idx >= bot.bot_cmdqueuebuf_end)
67 // NOTE: New commands should be added here. Do not forget to update BOT_CMD_COUNTER
68 #define BOT_CMD_NULL 0
69 #define BOT_CMD_PAUSE 1
70 #define BOT_CMD_CONTINUE 2
71 #define BOT_CMD_WAIT 3
72 #define BOT_CMD_TURN 4
73 #define BOT_CMD_MOVETO 5 // Not implemented yet
74 #define BOT_CMD_RESETGOAL 6 // Not implemented yet
77 #define BOT_CMD_ELSE 9
79 #define BOT_CMD_RESETAIM 11
80 #define BOT_CMD_AIM 12
81 #define BOT_CMD_PRESSKEY 13
82 #define BOT_CMD_RELEASEKEY 14
83 #define BOT_CMD_SELECTWEAPON 15
84 #define BOT_CMD_IMPULSE 16
85 #define BOT_CMD_WAIT_UNTIL 17
86 #define BOT_CMD_MOVETOTARGET 18
87 #define BOT_CMD_AIMTARGET 19
88 #define BOT_CMD_BARRIER 20
89 #define BOT_CMD_CONSOLE 21
90 #define BOT_CMD_WHILE 22 // TODO: Not implemented yet
91 #define BOT_CMD_WEND 23 // TODO: Not implemented yet
92 #define BOT_CMD_CHASE 24 // TODO: Not implemented yet
94 #define BOT_CMD_COUNTER 22 // Update this value if you add/remove a command
96 // NOTE: Following commands should be implemented on the bot ai
97 // If a new command should be handled by the target ai(s) please declare it here
98 .float(vector) cmd_moveto;
99 .float() cmd_resetgoal;
102 #define BOT_CMD_PARAMETER_NONE 0
103 #define BOT_CMD_PARAMETER_FLOAT 1
104 #define BOT_CMD_PARAMETER_STRING 2
105 #define BOT_CMD_PARAMETER_VECTOR 3
107 float bot_cmds_initialized;
108 float bot_cmd_parm_type[BOT_CMD_COUNTER];
109 string bot_cmd_string[BOT_CMD_COUNTER];
111 // Bots command queue
112 entity bot_cmd; // global current command
113 .entity bot_cmd_current; // current command of this bot
115 .float is_bot_cmd; // Tells if the entity is a bot command
116 .float bot_cmd_index; // Position of the command in the queue
117 .float bot_cmd_type; // If of command (see the BOT_CMD_* defines)
118 .float bot_cmd_parm_float; // Field to store a float parameter
119 .string bot_cmd_parm_string; // Field to store a string parameter
120 .vector bot_cmd_parm_vector; // Field to store a vector parameter
122 float bot_barriertime;
125 .float bot_cmd_execution_index; // Position in the queue of the command to be executed
127 // Initialize global commands list
128 // NOTE: New commands should be initialized here
129 void bot_commands_init()
131 bot_cmd_string[BOT_CMD_NULL] = "";
132 bot_cmd_parm_type[BOT_CMD_NULL] = BOT_CMD_PARAMETER_NONE;
134 bot_cmd_string[BOT_CMD_PAUSE] = "pause";
135 bot_cmd_parm_type[BOT_CMD_PAUSE] = BOT_CMD_PARAMETER_NONE;
137 bot_cmd_string[BOT_CMD_CONTINUE] = "continue";
138 bot_cmd_parm_type[BOT_CMD_CONTINUE] = BOT_CMD_PARAMETER_NONE;
140 bot_cmd_string[BOT_CMD_WAIT] = "wait";
141 bot_cmd_parm_type[BOT_CMD_WAIT] = BOT_CMD_PARAMETER_FLOAT;
143 bot_cmd_string[BOT_CMD_TURN] = "turn";
144 bot_cmd_parm_type[BOT_CMD_TURN] = BOT_CMD_PARAMETER_FLOAT;
146 bot_cmd_string[BOT_CMD_MOVETO] = "moveto";
147 bot_cmd_parm_type[BOT_CMD_MOVETO] = BOT_CMD_PARAMETER_VECTOR;
149 bot_cmd_string[BOT_CMD_MOVETOTARGET] = "movetotarget";
150 bot_cmd_parm_type[BOT_CMD_MOVETOTARGET] = BOT_CMD_PARAMETER_STRING;
152 bot_cmd_string[BOT_CMD_RESETGOAL] = "resetgoal";
153 bot_cmd_parm_type[BOT_CMD_RESETGOAL] = BOT_CMD_PARAMETER_NONE;
155 bot_cmd_string[BOT_CMD_CC] = "cc";
156 bot_cmd_parm_type[BOT_CMD_CC] = BOT_CMD_PARAMETER_STRING;
158 bot_cmd_string[BOT_CMD_IF] = "if";
159 bot_cmd_parm_type[BOT_CMD_IF] = BOT_CMD_PARAMETER_STRING;
161 bot_cmd_string[BOT_CMD_ELSE] = "else";
162 bot_cmd_parm_type[BOT_CMD_ELSE] = BOT_CMD_PARAMETER_NONE;
164 bot_cmd_string[BOT_CMD_FI] = "fi";
165 bot_cmd_parm_type[BOT_CMD_FI] = BOT_CMD_PARAMETER_NONE;
167 bot_cmd_string[BOT_CMD_RESETAIM] = "resetaim";
168 bot_cmd_parm_type[BOT_CMD_RESETAIM] = BOT_CMD_PARAMETER_NONE;
170 bot_cmd_string[BOT_CMD_AIM] = "aim";
171 bot_cmd_parm_type[BOT_CMD_AIM] = BOT_CMD_PARAMETER_STRING;
173 bot_cmd_string[BOT_CMD_AIMTARGET] = "aimtarget";
174 bot_cmd_parm_type[BOT_CMD_AIMTARGET] = BOT_CMD_PARAMETER_STRING;
176 bot_cmd_string[BOT_CMD_PRESSKEY] = "presskey";
177 bot_cmd_parm_type[BOT_CMD_PRESSKEY] = BOT_CMD_PARAMETER_STRING;
179 bot_cmd_string[BOT_CMD_RELEASEKEY] = "releasekey";
180 bot_cmd_parm_type[BOT_CMD_RELEASEKEY] = BOT_CMD_PARAMETER_STRING;
182 bot_cmd_string[BOT_CMD_SELECTWEAPON] = "selectweapon";
183 bot_cmd_parm_type[BOT_CMD_SELECTWEAPON] = BOT_CMD_PARAMETER_FLOAT;
185 bot_cmd_string[BOT_CMD_IMPULSE] = "impulse";
186 bot_cmd_parm_type[BOT_CMD_IMPULSE] = BOT_CMD_PARAMETER_FLOAT;
188 bot_cmd_string[BOT_CMD_WAIT_UNTIL] = "wait_until";
189 bot_cmd_parm_type[BOT_CMD_WAIT_UNTIL] = BOT_CMD_PARAMETER_FLOAT;
191 bot_cmd_string[BOT_CMD_BARRIER] = "barrier";
192 bot_cmd_parm_type[BOT_CMD_BARRIER] = BOT_CMD_PARAMETER_NONE;
194 bot_cmd_string[BOT_CMD_CONSOLE] = "console";
195 bot_cmd_parm_type[BOT_CMD_CONSOLE] = BOT_CMD_PARAMETER_STRING;
197 bot_cmds_initialized = TRUE;
200 // Returns first bot with matching name
201 entity find_bot_by_name(string name)
205 bot = findchainflags(flags, FL_CLIENT);
208 if(clienttype(bot) == CLIENTTYPE_BOT)
209 if(bot.netname==name)
218 // Returns a bot by number on list
219 entity find_bot_by_number(float number)
224 bot = findchainflags(flags, FL_CLIENT);
227 if(clienttype(bot) == CLIENTTYPE_BOT)
238 float bot_decodecommand(string cmdstring)
240 local float cmd_parm_type, i;
244 sp = strstrofs(cmdstring, " ", 0);
251 parm = substring(cmdstring, sp + 1, -1);
252 cmdstring = substring(cmdstring, 0, sp);
255 if(!bot_cmds_initialized)
258 for(i=1;i<BOT_CMD_COUNTER;++i)
260 if(bot_cmd_string[i]!=cmdstring)
263 cmd_parm_type = bot_cmd_parm_type[i];
265 if(cmd_parm_type!=BOT_CMD_PARAMETER_NONE&&parm=="")
267 print("ERROR: A parameter is required for this command\n");
271 // Load command into queue
272 bot_cmd.bot_cmd_type = i;
275 switch(cmd_parm_type)
277 case BOT_CMD_PARAMETER_FLOAT:
278 bot_cmd.bot_cmd_parm_float = stof(parm);
280 case BOT_CMD_PARAMETER_STRING:
281 if(bot_cmd.bot_cmd_parm_string)
282 strunzone(bot_cmd.bot_cmd_parm_string);
283 bot_cmd.bot_cmd_parm_string = strzone(parm);
285 case BOT_CMD_PARAMETER_VECTOR:
286 bot_cmd.bot_cmd_parm_vector = stov(parm);
293 print("ERROR: No such command '", cmdstring, "'\n");
297 void bot_cmdhelp(string scmd)
299 local float i, ntype;
302 if(!bot_cmds_initialized)
305 for(i=1;i<BOT_CMD_COUNTER;++i)
307 if(bot_cmd_string[i]!=scmd)
310 ntype = bot_cmd_parm_type[i];
314 case BOT_CMD_PARAMETER_FLOAT:
315 stype = "float number";
317 case BOT_CMD_PARAMETER_STRING:
320 case BOT_CMD_PARAMETER_VECTOR:
328 print(strcat("Command: ",bot_cmd_string[i],"\nParameter: <",stype,"> \n"));
330 print("Description: ");
334 print("Stops the bot completely. Any command other than 'continue' will be ignored.");
336 case BOT_CMD_CONTINUE:
337 print("Disable paused status");
340 print("Pause command parsing and bot ai for N seconds. Pressed key will remain pressed");
342 case BOT_CMD_WAIT_UNTIL:
343 print("Pause command parsing and bot ai until time is N from the last barrier. Pressed key will remain pressed");
345 case BOT_CMD_BARRIER:
346 print("Waits till all bots reach this command. Pressed key will remain pressed");
349 print("Look to the right or left N degrees. For turning to the left use positive numbers.");
352 print("Walk to an specific coordinate on the map. Usage: moveto \"x y z\"");
354 case BOT_CMD_MOVETOTARGET:
355 print("Walk to the specific target on the map");
357 case BOT_CMD_RESETGOAL:
358 print("Resets the goal stack");
361 print("Execute client command. Examples: cc \"say something\"; cc god; cc \"name newnickname\"; cc kill;");
364 print("Perform simple conditional execution.\n");
366 print(" sv_cmd .. if \"condition\"\n");
367 print(" sv_cmd .. <instruction if true>\n");
368 print(" sv_cmd .. <instruction if true>\n");
369 print(" sv_cmd .. else\n");
370 print(" sv_cmd .. <instruction if false>\n");
371 print(" sv_cmd .. <instruction if false>\n");
372 print(" sv_cmd .. fi\n");
373 print("Conditions: a=b, a>b, a<b, a\t\t(spaces not allowed)\n");
374 print(" Values in conditions can be numbers, cvars in the form cvar.cvar_string or special fields\n");
375 print("Fields: health, speed, flagcarrier\n");
376 print("Examples: if health>50; if health>cvar.g_balance_laser_primary_damage; if flagcarrier;");
378 case BOT_CMD_RESETAIM:
379 print("Points the aim to the coordinates x,y 0,0");
382 print("Move the aim x/y (horizontal/vertical) degrees relatives to the bot\n");
383 print("There is a 3rd optional parameter telling in how many seconds the aim has to reach the new position\n");
384 print("Examples: aim \"90 0\" // Turn 90 degrees inmediately (positive numbers move to the left/up)\n");
385 print(" aim \"0 90 2\" // Will gradually look to the sky in the next two seconds");
387 case BOT_CMD_AIMTARGET:
388 print("Points the aim to given target");
390 case BOT_CMD_PRESSKEY:
391 print("Press one of the following keys: forward, backward, left, right, jump, crouch, attack1, attack2, use\n");
392 print("Multiple keys can be pressed at time (with many presskey calls) and it will remain pressed until the command \"releasekey\" is called");
393 print("Note: The script will not return the control to the bot ai until all keys are released");
395 case BOT_CMD_RELEASEKEY:
396 print("Release previoulsy used keys. Use the parameter \"all\" to release all keys");
399 print("This command has no description yet.");
406 void bot_list_commands()
411 if(!bot_cmds_initialized)
414 print("List of all available commands:\n");
415 print(" Command\t\t\t\tParameter Type\n");
417 for(i=1;i<BOT_CMD_COUNTER;++i)
419 switch(bot_cmd_parm_type[i])
421 case BOT_CMD_PARAMETER_FLOAT:
422 ptype = "float number";
424 case BOT_CMD_PARAMETER_STRING:
427 case BOT_CMD_PARAMETER_VECTOR:
434 print(strcat(" ",bot_cmd_string[i],"\t\t\t\t<",ptype,"> \n"));
439 .float bot_exec_status;
441 #define BOT_EXEC_STATUS_IDLE 0
442 #define BOT_EXEC_STATUS_PAUSED 1
443 #define BOT_EXEC_STATUS_WAITING 2
445 #define CMD_STATUS_EXECUTING 0
446 #define CMD_STATUS_FINISHED 1
447 #define CMD_STATUS_ERROR 2
449 void SV_ParseClientCommand(string s);
452 SV_ParseClientCommand(bot_cmd.bot_cmd_parm_string);
453 return CMD_STATUS_FINISHED;
456 float bot_cmd_impulse()
458 self.impulse = bot_cmd.bot_cmd_parm_float;
459 return CMD_STATUS_FINISHED;
462 float bot_cmd_continue()
464 self.bot_exec_status &~= BOT_EXEC_STATUS_PAUSED;
465 return CMD_STATUS_FINISHED;
468 .float bot_cmd_wait_time;
471 if(self.bot_exec_status & BOT_EXEC_STATUS_WAITING)
473 if(time>=self.bot_cmd_wait_time)
475 self.bot_exec_status &~= BOT_EXEC_STATUS_WAITING;
476 return CMD_STATUS_FINISHED;
479 return CMD_STATUS_EXECUTING;
482 self.bot_cmd_wait_time = time + bot_cmd.bot_cmd_parm_float;
483 self.bot_exec_status |= BOT_EXEC_STATUS_WAITING;
484 return CMD_STATUS_EXECUTING;
487 float bot_cmd_wait_until()
489 if(time < bot_cmd.bot_cmd_parm_float + bot_barriertime)
491 self.bot_exec_status |= BOT_EXEC_STATUS_WAITING;
492 return CMD_STATUS_EXECUTING;
494 self.bot_exec_status &~= BOT_EXEC_STATUS_WAITING;
495 return CMD_STATUS_FINISHED;
498 float bot_cmd_barrier()
502 // 0 = no barrier, 1 = waiting, 2 = waiting finished
504 if(self.bot_barrier == 0) // initialization
506 self.bot_barrier = 1;
508 //self.colormod = '4 4 0';
511 if(self.bot_barrier == 1) // find other bots
513 FOR_EACH_CLIENT(cl) if(cl.isbot)
515 if(cl.bot_barrier != 1)
516 return CMD_STATUS_EXECUTING; // not all are at the barrier yet
519 // all bots hit the barrier!
520 FOR_EACH_CLIENT(cl) if(cl.isbot)
522 cl.bot_barrier = 2; // acknowledge barrier
525 bot_barriertime = time;
528 // if we get here, the barrier is finished
530 self.bot_barrier = 0;
531 //self.colormod = '0 0 0';
533 return CMD_STATUS_FINISHED;
538 self.v_angle_y = self.v_angle_y + bot_cmd.bot_cmd_parm_float;
539 self.v_angle_y = self.v_angle_y - floor(self.v_angle_y / 360) * 360;
540 return CMD_STATUS_FINISHED;
543 float bot_cmd_select_weapon()
547 id = bot_cmd.bot_cmd_parm_float;
549 if(id < WEP_FIRST || id > WEP_LAST)
550 return CMD_STATUS_ERROR;
552 if(client_hasweapon(self, id, TRUE, FALSE))
553 self.switchweapon = id;
555 return CMD_STATUS_ERROR;
557 return CMD_STATUS_FINISHED;
560 .float bot_cmd_condition_status;
562 #define CMD_CONDITION_NONE 0
563 #define CMD_CONDITION_TRUE 1
564 #define CMD_CONDITION_FALSE 2
565 #define CMD_CONDITION_TRUE_BLOCK 4
566 #define CMD_CONDITION_FALSE_BLOCK 8
568 float bot_cmd_eval(string expr)
570 // Search for numbers
571 if(strstrofs("0123456789", substring(expr, 0, 1), 0) >= 0)
577 if(substring(expr, 0, 5)=="cvar.")
579 return cvar(substring(expr, 5, strlen(expr)));
588 return vlen(self.velocity);
590 return ((self.flagcarried!=world));
593 print(strcat("ERROR: Unable to convert the expression '",expr,"' into a numeric value\n"));
599 local string expr, val_a, val_b;
602 if(self.bot_cmd_condition_status != CMD_CONDITION_NONE)
604 // Only one "if" block is allowed at time
605 print("ERROR: Only one conditional block can be processed at time");
606 bot_clearqueue(self);
607 return CMD_STATUS_ERROR;
610 self.bot_cmd_condition_status |= CMD_CONDITION_TRUE_BLOCK;
612 // search for operators
613 expr = bot_cmd.bot_cmd_parm_string;
615 cmpofs = strstrofs(expr,"=",0);
619 val_a = substring(expr,0,cmpofs);
620 val_b = substring(expr,cmpofs+1,strlen(expr));
622 if(bot_cmd_eval(val_a)==bot_cmd_eval(val_b))
623 self.bot_cmd_condition_status |= CMD_CONDITION_TRUE;
625 self.bot_cmd_condition_status |= CMD_CONDITION_FALSE;
627 return CMD_STATUS_FINISHED;
630 cmpofs = strstrofs(expr,">",0);
634 val_a = substring(expr,0,cmpofs);
635 val_b = substring(expr,cmpofs+1,strlen(expr));
637 if(bot_cmd_eval(val_a)>bot_cmd_eval(val_b))
638 self.bot_cmd_condition_status |= CMD_CONDITION_TRUE;
640 self.bot_cmd_condition_status |= CMD_CONDITION_FALSE;
642 return CMD_STATUS_FINISHED;
645 cmpofs = strstrofs(expr,"<",0);
649 val_a = substring(expr,0,cmpofs);
650 val_b = substring(expr,cmpofs+1,strlen(expr));
652 if(bot_cmd_eval(val_a)<bot_cmd_eval(val_b))
653 self.bot_cmd_condition_status |= CMD_CONDITION_TRUE;
655 self.bot_cmd_condition_status |= CMD_CONDITION_FALSE;
657 return CMD_STATUS_FINISHED;
660 if(bot_cmd_eval(expr))
661 self.bot_cmd_condition_status |= CMD_CONDITION_TRUE;
663 self.bot_cmd_condition_status |= CMD_CONDITION_FALSE;
665 return CMD_STATUS_FINISHED;
670 self.bot_cmd_condition_status &~= CMD_CONDITION_TRUE_BLOCK;
671 self.bot_cmd_condition_status |= CMD_CONDITION_FALSE_BLOCK;
672 return CMD_STATUS_FINISHED;
677 self.bot_cmd_condition_status = CMD_CONDITION_NONE;
678 return CMD_STATUS_FINISHED;
681 float bot_cmd_resetaim()
683 self.v_angle = '0 0 0';
684 return CMD_STATUS_FINISHED;
687 .float bot_cmd_aim_begintime;
688 .float bot_cmd_aim_endtime;
689 .vector bot_cmd_aim_begin;
690 .vector bot_cmd_aim_end;
695 if(self.bot_cmd_aim_endtime)
697 local float progress;
699 progress = min(1 - (self.bot_cmd_aim_endtime - time) / (self.bot_cmd_aim_endtime - self.bot_cmd_aim_begintime),1);
700 self.v_angle = self.bot_cmd_aim_begin + ((self.bot_cmd_aim_end - self.bot_cmd_aim_begin) * progress);
702 if(time>=self.bot_cmd_aim_endtime)
704 self.bot_cmd_aim_endtime = 0;
705 return CMD_STATUS_FINISHED;
708 return CMD_STATUS_EXECUTING;
711 // New aiming direction
713 local float tokens, step;
715 parms = bot_cmd.bot_cmd_parm_string;
717 tokens = tokenizebyseparator(parms, " ");
721 self.v_angle_x -= stof(argv(1));
722 self.v_angle_y += stof(argv(0));
723 return CMD_STATUS_FINISHED;
726 if(tokens<2||tokens>3)
727 return CMD_STATUS_ERROR;
729 step = stof(argv(2));
731 self.bot_cmd_aim_begin = self.v_angle;
733 self.bot_cmd_aim_end_x = self.v_angle_x - stof(argv(1));
734 self.bot_cmd_aim_end_y = self.v_angle_y + stof(argv(0));
735 self.bot_cmd_aim_end_z = 0;
737 self.bot_cmd_aim_begintime = time;
738 self.bot_cmd_aim_endtime = time + step;
740 return CMD_STATUS_EXECUTING;
743 float bot_cmd_aimtarget()
745 if(self.bot_cmd_aim_endtime)
747 return bot_cmd_aim();
753 local float tokens, step;
755 parms = bot_cmd.bot_cmd_parm_string;
757 tokens = tokenizebyseparator(parms, " ");
759 e = find(world, targetname, argv(0));
761 return CMD_STATUS_ERROR;
763 v = e.origin + (e.mins + e.maxs) * 0.5;
767 self.v_angle = vectoangles(v - (self.origin + self.view_ofs));
768 self.v_angle_x = -self.v_angle_x;
769 return CMD_STATUS_FINISHED;
772 if(tokens<1||tokens>2)
773 return CMD_STATUS_ERROR;
775 step = stof(argv(1));
777 self.bot_cmd_aim_begin = self.v_angle;
778 self.bot_cmd_aim_end = vectoangles(v - (self.origin + self.view_ofs));
779 self.bot_cmd_aim_end_x = -self.bot_cmd_aim_end_x;
781 self.bot_cmd_aim_begintime = time;
782 self.bot_cmd_aim_endtime = time + step;
784 return CMD_STATUS_EXECUTING;
789 #define BOT_CMD_KEY_NONE 0
790 #define BOT_CMD_KEY_FORWARD 1
791 #define BOT_CMD_KEY_BACKWARD 2
792 #define BOT_CMD_KEY_RIGHT 4
793 #define BOT_CMD_KEY_LEFT 8
794 #define BOT_CMD_KEY_JUMP 16
795 #define BOT_CMD_KEY_ATTACK1 32
796 #define BOT_CMD_KEY_ATTACK2 64
797 #define BOT_CMD_KEY_USE 128
798 #define BOT_CMD_KEY_HOOK 256
799 #define BOT_CMD_KEY_CROUCH 512
800 #define BOT_CMD_KEY_CHAT 1024
802 float bot_presskeys()
804 self.movement = '0 0 0';
806 if(self.bot_cmd_keys == BOT_CMD_KEY_NONE)
809 if(self.bot_cmd_keys & BOT_CMD_KEY_FORWARD)
810 self.movement_x = cvar("sv_maxspeed");
811 else if(self.bot_cmd_keys & BOT_CMD_KEY_BACKWARD)
812 self.movement_x = -cvar("sv_maxspeed");
814 if(self.bot_cmd_keys & BOT_CMD_KEY_RIGHT)
815 self.movement_y = cvar("sv_maxspeed");
816 else if(self.bot_cmd_keys & BOT_CMD_KEY_LEFT)
817 self.movement_y = -cvar("sv_maxspeed");
819 if(self.bot_cmd_keys & BOT_CMD_KEY_JUMP)
820 self.BUTTON_JUMP = TRUE;
822 if(self.bot_cmd_keys & BOT_CMD_KEY_CROUCH)
823 self.BUTTON_CROUCH = TRUE;
825 if(self.bot_cmd_keys & BOT_CMD_KEY_ATTACK1)
826 self.BUTTON_ATCK = TRUE;
828 if(self.bot_cmd_keys & BOT_CMD_KEY_ATTACK2)
829 self.BUTTON_ATCK2 = TRUE;
831 if(self.bot_cmd_keys & BOT_CMD_KEY_USE)
832 self.BUTTON_USE = TRUE;
834 if(self.bot_cmd_keys & BOT_CMD_KEY_HOOK)
835 self.BUTTON_HOOK = TRUE;
837 if(self.bot_cmd_keys & BOT_CMD_KEY_CHAT)
838 self.BUTTON_CHAT = TRUE;
844 float bot_cmd_keypress_handler(string key, float enabled)
850 self.bot_cmd_keys = power2of(20) - 1; // >:)
852 self.bot_cmd_keys = BOT_CMD_KEY_NONE;
856 self.bot_cmd_keys |= BOT_CMD_KEY_FORWARD;
857 self.bot_cmd_keys &~= BOT_CMD_KEY_BACKWARD;
860 self.bot_cmd_keys &~= BOT_CMD_KEY_FORWARD;
865 self.bot_cmd_keys |= BOT_CMD_KEY_BACKWARD;
866 self.bot_cmd_keys &~= BOT_CMD_KEY_FORWARD;
869 self.bot_cmd_keys &~= BOT_CMD_KEY_BACKWARD;
874 self.bot_cmd_keys |= BOT_CMD_KEY_LEFT;
875 self.bot_cmd_keys &~= BOT_CMD_KEY_RIGHT;
878 self.bot_cmd_keys &~= BOT_CMD_KEY_LEFT;
883 self.bot_cmd_keys |= BOT_CMD_KEY_RIGHT;
884 self.bot_cmd_keys &~= BOT_CMD_KEY_LEFT;
887 self.bot_cmd_keys &~= BOT_CMD_KEY_RIGHT;
891 self.bot_cmd_keys |= BOT_CMD_KEY_JUMP;
893 self.bot_cmd_keys &~= BOT_CMD_KEY_JUMP;
897 self.bot_cmd_keys |= BOT_CMD_KEY_CROUCH;
899 self.bot_cmd_keys &~= BOT_CMD_KEY_CROUCH;
903 self.bot_cmd_keys |= BOT_CMD_KEY_ATTACK1;
905 self.bot_cmd_keys &~= BOT_CMD_KEY_ATTACK1;
909 self.bot_cmd_keys |= BOT_CMD_KEY_ATTACK2;
911 self.bot_cmd_keys &~= BOT_CMD_KEY_ATTACK2;
915 self.bot_cmd_keys |= BOT_CMD_KEY_USE;
917 self.bot_cmd_keys &~= BOT_CMD_KEY_USE;
921 self.bot_cmd_keys |= BOT_CMD_KEY_HOOK;
923 self.bot_cmd_keys &~= BOT_CMD_KEY_HOOK;
927 self.bot_cmd_keys |= BOT_CMD_KEY_CHAT;
929 self.bot_cmd_keys &~= BOT_CMD_KEY_CHAT;
935 return CMD_STATUS_FINISHED;
938 float bot_cmd_presskey()
942 key = bot_cmd.bot_cmd_parm_string;
944 bot_cmd_keypress_handler(key,TRUE);
946 return CMD_STATUS_FINISHED;
949 float bot_cmd_releasekey()
953 key = bot_cmd.bot_cmd_parm_string;
955 return bot_cmd_keypress_handler(key,FALSE);
958 float bot_cmd_pause()
963 self.BUTTON_ATCK = 0;
964 self.BUTTON_JUMP = 0;
965 self.BUTTON_HOOK = 0;
966 self.BUTTON_CHAT = 0;
967 self.BUTTON_ATCK2 = 0;
968 self.BUTTON_CROUCH = 0;
970 self.movement = '0 0 0';
971 self.bot_cmd_keys = BOT_CMD_KEY_NONE;
973 self.bot_exec_status = self.bot_exec_status | BOT_EXEC_STATUS_PAUSED;
974 return CMD_STATUS_FINISHED;
977 float bot_cmd_moveto()
979 return self.cmd_moveto(bot_cmd.bot_cmd_parm_vector);
982 float bot_cmd_movetotarget()
985 e = find(world, targetname, bot_cmd.bot_cmd_parm_string);
987 return CMD_STATUS_ERROR;
988 return self.cmd_moveto(e.origin + (e.mins + e.maxs) * 0.5);
991 float bot_cmd_resetgoal()
993 return self.cmd_resetgoal();
1004 void bot_command_executed(float rm)
1011 bot_dequeuecommand(self, self.bot_cmd_execution_index);
1013 self.bot_cmd_execution_index++;
1016 void bot_setcurrentcommand()
1020 if(self.bot_cmd_execution_index == 0)
1021 self.bot_cmd_execution_index = 1;
1023 if(!self.bot_cmd_current)
1025 self.bot_cmd_current = spawn();
1026 self.bot_cmd_current.classname = "bot_cmd";
1027 self.bot_cmd_current.is_bot_cmd = 1;
1028 self.bot_cmd_current.bot_cmd_index = 0;
1031 bot_cmd = self.bot_cmd_current;
1032 if(bot_cmd.bot_cmd_index != self.bot_cmd_execution_index)
1034 if(bot_havecommand(self, self.bot_cmd_execution_index))
1037 cmdstring = bot_readcommand(self, self.bot_cmd_execution_index);
1038 if(bot_decodecommand(cmdstring))
1040 bot_cmd.owner = self;
1041 bot_cmd.bot_cmd_index = self.bot_cmd_execution_index;
1051 void bot_resetqueues()
1055 FOR_EACH_CLIENT(cl) if(cl.isbot)
1058 // also, cancel all barriers
1059 FOR_EACH_CLIENT(cl) if(cl.isbot)
1065 bot_barriertime = time;
1068 // This function should be (the only) called directly from the bot ai loop
1069 // It maps commands to functions and deal with complex interactions between commands and execution states
1070 // NOTE: Of course you need to include your commands here too :)
1071 float bot_execute_commands()
1073 local float status, ispressingkey;
1078 if(self.deadflag!=DEAD_NO)
1082 bot_setcurrentcommand();
1084 // Keep pressing keys raised by the "presskey" command
1085 ispressingkey = bot_presskeys();
1088 return ispressingkey;
1090 // Ignore all commands except continue when the bot is paused
1091 if(self.bot_exec_status & BOT_EXEC_STATUS_PAUSED)
1092 if(bot_cmd.bot_cmd_type!=BOT_CMD_CONTINUE)
1094 if(bot_cmd.bot_cmd_type!=BOT_CMD_NULL)
1096 bot_command_executed(TRUE);
1097 print( "WARNING: Commands are ignored while the bot is paused. Use the command 'continue' instead.\n");
1102 // Handle conditions
1103 if not(bot_cmd.bot_cmd_type==BOT_CMD_FI||bot_cmd.bot_cmd_type==BOT_CMD_ELSE)
1104 if(self.bot_cmd_condition_status & CMD_CONDITION_TRUE && self.bot_cmd_condition_status & CMD_CONDITION_FALSE_BLOCK)
1106 bot_command_executed(TRUE);
1109 else if(self.bot_cmd_condition_status & CMD_CONDITION_FALSE && self.bot_cmd_condition_status & CMD_CONDITION_TRUE_BLOCK)
1111 bot_command_executed(TRUE);
1115 // Map commands to functions
1116 switch(bot_cmd.bot_cmd_type)
1119 return ispressingkey;
1122 status = bot_cmd_pause();
1124 case BOT_CMD_CONTINUE:
1125 status = bot_cmd_continue();
1128 status = bot_cmd_wait();
1130 case BOT_CMD_WAIT_UNTIL:
1131 status = bot_cmd_wait_until();
1134 status = bot_cmd_turn();
1136 case BOT_CMD_MOVETO:
1137 status = bot_cmd_moveto();
1139 case BOT_CMD_MOVETOTARGET:
1140 status = bot_cmd_movetotarget();
1142 case BOT_CMD_RESETGOAL:
1143 status = bot_cmd_resetgoal();
1146 status = bot_cmd_cc();
1149 status = bot_cmd_if();
1152 status = bot_cmd_else();
1155 status = bot_cmd_fi();
1157 case BOT_CMD_RESETAIM:
1158 status = bot_cmd_resetaim();
1161 status = bot_cmd_aim();
1163 case BOT_CMD_AIMTARGET:
1164 status = bot_cmd_aimtarget();
1166 case BOT_CMD_PRESSKEY:
1167 status = bot_cmd_presskey();
1169 case BOT_CMD_RELEASEKEY:
1170 status = bot_cmd_releasekey();
1172 case BOT_CMD_SELECTWEAPON:
1173 status = bot_cmd_select_weapon();
1175 case BOT_CMD_IMPULSE:
1176 status = bot_cmd_impulse();
1178 case BOT_CMD_BARRIER:
1179 status = bot_cmd_barrier();
1181 case BOT_CMD_CONSOLE:
1182 localcmd(strcat(bot_cmd.bot_cmd_parm_string, "\n"));
1183 status = CMD_STATUS_FINISHED;
1186 print(strcat("ERROR: Invalid command on queue with id '",ftos(bot_cmd.bot_cmd_type),"'\n"));
1190 if (status==CMD_STATUS_ERROR)
1191 print(strcat("ERROR: The command '",bot_cmd_string[bot_cmd.bot_cmd_type],"' returned an error status\n"));
1193 // Move execution pointer
1194 if(status==CMD_STATUS_EXECUTING)
1200 if(cvar("g_debug_bot_commands"))
1204 switch(bot_cmd_parm_type[bot_cmd.bot_cmd_type])
1206 case BOT_CMD_PARAMETER_FLOAT:
1207 parms = ftos(bot_cmd.bot_cmd_parm_float);
1209 case BOT_CMD_PARAMETER_STRING:
1210 parms = bot_cmd.bot_cmd_parm_string;
1212 case BOT_CMD_PARAMETER_VECTOR:
1213 parms = vtos(bot_cmd.bot_cmd_parm_vector);
1219 clientcommand(self,strcat("say ^7", bot_cmd_string[bot_cmd.bot_cmd_type]," ",parms,"\n"));
1222 bot_command_executed(TRUE);
1225 } while(status==CMD_STATUS_FINISHED);