1 // NOTE: New commands should be added here. Do not forget to update BOT_CMD_COUNTER
3 #define BOT_CMD_PAUSE 1
4 #define BOT_CMD_CONTINUE 2
7 #define BOT_CMD_MOVETO 5 // Not implemented yet
8 #define BOT_CMD_RESETGOAL 6 // Not implemented yet
11 #define BOT_CMD_ELSE 9
13 #define BOT_CMD_RESETAIM 11
14 #define BOT_CMD_AIM 12
15 #define BOT_CMD_PRESSKEY 13
16 #define BOT_CMD_RELEASEKEY 14
17 #define BOT_CMD_SELECTWEAPON 15
18 #define BOT_CMD_IMPULSE 16
19 #define BOT_CMD_WAIT_UNTIL 17
20 #define BOT_CMD_RESETQUEUE 18
21 #define BOT_CMD_MOVETOTARGET 19
22 #define BOT_CMD_AIMTARGET 20
23 #define BOT_CMD_WHILE 21 // TODO: Not implemented yet
24 #define BOT_CMD_WEND 22 // TODO: Not implemented yet
25 #define BOT_CMD_CHASE 23 // TODO: Not implemented yet
27 #define BOT_CMD_COUNTER 21 // Update this value if you add/remove a command
29 // NOTE: Following commands should be implemented on the bot ai
30 // If a new command should be handled by the target ai(s) please declare it here
31 .float(vector) cmd_moveto;
32 .float() cmd_resetgoal;
35 #define BOT_CMD_PARAMETER_NONE 0
36 #define BOT_CMD_PARAMETER_FLOAT 1
37 #define BOT_CMD_PARAMETER_STRING 2
38 #define BOT_CMD_PARAMETER_VECTOR 3
39 #define BOT_CMD_PARAMETER_FLOAT_RELATIVE_TO_TIME 4
41 float bot_cmds_initialized;
42 float bot_cmd_parm_type[BOT_CMD_COUNTER];
43 string bot_cmd_string[BOT_CMD_COUNTER];
46 entity bot_cmd; // Current command
48 .float is_bot_cmd; // Tells if the entity is a bot command
49 .float bot_cmd_index; // Position of the command in the queue
50 .float bot_cmd_type; // If of command (see the BOT_CMD_* defines)
51 .float bot_cmd_parm_float; // Field to store a float parameter
52 .string bot_cmd_parm_string; // Field to store a string parameter
53 .vector bot_cmd_parm_vector; // Field to store a vector parameter
54 .float bot_cmd_execution_counter; // How many times this command on the queue was executed
56 .float bot_cmd_execution_index; // Position in the queue of the command to be executed
57 .float bot_cmd_queue_index; // Position of the last command in the queue
59 .entity bot_cmd_next; // next command of the same bot (field on a command), or next command for this bot to execute (field on a bot)
60 .entity bot_cmd_newest; // last command of this bot (field on a bot, used for adding new commands)
62 // Initialize global commands list
63 // NOTE: New commands should be initialized here
64 void bot_commands_init()
66 bot_cmd_string[BOT_CMD_NULL] = "";
67 bot_cmd_parm_type[BOT_CMD_NULL] = BOT_CMD_PARAMETER_NONE;
69 bot_cmd_string[BOT_CMD_PAUSE] = "pause";
70 bot_cmd_parm_type[BOT_CMD_PAUSE] = BOT_CMD_PARAMETER_NONE;
72 bot_cmd_string[BOT_CMD_CONTINUE] = "continue";
73 bot_cmd_parm_type[BOT_CMD_CONTINUE] = BOT_CMD_PARAMETER_NONE;
75 bot_cmd_string[BOT_CMD_WAIT] = "wait";
76 bot_cmd_parm_type[BOT_CMD_WAIT] = BOT_CMD_PARAMETER_FLOAT;
78 bot_cmd_string[BOT_CMD_TURN] = "turn";
79 bot_cmd_parm_type[BOT_CMD_TURN] = BOT_CMD_PARAMETER_FLOAT;
81 bot_cmd_string[BOT_CMD_MOVETO] = "moveto";
82 bot_cmd_parm_type[BOT_CMD_MOVETO] = BOT_CMD_PARAMETER_VECTOR;
84 bot_cmd_string[BOT_CMD_MOVETOTARGET] = "movetotarget";
85 bot_cmd_parm_type[BOT_CMD_MOVETOTARGET] = BOT_CMD_PARAMETER_STRING;
87 bot_cmd_string[BOT_CMD_RESETGOAL] = "resetgoal";
88 bot_cmd_parm_type[BOT_CMD_RESETGOAL] = BOT_CMD_PARAMETER_NONE;
90 bot_cmd_string[BOT_CMD_CC] = "cc";
91 bot_cmd_parm_type[BOT_CMD_CC] = BOT_CMD_PARAMETER_STRING;
93 bot_cmd_string[BOT_CMD_IF] = "if";
94 bot_cmd_parm_type[BOT_CMD_IF] = BOT_CMD_PARAMETER_STRING;
96 bot_cmd_string[BOT_CMD_ELSE] = "else";
97 bot_cmd_parm_type[BOT_CMD_ELSE] = BOT_CMD_PARAMETER_NONE;
99 bot_cmd_string[BOT_CMD_FI] = "fi";
100 bot_cmd_parm_type[BOT_CMD_FI] = BOT_CMD_PARAMETER_NONE;
102 bot_cmd_string[BOT_CMD_RESETAIM] = "resetaim";
103 bot_cmd_parm_type[BOT_CMD_RESETAIM] = BOT_CMD_PARAMETER_NONE;
105 bot_cmd_string[BOT_CMD_AIM] = "aim";
106 bot_cmd_parm_type[BOT_CMD_AIM] = BOT_CMD_PARAMETER_STRING;
108 bot_cmd_string[BOT_CMD_AIMTARGET] = "aimtarget";
109 bot_cmd_parm_type[BOT_CMD_AIMTARGET] = BOT_CMD_PARAMETER_STRING;
111 bot_cmd_string[BOT_CMD_PRESSKEY] = "presskey";
112 bot_cmd_parm_type[BOT_CMD_PRESSKEY] = BOT_CMD_PARAMETER_STRING;
114 bot_cmd_string[BOT_CMD_RELEASEKEY] = "releasekey";
115 bot_cmd_parm_type[BOT_CMD_RELEASEKEY] = BOT_CMD_PARAMETER_STRING;
117 bot_cmd_string[BOT_CMD_SELECTWEAPON] = "selectweapon";
118 bot_cmd_parm_type[BOT_CMD_SELECTWEAPON] = BOT_CMD_PARAMETER_FLOAT;
120 bot_cmd_string[BOT_CMD_IMPULSE] = "impulse";
121 bot_cmd_parm_type[BOT_CMD_IMPULSE] = BOT_CMD_PARAMETER_FLOAT;
123 bot_cmd_string[BOT_CMD_WAIT_UNTIL] = "wait_until";
124 bot_cmd_parm_type[BOT_CMD_WAIT_UNTIL] = BOT_CMD_PARAMETER_FLOAT_RELATIVE_TO_TIME;
126 bot_cmd_string[BOT_CMD_RESETQUEUE] = "resetqueue";
127 bot_cmd_parm_type[BOT_CMD_RESETQUEUE] = BOT_CMD_PARAMETER_NONE;
129 bot_cmds_initialized = TRUE;
132 // Returns first bot with matching name
133 entity find_bot_by_name(string name)
137 bot = findchainflags(flags, FL_CLIENT);
140 if(clienttype(bot) == CLIENTTYPE_BOT)
141 if(bot.netname==name)
150 // Returns a bot by number on list
151 entity find_bot_by_number(float number)
156 bot = findchainflags(flags, FL_CLIENT);
159 if(clienttype(bot) == CLIENTTYPE_BOT)
170 void bot_clearqueue()
172 entity head, newhead;
174 head = findchainfloat(is_bot_cmd, TRUE);
179 newhead = head.chain;
187 self.bot_cmd_queue_index = 0;
188 self.bot_cmd_execution_index = 0;
191 entity bot_spawncmd(entity bot, float type)
195 bot.bot_cmd_queue_index++;
199 cmd.is_bot_cmd = TRUE;
200 cmd.bot_cmd_type = type;
201 cmd.bot_cmd_index = bot.bot_cmd_queue_index;
203 if(bot.bot_cmd_newest)
204 bot.bot_cmd_newest.bot_cmd_next = cmd;
205 bot.bot_cmd_newest = cmd;
210 void bot_debugcmd(entity cmd)
212 print(strcat("Owner: ",cmd.owner.netname, "\n"));
213 print(strcat("Cmd Type: ",ftos(cmd.bot_cmd_type), "\n"));
214 print(strcat("Cmd Index: ",ftos(cmd.bot_cmd_index), "\n"));
216 print(strcat("Param Float: ",ftos(cmd.bot_cmd_parm_float), "\n"));
217 print(strcat("Param String: ",cmd.bot_cmd_parm_string, "\n"));
218 print(strcat("Param Vector: ",vtos(cmd.bot_cmd_parm_vector), "\n"));
220 print(strcat("Bot queue index: ", ftos(cmd.owner.bot_cmd_queue_index), "\n"));
221 print(strcat("Bot execution index: ", ftos(cmd.owner.bot_cmd_execution_index), "\n\n"));
224 void bot_queuecommand(entity bot, string cmdstring, string parm)
227 local float cmd_parm_type, i;
232 if(!bot_cmds_initialized)
235 for(i=1;i<BOT_CMD_COUNTER;++i)
237 if(bot_cmd_string[i]!=cmdstring)
240 cmd_parm_type = bot_cmd_parm_type[i];
242 if(cmd_parm_type!=BOT_CMD_PARAMETER_NONE&&parm=="")
244 print("ERROR: A parameter is required for this command\n");
248 // Load command into queue
249 cmd = bot_spawncmd(bot, i);
252 switch(cmd_parm_type)
254 case BOT_CMD_PARAMETER_FLOAT:
255 cmd.bot_cmd_parm_float = stof(parm);
257 case BOT_CMD_PARAMETER_FLOAT_RELATIVE_TO_TIME:
258 cmd.bot_cmd_parm_float = stof(parm) + time;
260 case BOT_CMD_PARAMETER_STRING:
261 cmd.bot_cmd_parm_string = strzone(parm);
263 case BOT_CMD_PARAMETER_VECTOR:
264 cmd.bot_cmd_parm_vector = stov(parm);
271 print("ERROR: No such command '", cmdstring, "'\n");
274 void bot_cmdhelp(string scmd)
276 local float i, ntype;
279 if(!bot_cmds_initialized)
282 for(i=1;i<BOT_CMD_COUNTER;++i)
284 if(bot_cmd_string[i]!=scmd)
287 ntype = bot_cmd_parm_type[i];
291 case BOT_CMD_PARAMETER_FLOAT:
292 case BOT_CMD_PARAMETER_FLOAT_RELATIVE_TO_TIME:
293 stype = "float number";
295 case BOT_CMD_PARAMETER_STRING:
298 case BOT_CMD_PARAMETER_VECTOR:
306 print(strcat("Command: ",bot_cmd_string[i],"\nParameter: <",stype,"> \n"));
308 print("Description: ");
312 print("Stops the bot completely. Any command other than 'continue' will be ignored.");
314 case BOT_CMD_CONTINUE:
315 print("Disable paused status");
318 print("Pause command parsing and bot ai for N seconds. Pressed key will remain pressed");
320 case BOT_CMD_WAIT_UNTIL:
321 print("Pause command parsing and bot ai until time is N from NOW (when the command is added). Pressed key will remain pressed");
324 print("Look to the right or left N degrees. For turning to the left use positive numbers.");
327 print("Walk to an specific coordinate on the map. Usage: moveto \"x y z\"");
329 case BOT_CMD_MOVETOTARGET:
330 print("Walk to the specific target on the map");
332 case BOT_CMD_RESETGOAL:
333 print("Resets the goal stack");
336 print("Execute client command. Examples: cc \"say something\"; cc god; cc \"name newnickname\"; cc kill;");
339 print("Perform simple conditional execution.\n");
341 print(" sv_cmd .. if \"condition\"\n");
342 print(" sv_cmd .. <instruction if true>\n");
343 print(" sv_cmd .. <instruction if true>\n");
344 print(" sv_cmd .. else\n");
345 print(" sv_cmd .. <instruction if false>\n");
346 print(" sv_cmd .. <instruction if false>\n");
347 print(" sv_cmd .. fi\n");
348 print("Conditions: a=b, a>b, a<b, a\t\t(spaces not allowed)\n");
349 print(" Values in conditions can be numbers, cvars in the form cvar.cvar_string or special fields\n");
350 print("Fields: health, speed, flagcarrier\n");
351 print("Examples: if health>50; if health>cvar.g_balance_laser_primary_damage; if flagcarrier;");
353 case BOT_CMD_RESETAIM:
354 print("Points the aim to the coordinates x,y 0,0");
357 print("Move the aim x/y (horizontal/vertical) degrees relatives to the bot\n");
358 print("There is a 3rd optional parameter telling in how many seconds the aim has to reach the new position\n");
359 print("Examples: aim \"90 0\" // Turn 90 degrees inmediately (positive numbers move to the left/up)\n");
360 print(" aim \"0 90 2\" // Will gradually look to the sky in the next two seconds");
362 case BOT_CMD_AIMTARGET:
363 print("Points the aim to given target");
365 case BOT_CMD_PRESSKEY:
366 print("Press one of the following keys: forward, backward, left, right, jump, crouch, attack1, attack2, use\n");
367 print("Multiple keys can be pressed at time (with many presskey calls) and it will remain pressed until the command \"releasekey\" is called");
368 print("Note: The script will not return the control to the bot ai until all keys are released");
370 case BOT_CMD_RELEASEKEY:
371 print("Release previoulsy used keys. Use the parameter \"all\" to release all keys");
374 print("This command has no description yet.");
381 void bot_list_commands()
386 if(!bot_cmds_initialized)
389 print("List of all available commands:\n");
390 print(" Command\t\t\t\tParameter Type\n");
392 for(i=1;i<BOT_CMD_COUNTER;++i)
394 switch(bot_cmd_parm_type[i])
396 case BOT_CMD_PARAMETER_FLOAT:
397 case BOT_CMD_PARAMETER_FLOAT_RELATIVE_TO_TIME:
398 ptype = "float number";
400 case BOT_CMD_PARAMETER_STRING:
403 case BOT_CMD_PARAMETER_VECTOR:
410 print(strcat(" ",bot_cmd_string[i],"\t\t\t\t<",ptype,"> \n"));
415 .float bot_exec_status;
417 #define BOT_EXEC_STATUS_IDLE 0
418 #define BOT_EXEC_STATUS_PAUSED 1
419 #define BOT_EXEC_STATUS_WAITING 2
421 #define CMD_STATUS_EXECUTING 0
422 #define CMD_STATUS_FINISHED 1
423 #define CMD_STATUS_ERROR 2
425 void SV_ParseClientCommand(string s);
428 SV_ParseClientCommand(bot_cmd.bot_cmd_parm_string);
429 return CMD_STATUS_FINISHED;
432 float bot_cmd_impulse()
434 self.impulse = bot_cmd.bot_cmd_parm_float;
435 return CMD_STATUS_FINISHED;
438 float bot_cmd_continue()
440 self.bot_exec_status &~= BOT_EXEC_STATUS_PAUSED;
441 return CMD_STATUS_FINISHED;
444 .float bot_cmd_wait_time;
447 if(self.bot_exec_status & BOT_EXEC_STATUS_WAITING)
449 if(time>=self.bot_cmd_wait_time)
451 self.bot_exec_status &~= BOT_EXEC_STATUS_WAITING;
452 return CMD_STATUS_FINISHED;
455 return CMD_STATUS_EXECUTING;
458 self.bot_cmd_wait_time = time + bot_cmd.bot_cmd_parm_float;
459 self.bot_exec_status |= BOT_EXEC_STATUS_WAITING;
460 return CMD_STATUS_EXECUTING;
463 float bot_cmd_wait_until()
465 if(time < bot_cmd.bot_cmd_parm_float)
467 self.bot_exec_status |= BOT_EXEC_STATUS_WAITING;
468 return CMD_STATUS_EXECUTING;
470 self.bot_exec_status &~= BOT_EXEC_STATUS_WAITING;
471 return CMD_STATUS_FINISHED;
476 self.v_angle_y = self.v_angle_y + bot_cmd.bot_cmd_parm_float;
477 self.v_angle_y = self.v_angle_y - floor(self.v_angle_y / 360) * 360;
478 return CMD_STATUS_FINISHED;
481 float bot_cmd_select_weapon()
485 id = bot_cmd.bot_cmd_parm_float;
487 if(id < WEP_FIRST || id > WEP_LAST)
488 return CMD_STATUS_ERROR;
490 if(client_hasweapon(self, id, TRUE, FALSE))
491 self.switchweapon = id;
493 return CMD_STATUS_ERROR;
495 return CMD_STATUS_FINISHED;
498 .float bot_cmd_condition_status;
500 #define CMD_CONDITION_NONE 0
501 #define CMD_CONDITION_TRUE 1
502 #define CMD_CONDITION_FALSE 2
503 #define CMD_CONDITION_TRUE_BLOCK 4
504 #define CMD_CONDITION_FALSE_BLOCK 8
506 float bot_cmd_eval(string expr)
508 // Search for numbers
509 if(strstrofs("0123456789", substring(expr, 0, 1), 0) >= 0)
515 if(substring(expr, 0, 5)=="cvar.")
517 return cvar(substring(expr, 5, strlen(expr)));
526 return vlen(self.velocity);
528 return ((self.flagcarried!=world));
531 print(strcat("ERROR: Unable to convert the expression '",expr,"' into a numeric value\n"));
537 local string expr, val_a, val_b;
540 if(self.bot_cmd_condition_status != CMD_CONDITION_NONE)
542 // Only one "if" block is allowed at time
543 print("ERROR: Only one conditional block can be processed at time");
545 return CMD_STATUS_ERROR;
548 self.bot_cmd_condition_status |= CMD_CONDITION_TRUE_BLOCK;
550 // search for operators
551 expr = bot_cmd.bot_cmd_parm_string;
553 cmpofs = strstrofs(expr,"=",0);
557 val_a = substring(expr,0,cmpofs);
558 val_b = substring(expr,cmpofs+1,strlen(expr));
560 if(bot_cmd_eval(val_a)==bot_cmd_eval(val_b))
561 self.bot_cmd_condition_status |= CMD_CONDITION_TRUE;
563 self.bot_cmd_condition_status |= CMD_CONDITION_FALSE;
565 return CMD_STATUS_FINISHED;
568 cmpofs = strstrofs(expr,">",0);
572 val_a = substring(expr,0,cmpofs);
573 val_b = substring(expr,cmpofs+1,strlen(expr));
575 if(bot_cmd_eval(val_a)>bot_cmd_eval(val_b))
576 self.bot_cmd_condition_status |= CMD_CONDITION_TRUE;
578 self.bot_cmd_condition_status |= CMD_CONDITION_FALSE;
580 return CMD_STATUS_FINISHED;
583 cmpofs = strstrofs(expr,"<",0);
587 val_a = substring(expr,0,cmpofs);
588 val_b = substring(expr,cmpofs+1,strlen(expr));
590 if(bot_cmd_eval(val_a)<bot_cmd_eval(val_b))
591 self.bot_cmd_condition_status |= CMD_CONDITION_TRUE;
593 self.bot_cmd_condition_status |= CMD_CONDITION_FALSE;
595 return CMD_STATUS_FINISHED;
598 if(bot_cmd_eval(expr))
599 self.bot_cmd_condition_status |= CMD_CONDITION_TRUE;
601 self.bot_cmd_condition_status |= CMD_CONDITION_FALSE;
603 return CMD_STATUS_FINISHED;
608 self.bot_cmd_condition_status &~= CMD_CONDITION_TRUE_BLOCK;
609 self.bot_cmd_condition_status |= CMD_CONDITION_FALSE_BLOCK;
610 return CMD_STATUS_FINISHED;
615 self.bot_cmd_condition_status = CMD_CONDITION_NONE;
616 return CMD_STATUS_FINISHED;
619 float bot_cmd_resetaim()
621 self.v_angle = '0 0 0';
622 return CMD_STATUS_FINISHED;
625 .float bot_cmd_aim_begintime;
626 .float bot_cmd_aim_endtime;
627 .vector bot_cmd_aim_begin;
628 .vector bot_cmd_aim_end;
633 if(self.bot_cmd_aim_endtime)
635 local float progress;
637 progress = min(1 - (self.bot_cmd_aim_endtime - time) / (self.bot_cmd_aim_endtime - self.bot_cmd_aim_begintime),1);
638 self.v_angle = self.bot_cmd_aim_begin + ((self.bot_cmd_aim_end - self.bot_cmd_aim_begin) * progress);
640 if(time>=self.bot_cmd_aim_endtime)
642 self.bot_cmd_aim_endtime = 0;
643 return CMD_STATUS_FINISHED;
646 return CMD_STATUS_EXECUTING;
649 // New aiming direction
651 local float tokens, step;
653 parms = bot_cmd.bot_cmd_parm_string;
655 tokens = tokenizebyseparator(parms, " ");
659 self.v_angle_x -= stof(argv(1));
660 self.v_angle_y += stof(argv(0));
661 return CMD_STATUS_FINISHED;
664 if(tokens<2||tokens>3)
665 return CMD_STATUS_ERROR;
667 step = stof(argv(2));
669 self.bot_cmd_aim_begin = self.v_angle;
671 self.bot_cmd_aim_end_x = self.v_angle_x - stof(argv(1));
672 self.bot_cmd_aim_end_y = self.v_angle_y + stof(argv(0));
673 self.bot_cmd_aim_end_z = 0;
675 self.bot_cmd_aim_begintime = time;
676 self.bot_cmd_aim_endtime = time + step;
678 return CMD_STATUS_EXECUTING;
681 float bot_cmd_aimtarget()
683 if(self.bot_cmd_aim_endtime)
685 return bot_cmd_aim();
691 local float tokens, step;
693 parms = bot_cmd.bot_cmd_parm_string;
695 tokens = tokenizebyseparator(parms, " ");
697 e = find(world, targetname, argv(0));
699 return CMD_STATUS_ERROR;
701 v = e.origin + (e.mins + e.maxs) * 0.5;
705 self.v_angle = vectoangles(v - (self.origin + self.view_ofs));
706 self.v_angle_x = -self.v_angle_x;
707 return CMD_STATUS_FINISHED;
710 if(tokens<1||tokens>2)
711 return CMD_STATUS_ERROR;
713 step = stof(argv(1));
715 self.bot_cmd_aim_begin = self.v_angle;
716 self.bot_cmd_aim_end = vectoangles(v - (self.origin + self.view_ofs));
717 self.bot_cmd_aim_end_x = -self.bot_cmd_aim_end_x;
719 self.bot_cmd_aim_begintime = time;
720 self.bot_cmd_aim_endtime = time + step;
722 return CMD_STATUS_EXECUTING;
727 #define BOT_CMD_KEY_NONE 0
728 #define BOT_CMD_KEY_FORWARD 1
729 #define BOT_CMD_KEY_BACKWARD 2
730 #define BOT_CMD_KEY_RIGHT 4
731 #define BOT_CMD_KEY_LEFT 8
732 #define BOT_CMD_KEY_JUMP 16
733 #define BOT_CMD_KEY_ATTACK1 32
734 #define BOT_CMD_KEY_ATTACK2 64
735 #define BOT_CMD_KEY_USE 128
736 #define BOT_CMD_KEY_HOOK 256
737 #define BOT_CMD_KEY_CROUCH 512
738 #define BOT_CMD_KEY_CHAT 1024
740 float bot_presskeys()
742 self.movement = '0 0 0';
744 if(self.bot_cmd_keys == BOT_CMD_KEY_NONE)
747 if(self.bot_cmd_keys & BOT_CMD_KEY_FORWARD)
748 self.movement_x = cvar("sv_maxspeed");
749 else if(self.bot_cmd_keys & BOT_CMD_KEY_BACKWARD)
750 self.movement_x = -cvar("sv_maxspeed");
752 if(self.bot_cmd_keys & BOT_CMD_KEY_RIGHT)
753 self.movement_y = cvar("sv_maxspeed");
754 else if(self.bot_cmd_keys & BOT_CMD_KEY_LEFT)
755 self.movement_y = -cvar("sv_maxspeed");
757 if(self.bot_cmd_keys & BOT_CMD_KEY_JUMP)
758 self.BUTTON_JUMP = TRUE;
760 if(self.bot_cmd_keys & BOT_CMD_KEY_CROUCH)
761 self.BUTTON_CROUCH = TRUE;
763 if(self.bot_cmd_keys & BOT_CMD_KEY_ATTACK1)
764 self.BUTTON_ATCK = TRUE;
766 if(self.bot_cmd_keys & BOT_CMD_KEY_ATTACK2)
767 self.BUTTON_ATCK2 = TRUE;
769 if(self.bot_cmd_keys & BOT_CMD_KEY_USE)
770 self.BUTTON_USE = TRUE;
772 if(self.bot_cmd_keys & BOT_CMD_KEY_HOOK)
773 self.BUTTON_HOOK = TRUE;
775 if(self.bot_cmd_keys & BOT_CMD_KEY_CHAT)
776 self.BUTTON_CHAT = TRUE;
782 float bot_cmd_keypress_handler(string key, float enabled)
788 self.bot_cmd_keys = power2of(20) - 1; // >:)
790 self.bot_cmd_keys = BOT_CMD_KEY_NONE;
794 self.bot_cmd_keys |= BOT_CMD_KEY_FORWARD;
795 self.bot_cmd_keys &~= BOT_CMD_KEY_BACKWARD;
798 self.bot_cmd_keys &~= BOT_CMD_KEY_FORWARD;
803 self.bot_cmd_keys |= BOT_CMD_KEY_BACKWARD;
804 self.bot_cmd_keys &~= BOT_CMD_KEY_FORWARD;
807 self.bot_cmd_keys &~= BOT_CMD_KEY_BACKWARD;
812 self.bot_cmd_keys |= BOT_CMD_KEY_LEFT;
813 self.bot_cmd_keys &~= BOT_CMD_KEY_RIGHT;
816 self.bot_cmd_keys &~= BOT_CMD_KEY_LEFT;
821 self.bot_cmd_keys |= BOT_CMD_KEY_RIGHT;
822 self.bot_cmd_keys &~= BOT_CMD_KEY_LEFT;
825 self.bot_cmd_keys &~= BOT_CMD_KEY_RIGHT;
829 self.bot_cmd_keys |= BOT_CMD_KEY_JUMP;
831 self.bot_cmd_keys &~= BOT_CMD_KEY_JUMP;
835 self.bot_cmd_keys |= BOT_CMD_KEY_CROUCH;
837 self.bot_cmd_keys &~= BOT_CMD_KEY_CROUCH;
841 self.bot_cmd_keys |= BOT_CMD_KEY_ATTACK1;
843 self.bot_cmd_keys &~= BOT_CMD_KEY_ATTACK1;
847 self.bot_cmd_keys |= BOT_CMD_KEY_ATTACK2;
849 self.bot_cmd_keys &~= BOT_CMD_KEY_ATTACK2;
853 self.bot_cmd_keys |= BOT_CMD_KEY_USE;
855 self.bot_cmd_keys &~= BOT_CMD_KEY_USE;
859 self.bot_cmd_keys |= BOT_CMD_KEY_HOOK;
861 self.bot_cmd_keys &~= BOT_CMD_KEY_HOOK;
865 self.bot_cmd_keys |= BOT_CMD_KEY_CHAT;
867 self.bot_cmd_keys &~= BOT_CMD_KEY_CHAT;
873 return CMD_STATUS_FINISHED;
876 float bot_cmd_presskey()
880 key = bot_cmd.bot_cmd_parm_string;
882 bot_cmd_keypress_handler(key,TRUE);
884 return CMD_STATUS_FINISHED;
887 float bot_cmd_releasekey()
891 key = bot_cmd.bot_cmd_parm_string;
893 return bot_cmd_keypress_handler(key,FALSE);
896 float bot_cmd_pause()
901 self.BUTTON_ATCK = 0;
902 self.BUTTON_JUMP = 0;
903 self.BUTTON_HOOK = 0;
904 self.BUTTON_CHAT = 0;
905 self.BUTTON_ATCK2 = 0;
906 self.BUTTON_CROUCH = 0;
908 self.movement = '0 0 0';
909 self.bot_cmd_keys = BOT_CMD_KEY_NONE;
911 self.bot_exec_status = self.bot_exec_status | BOT_EXEC_STATUS_PAUSED;
912 return CMD_STATUS_FINISHED;
915 float bot_cmd_moveto()
917 return self.cmd_moveto(bot_cmd.bot_cmd_parm_vector);
920 float bot_cmd_movetotarget()
923 e = find(world, targetname, bot_cmd.bot_cmd_parm_string);
925 return CMD_STATUS_ERROR;
926 return self.cmd_moveto(e.origin + (e.mins + e.maxs) * 0.5);
929 float bot_cmd_resetgoal()
931 return self.cmd_resetgoal();
934 void bot_command_executed(float rm)
940 self.bot_cmd_execution_index++;
944 if(bot_cmd_parm_type[cmd.bot_cmd_type]==BOT_CMD_PARAMETER_STRING)
946 strunzone(cmd.bot_cmd_parm_string);
948 self.bot_cmd_next = cmd.bot_cmd_next;
949 if(cmd == self.bot_cmd_newest)
950 self.bot_cmd_newest = world;
955 cmd.bot_cmd_execution_counter++;
958 void bot_setcurrentcommand()
964 if(!self.bot_cmd_newest) // no command in the queue?
967 if(self.bot_cmd_execution_index==0)
968 self.bot_cmd_execution_index=1;
970 cmd = self.bot_cmd_next;
972 if(cmd.owner == self)
973 if(cmd.bot_cmd_index == self.bot_cmd_execution_index)
982 print("next bot cmd not set\n");
985 if(cmd && cmd.owner != self)
987 print("next bot cmd has wrong owner ", etos(cmd.owner), " for ", etos(self), "\n");
990 if(cmd && cmd.owner == self && cmd.bot_cmd_index != self.bot_cmd_execution_index)
992 print("next bot cmd has wrong index ", ftos(cmd.bot_cmd_execution_index), " for ", ftos(self.bot_cmd_execution_index), "\n");
996 for (cmd = findchainfloat(bot_cmd_index, self.bot_cmd_execution_index); cmd; cmd = cmd.chain)
1001 self.bot_cmd_next = cmd;
1002 //print(etos(self), " probably a jump...\n");
1008 // This function should be (the only) called directly from the bot ai loop
1009 // It maps commands to functions and deal with complex interactions between commands and execution states
1010 // NOTE: Of course you need to include your commands here too :)
1011 float bot_execute_commands()
1013 local float status, ispressingkey;
1018 if(self.deadflag!=DEAD_NO)
1022 bot_setcurrentcommand();
1024 // Keep pressing keys raised by the "presskey" command
1025 ispressingkey = bot_presskeys();
1028 return ispressingkey;
1030 // Ignore all commands except continue when the bot is paused
1031 if(self.bot_exec_status & BOT_EXEC_STATUS_PAUSED)
1032 if(bot_cmd.bot_cmd_type!=BOT_CMD_CONTINUE)
1034 if(bot_cmd.bot_cmd_type!=BOT_CMD_NULL)
1036 bot_command_executed(TRUE);
1037 print( "WARNING: Commands are ignored while the bot is paused. Use the command 'continue' instead.\n");
1042 // Handle conditions
1043 if not(bot_cmd.bot_cmd_type==BOT_CMD_FI||bot_cmd.bot_cmd_type==BOT_CMD_ELSE)
1044 if(self.bot_cmd_condition_status & CMD_CONDITION_TRUE && self.bot_cmd_condition_status & CMD_CONDITION_FALSE_BLOCK)
1046 bot_command_executed(TRUE);
1049 else if(self.bot_cmd_condition_status & CMD_CONDITION_FALSE && self.bot_cmd_condition_status & CMD_CONDITION_TRUE_BLOCK)
1051 bot_command_executed(TRUE);
1055 // Map commands to functions
1056 switch(bot_cmd.bot_cmd_type)
1059 return ispressingkey;
1062 status = bot_cmd_pause();
1064 case BOT_CMD_CONTINUE:
1065 status = bot_cmd_continue();
1068 status = bot_cmd_wait();
1070 case BOT_CMD_WAIT_UNTIL:
1071 status = bot_cmd_wait_until();
1074 status = bot_cmd_turn();
1076 case BOT_CMD_MOVETO:
1077 status = bot_cmd_moveto();
1079 case BOT_CMD_MOVETOTARGET:
1080 status = bot_cmd_movetotarget();
1082 case BOT_CMD_RESETGOAL:
1083 status = bot_cmd_resetgoal();
1086 status = bot_cmd_cc();
1089 status = bot_cmd_if();
1092 status = bot_cmd_else();
1095 status = bot_cmd_fi();
1097 case BOT_CMD_RESETAIM:
1098 status = bot_cmd_resetaim();
1101 status = bot_cmd_aim();
1103 case BOT_CMD_AIMTARGET:
1104 status = bot_cmd_aimtarget();
1106 case BOT_CMD_PRESSKEY:
1107 status = bot_cmd_presskey();
1109 case BOT_CMD_RELEASEKEY:
1110 status = bot_cmd_releasekey();
1112 case BOT_CMD_SELECTWEAPON:
1113 status = bot_cmd_select_weapon();
1115 case BOT_CMD_IMPULSE:
1116 status = bot_cmd_impulse();
1118 case BOT_CMD_RESETQUEUE:
1120 status = CMD_STATUS_FINISHED;
1123 print(strcat("ERROR: Invalid command on queue with id '",ftos(bot_cmd.bot_cmd_type),"'\n"));
1127 if (status==CMD_STATUS_ERROR)
1128 print(strcat("ERROR: The command '",bot_cmd_string[bot_cmd.bot_cmd_type],"' returned an error status\n"));
1130 // Move execution pointer
1131 if(status==CMD_STATUS_EXECUTING)
1137 if(cvar("g_debug_bot_commands"))
1141 switch(bot_cmd_parm_type[bot_cmd.bot_cmd_type])
1143 case BOT_CMD_PARAMETER_FLOAT:
1144 case BOT_CMD_PARAMETER_FLOAT_RELATIVE_TO_TIME:
1145 parms = ftos(bot_cmd.bot_cmd_parm_float);
1147 case BOT_CMD_PARAMETER_STRING:
1148 parms = bot_cmd.bot_cmd_parm_string;
1150 case BOT_CMD_PARAMETER_VECTOR:
1151 parms = vtos(bot_cmd.bot_cmd_parm_vector);
1157 clientcommand(self,strcat("say ^7", bot_cmd_string[bot_cmd.bot_cmd_type]," ",parms,"\n"));
1160 bot_command_executed(TRUE);
1163 } while(status==CMD_STATUS_FINISHED);