Xonotic QuakeC
The free, fast arena FPS with crisp movement and a wide array of weapons
scripting.qc
Go to the documentation of this file.
1#include "scripting.qh"
2
5#include <common/state.qh>
6#include <common/stats.qh>
8#include <common/wepent.qh>
13
14.int state;
15
20
22{
23 if(!bot.bot_cmdqueuebuf_allocated)
24 return;
25 buf_del(bot.bot_cmdqueuebuf);
26 bot.bot_cmdqueuebuf_allocated = false;
27 LOG_TRACE("bot ", bot.netname, " queue cleared");
28}
29
30void bot_queuecommand(entity bot, string cmdstring)
31{
32 if(!bot.bot_cmdqueuebuf_allocated)
33 {
34 bot.bot_cmdqueuebuf = buf_create();
35 bot.bot_cmdqueuebuf_allocated = true;
36 bot.bot_cmdqueuebuf_start = 0;
37 bot.bot_cmdqueuebuf_end = 0;
38 }
39
40 bufstr_set(bot.bot_cmdqueuebuf, bot.bot_cmdqueuebuf_end, cmdstring);
41
42 // if the command was a "sound" command, precache the sound NOW
43 // this prevents lagging!
44 {
45 float sp;
46 string parm;
47 string cmdstr;
48
49 sp = strstrofs(cmdstring, " ", 0);
50 if(sp >= 0)
51 {
52 parm = substring(cmdstring, sp + 1, -1);
53 cmdstr = substring(cmdstring, 0, sp);
54 if(cmdstr == "sound")
55 {
56 // find the LAST word
57 for (;;)
58 {
59 sp = strstrofs(parm, " ", 0);
60 if(sp < 0)
61 break;
62 parm = substring(parm, sp + 1, -1);
63 }
64 precache_sound(parm);
65 }
66 }
67 }
68
69 ++bot.bot_cmdqueuebuf_end;
70}
71
72void bot_dequeuecommand(entity bot, float idx)
73{
74 if(!bot.bot_cmdqueuebuf_allocated)
75 error("dequeuecommand but no queue allocated");
76 if(idx < bot.bot_cmdqueuebuf_start)
77 error("dequeueing a command in the past");
78 if(idx >= bot.bot_cmdqueuebuf_end)
79 error("dequeueing a command in the future");
80 bufstr_set(bot.bot_cmdqueuebuf, idx, "");
81 if(idx == bot.bot_cmdqueuebuf_start)
82 ++bot.bot_cmdqueuebuf_start;
83 if(bot.bot_cmdqueuebuf_start >= bot.bot_cmdqueuebuf_end)
84 bot_clearqueue(bot);
85}
86
87string bot_readcommand(entity bot, float idx)
88{
89 if(!bot.bot_cmdqueuebuf_allocated)
90 error("readcommand but no queue allocated");
91 if(idx < bot.bot_cmdqueuebuf_start)
92 error("reading a command in the past");
93 if(idx >= bot.bot_cmdqueuebuf_end)
94 error("reading a command in the future");
95 return bufstr_get(bot.bot_cmdqueuebuf, idx);
96}
97
98bool bot_havecommand(entity this, int idx)
99{
101 return false;
102 if(idx < this.bot_cmdqueuebuf_start)
103 return false;
104 if(idx >= this.bot_cmdqueuebuf_end)
105 return false;
106 return true;
107}
108
109const int MAX_BOT_PLACES = 4;
113entity bot_getplace(entity this, string placename)
114{
115 entity e;
116 if(substring(placename, 0, 1) == "@")
117 {
118 int i, p;
119 placename = substring(placename, 1, -1);
120 string s, s2;
121 for(i = 0; i < this.bot_places_count; ++i)
122 if(this.(bot_placenames[i]) == placename)
123 return this.(bot_places[i]);
124 // now: i == this.bot_places_count
125 s = s2 = cvar_string(placename);
126 p = strstrofs(s2, " ", 0);
127 if(p >= 0)
128 {
129 s = substring(s2, 0, p);
130 //print("places: ", placename, " -> ", cvar_string(placename), "\n");
131 cvar_set(placename, strcat(substring(s2, p+1, -1), " ", s));
132 //print("places: ", placename, " := ", cvar_string(placename), "\n");
133 }
134 e = find(NULL, targetname, s);
135 if(!e)
136 LOG_INFO("invalid place ", s);
137 if(i < MAX_BOT_PLACES)
138 {
139 this.(bot_placenames[i]) = strzone(placename);
140 this.(bot_places[i]) = e;
141 ++this.bot_places_count;
142 }
143 return e;
144 }
145 else
146 {
147 e = find(NULL, targetname, placename);
148 if(!e)
149 LOG_INFO("invalid place ", placename);
150 return e;
151 }
152}
153
154
155// Initialize global commands list
156// NOTE: New commands should be initialized here
158{
161
162 bot_cmd_string[BOT_CMD_PAUSE] = "pause";
164
165 bot_cmd_string[BOT_CMD_CONTINUE] = "continue";
167
170
173
174 bot_cmd_string[BOT_CMD_MOVETO] = "moveto";
176
177 bot_cmd_string[BOT_CMD_MOVETOTARGET] = "movetotarget";
179
180 bot_cmd_string[BOT_CMD_RESETGOAL] = "resetgoal";
182
185
188
191
194
195 bot_cmd_string[BOT_CMD_RESETAIM] = "resetaim";
197
200
201 bot_cmd_string[BOT_CMD_AIMTARGET] = "aimtarget";
203
204 bot_cmd_string[BOT_CMD_PRESSKEY] = "presskey";
206
207 bot_cmd_string[BOT_CMD_RELEASEKEY] = "releasekey";
209
210 bot_cmd_string[BOT_CMD_SELECTWEAPON] = "selectweapon";
212
213 bot_cmd_string[BOT_CMD_IMPULSE] = "impulse";
215
216 bot_cmd_string[BOT_CMD_WAIT_UNTIL] = "wait_until";
218
219 bot_cmd_string[BOT_CMD_BARRIER] = "barrier";
221
222 bot_cmd_string[BOT_CMD_CONSOLE] = "console";
224
225 bot_cmd_string[BOT_CMD_SOUND] = "sound";
227
228 bot_cmd_string[BOT_CMD_DEBUG_ASSERT_CANFIRE] = "debug_assert_canfire";
230
232}
233
234// Returns first bot with matching name
236{
237 FOREACH_CLIENT(IS_BOT_CLIENT(it) && it.netname == name,
238 {
239 return it;
240 });
241
242 return NULL;
243}
244
245// Returns a bot by number on list
247{
248 entity bot;
249 float c = 0;
250
251 if(!number)
252 return NULL;
253
254 bot = findchainflags(flags, FL_CLIENT); // TODO: doesn't findchainflags loop backwards through entities?
255 while (bot)
256 {
257 if(IS_BOT_CLIENT(bot))
258 {
259 if(++c==number)
260 return bot;
261 }
262 bot = bot.chain;
263 }
264
265 return NULL;
266}
267
268float bot_decodecommand(string cmdstring)
269{
270 float cmd_parm_type;
271 float sp;
272 string parm;
273
274 sp = strstrofs(cmdstring, " ", 0);
275 if(sp < 0)
276 {
277 parm = "";
278 }
279 else
280 {
281 parm = substring(cmdstring, sp + 1, -1);
282 cmdstring = substring(cmdstring, 0, sp);
283 }
284
287
288 int i;
289 for(i=1;i<BOT_CMD_COUNTER;++i)
290 {
291 if(bot_cmd_string[i]!=cmdstring)
292 continue;
293
294 cmd_parm_type = bot_cmd_parm_type[i];
295
296 if(cmd_parm_type!=BOT_CMD_PARAMETER_NONE&&parm=="")
297 {
298 LOG_INFO("ERROR: A parameter is required for this command");
299 return 0;
300 }
301
302 // Load command into queue
303 bot_cmd.bot_cmd_type = i;
304
305 // Attach parameter
306 switch(cmd_parm_type)
307 {
309 bot_cmd.bot_cmd_parm_float = stof(parm);
310 break;
312 strcpy(bot_cmd.bot_cmd_parm_string, parm);
313 break;
315 if(substring(parm, 0, 1) != "\'")
316 {
317 LOG_INFOF("ERROR: expected vector type \'x y z\', got %s", parm);
318 return 0;
319 }
320 bot_cmd.bot_cmd_parm_vector = stov(parm);
321 break;
322 default:
323 break;
324 }
325 return 1;
326 }
327 LOG_INFO("ERROR: No such command '", cmdstring, "'");
328 return 0;
329}
330
331void bot_cmdhelp(string scmd)
332{
333 int i, ntype;
334 string stype;
335
338
339 for(i=1;i<BOT_CMD_COUNTER;++i)
340 {
341 if(bot_cmd_string[i]!=scmd)
342 continue;
343
344 ntype = bot_cmd_parm_type[i];
345
346 switch(ntype)
347 {
349 stype = "float";
350 break;
352 stype = "string";
353 break;
355 stype = "vector";
356 break;
357 default:
358 stype = "none";
359 break;
360 }
361
362 string desc = "";
363 switch(i)
364 {
365 case BOT_CMD_PAUSE:
366 desc = "Stops the bot completely. Any command other than 'continue' will be ignored.";
367 break;
368 case BOT_CMD_CONTINUE:
369 desc = "Disable paused status";
370 break;
371 case BOT_CMD_WAIT:
372 desc = "Pause command parsing and bot ai for N seconds. Pressed key will remain pressed";
373 break;
375 desc = "Pause command parsing and bot ai until time is N from the last barrier. Pressed key will remain pressed";
376 break;
377 case BOT_CMD_BARRIER:
378 desc = "Waits till all bots that have a command queue reach this command. Pressed key will remain pressed";
379 break;
380 case BOT_CMD_TURN:
381 desc = "Look to the right or left N degrees. For turning to the left use positive numbers.";
382 break;
383 case BOT_CMD_MOVETO:
384 desc = "Walk to an specific coordinate on the map. Usage: moveto \'x y z\'";
385 break;
387 desc = "Walk to the specific target on the map";
388 break;
390 desc = "Resets the goal stack";
391 break;
392 case BOT_CMD_CC:
393 desc = "Execute client command. Examples: cc say something; cc god; cc name newnickname; cc kill;";
394 break;
395 case BOT_CMD_IF:
396 desc = "Perform simple conditional execution.\n"
397 "Syntax: \n"
398 " sv_cmd .. if \"condition\"\n"
399 " sv_cmd .. <instruction if true>\n"
400 " sv_cmd .. <instruction if true>\n"
401 " sv_cmd .. else\n"
402 " sv_cmd .. <instruction if false>\n"
403 " sv_cmd .. <instruction if false>\n"
404 " sv_cmd .. fi\n"
405 "Conditions: a=b, a>b, a<b, a\t\t(spaces not allowed)\n"
406 " Values in conditions can be numbers, cvars in the form cvar.cvar_string or special fields\n"
407 "Fields: health, speed, flagcarrier\n"
408 "Examples: if health>50; if health>cvar.g_balance_laser_primary_damage; if flagcarrier;";
409 break;
410 case BOT_CMD_RESETAIM:
411 desc = "Points the aim to the coordinates x,y 0,0";
412 break;
413 case BOT_CMD_AIM:
414 desc = "Move the aim x/y (horizontal/vertical) degrees relatives to the bot\n"
415 "There is a 3rd optional parameter telling in how many seconds the aim has to reach the new position\n"
416 "Examples: aim \"90 0\" // Turn 90 degrees inmediately (positive numbers move to the left/up)\n"
417 " aim \"0 90 2\" // Will gradually look to the sky in the next two seconds";
418 break;
420 desc = "Points the aim to given target";
421 break;
422 case BOT_CMD_PRESSKEY:
423 desc = "Press one of the following keys: forward, backward, left, right, jump, crouch, attack1, attack2, use"
424 "Multiple keys can be pressed at time (with many presskey calls) and it will remain pressed until the command \"releasekey\" is called"
425 "Note: The script will not return the control to the bot ai until all keys are released";
426 break;
428 desc = "Release previoulsy used keys. Use the parameter \"all\" to release all keys";
429 break;
430 case BOT_CMD_SOUND:
431 desc = "play sound file at bot location";
432 break;
434 desc = "verify the state of the weapon entity";
435 break;
436 default:
437 desc = "This command has no description yet.";
438 break;
439 }
440 LOG_HELP("Command: ", bot_cmd_string[i], "\nParameter: <", stype, ">", "\nDescription: ", desc);
441 }
442}
443
445{
446 int i;
447 string ptype;
448
451
452 LOG_HELP("Bot commands:");
453
454 for(i=1;i<BOT_CMD_COUNTER;++i)
455 {
456 switch(bot_cmd_parm_type[i])
457 {
459 ptype = "float";
460 break;
462 ptype = "string";
463 break;
465 ptype = "vector";
466 break;
467 default:
468 ptype = "none";
469 break;
470 }
471 if (ptype != "none")
472 LOG_HELP(" ^2", bot_cmd_string[i]," ^7<", ptype, ">");
473 else
474 LOG_HELP(" ^2", bot_cmd_string[i]);
475 }
476 LOG_HELP("For help about a specific command, type bot_cmd help <command>");
477}
478
479// Commands code
481
483{
484 SV_ParseClientCommand(this, bot_cmd.bot_cmd_parm_string);
485 return CMD_STATUS_FINISHED;
486}
487
489{
490 CS(this).impulse = bot_cmd.bot_cmd_parm_float;
491 return CMD_STATUS_FINISHED;
492}
493
500
503{
505 {
506 if(time>=this.bot_cmd_wait_time)
507 {
509 return CMD_STATUS_FINISHED;
510 }
511 else
513 }
514
515 this.bot_cmd_wait_time = time + bot_cmd.bot_cmd_parm_float;
518}
519
521{
522 if(time < bot_cmd.bot_cmd_parm_float + bot_barriertime)
523 {
526 }
528 return CMD_STATUS_FINISHED;
529}
530
532{
533 // 0 = no barrier, 1 = waiting, 2 = waiting finished
534
535 if(this.bot_barrier == 0) // initialization
536 {
537 this.bot_barrier = 1;
538
539 //this.colormod = '4 4 0';
540 }
541
542 if(this.bot_barrier == 1) // find other bots
543 {
544 FOREACH_CLIENT(it.isbot, {
545 if(it.bot_cmdqueuebuf_allocated)
546 if(it.bot_barrier != 1)
547 return CMD_STATUS_EXECUTING; // not all are at the barrier yet
548 });
549
550 // all bots hit the barrier!
551
552 // acknowledge barrier
553 FOREACH_CLIENT(it.isbot, { it.bot_barrier = 2; });
554
556 }
557
558 // if we get here, the barrier is finished
559 // so end it...
560 this.bot_barrier = 0;
561 //this.colormod = '0 0 0';
562
563 return CMD_STATUS_FINISHED;
564}
565
567{
568 this.v_angle_y = this.v_angle.y + bot_cmd.bot_cmd_parm_float;
569 this.v_angle_y = this.v_angle.y - floor(this.v_angle.y / 360) * 360;
570 return CMD_STATUS_FINISHED;
571}
572
574{
575 float id = bot_cmd.bot_cmd_parm_float;
576
577 if(id < WEP_FIRST || id > WEP_LAST)
578 return CMD_STATUS_ERROR;
579
580 bool success = false;
581
582 for(int slot = 0; slot < MAX_WEAPONSLOTS; ++slot)
583 {
584 .entity weaponentity = weaponentities[slot];
585 if(this.(weaponentity).m_weapon == WEP_Null && slot != 0)
586 continue;
587
588 if(client_hasweapon(this, REGISTRY_GET(Weapons, id), weaponentity, true, false))
589 {
590 success = true;
591 this.(weaponentity).m_switchweapon = REGISTRY_GET(Weapons, id);
592 }
593 }
594
595 if(!success)
596 return CMD_STATUS_ERROR;
597
598 return CMD_STATUS_FINISHED;
599}
600
602
603const int CMD_CONDITION_NONE = 0;
604const int CMD_CONDITION_true = 1;
608
609float bot_cmd_eval(entity this, string expr)
610{
611 // Search for numbers
612 if(IS_DIGIT(substring(expr, 0, 1)))
613 return stof(expr);
614
615 // Search for cvars
616 if(substring(expr, 0, 5)=="cvar.")
617 return cvar(substring(expr, 5, strlen(expr)));
618
619 // Search for fields
620 // TODO: expand with support for more fields (key carrier, ball carrier, armor etc)
621 switch(expr)
622 {
623 case "health":
624 return GetResource(this, RES_HEALTH);
625 case "speed":
626 return vlen(this.velocity);
627 case "flagcarrier":
628 return ((this.flagcarried!=NULL));
629 }
630
631 LOG_INFO("ERROR: Unable to convert the expression '", expr, "' into a numeric value");
632 return 0;
633}
634
636{
637 string expr, val_a, val_b;
638 float cmpofs;
639
641 {
642 // Only one "if" block is allowed at time
643 LOG_INFO("ERROR: Only one conditional block can be processed at time");
644 bot_clearqueue(this);
645 return CMD_STATUS_ERROR;
646 }
647
649
650 // search for operators
651 expr = bot_cmd.bot_cmd_parm_string;
652
653 cmpofs = strstrofs(expr,"=",0);
654
655 if(cmpofs>0)
656 {
657 val_a = substring(expr,0,cmpofs);
658 val_b = substring(expr,cmpofs+1,strlen(expr));
659
660 if(bot_cmd_eval(this, val_a)==bot_cmd_eval(this, val_b))
662 else
664
665 return CMD_STATUS_FINISHED;
666 }
667
668 cmpofs = strstrofs(expr,">",0);
669
670 if(cmpofs>0)
671 {
672 val_a = substring(expr,0,cmpofs);
673 val_b = substring(expr,cmpofs+1,strlen(expr));
674
675 if(bot_cmd_eval(this, val_a)>bot_cmd_eval(this, val_b))
677 else
679
680 return CMD_STATUS_FINISHED;
681 }
682
683 cmpofs = strstrofs(expr,"<",0);
684
685 if(cmpofs>0)
686 {
687 val_a = substring(expr,0,cmpofs);
688 val_b = substring(expr,cmpofs+1,strlen(expr));
689
690 if(bot_cmd_eval(this, val_a)<bot_cmd_eval(this, val_b))
692 else
694
695 return CMD_STATUS_FINISHED;
696 }
697
698 if(bot_cmd_eval(this, expr))
700 else
702
703 return CMD_STATUS_FINISHED;
704}
705
712
718
720{
721 this.v_angle = '0 0 0';
722 return CMD_STATUS_FINISHED;
723}
724
729
731{
732 // Current direction
733 if(this.bot_cmd_aim_endtime)
734 {
735 float progress = min(1 - (this.bot_cmd_aim_endtime - time) / (this.bot_cmd_aim_endtime - this.bot_cmd_aim_begintime),1);
736 this.v_angle = this.bot_cmd_aim_begin + ((this.bot_cmd_aim_end - this.bot_cmd_aim_begin) * progress);
737
738 if(time>=this.bot_cmd_aim_endtime)
739 {
740 this.bot_cmd_aim_endtime = 0;
741 return CMD_STATUS_FINISHED;
742 }
743 else
745 }
746
747 // New aiming direction
748 string parms;
749 float tokens, step;
750
751 parms = bot_cmd.bot_cmd_parm_string;
752
753 tokens = tokenizebyseparator(parms, " ");
754
755 if(tokens<2||tokens>3)
756 return CMD_STATUS_ERROR;
757
758 step = (tokens == 3) ? stof(argv(2)) : 0;
759
760 if(step == 0)
761 {
762 this.v_angle_x -= stof(argv(1));
763 this.v_angle_y += stof(argv(0));
764 return CMD_STATUS_FINISHED;
765 }
766
767 this.bot_cmd_aim_begin = this.v_angle;
768
769 this.bot_cmd_aim_end_x = this.v_angle.x - stof(argv(1));
770 this.bot_cmd_aim_end_y = this.v_angle.y + stof(argv(0));
771 this.bot_cmd_aim_end_z = 0;
772
774 this.bot_cmd_aim_endtime = time + step;
775
777}
778
780{
781 if(this.bot_cmd_aim_endtime)
782 {
783 return bot_cmd_aim(this);
784 }
785
786 entity e;
787 string parms;
788 vector v;
789 float tokens, step;
790
791 parms = bot_cmd.bot_cmd_parm_string;
792
793 tokens = tokenizebyseparator(parms, " ");
794
795 e = bot_getplace(this, argv(0));
796 if(!e)
797 return CMD_STATUS_ERROR;
798
799 v = e.origin + (e.mins + e.maxs) * 0.5;
800
801 if(tokens==1)
802 {
803 this.v_angle = vectoangles(v - (this.origin + this.view_ofs));
804 this.v_angle_x = -this.v_angle.x;
805 return CMD_STATUS_FINISHED;
806 }
807
808 if(tokens<1||tokens>2)
809 return CMD_STATUS_ERROR;
810
811 step = stof(argv(1));
812
813 this.bot_cmd_aim_begin = this.v_angle;
814 this.bot_cmd_aim_end = vectoangles(v - (this.origin + this.view_ofs));
815 this.bot_cmd_aim_end_x = -this.bot_cmd_aim_end.x;
816
818 this.bot_cmd_aim_endtime = time + step;
819
821}
822
824
825const int BOT_CMD_KEY_NONE = 0;
828const int BOT_CMD_KEY_RIGHT = BIT(2);
829const int BOT_CMD_KEY_LEFT = BIT(3);
830const int BOT_CMD_KEY_JUMP = BIT(4);
833const int BOT_CMD_KEY_USE = BIT(7);
834const int BOT_CMD_KEY_HOOK = BIT(8);
835const int BOT_CMD_KEY_CROUCH = BIT(9);
836const int BOT_CMD_KEY_CHAT = BIT(10);
837
839{
840 CS(this).movement = '0 0 0';
841 PHYS_INPUT_BUTTON_JUMP(this) = false;
842 PHYS_INPUT_BUTTON_CROUCH(this) = false;
843 PHYS_INPUT_BUTTON_ATCK(this) = false;
844 PHYS_INPUT_BUTTON_ATCK2(this) = false;
845 PHYS_INPUT_BUTTON_USE(this) = false;
846 PHYS_INPUT_BUTTON_HOOK(this) = false;
847 PHYS_INPUT_BUTTON_CHAT(this) = false;
848
850 return false;
851
853 CS(this).movement_x = autocvar_sv_maxspeed;
854 else if(this.bot_cmd_keys & BOT_CMD_KEY_BACKWARD)
855 CS(this).movement_x = -autocvar_sv_maxspeed;
856
858 CS(this).movement_y = autocvar_sv_maxspeed;
859 else if(this.bot_cmd_keys & BOT_CMD_KEY_LEFT)
860 CS(this).movement_y = -autocvar_sv_maxspeed;
861
863 PHYS_INPUT_BUTTON_JUMP(this) = true;
864
866 PHYS_INPUT_BUTTON_CROUCH(this) = true;
867
869 PHYS_INPUT_BUTTON_ATCK(this) = true;
870
872 PHYS_INPUT_BUTTON_ATCK2(this) = true;
873
875 PHYS_INPUT_BUTTON_USE(this) = true;
876
878 PHYS_INPUT_BUTTON_HOOK(this) = true;
879
881 PHYS_INPUT_BUTTON_CHAT(this) = true;
882
883 return true;
884}
885
886
887float bot_cmd_keypress_handler(entity this, string key, float enabled)
888{
889 switch(key)
890 {
891 case "all":
892 if(enabled)
893 this.bot_cmd_keys = (2 ** 20) - 1; // >:)
894 else
896 case "forward":
897 if(enabled)
898 {
901 }
902 else
904 break;
905 case "backward":
906 if(enabled)
907 {
910 }
911 else
913 break;
914 case "left":
915 if(enabled)
916 {
919 }
920 else
922 break;
923 case "right":
924 if(enabled)
925 {
928 }
929 else
931 break;
932 case "jump":
933 if(enabled)
935 else
937 break;
938 case "crouch":
939 if(enabled)
941 else
943 break;
944 case "attack1":
945 if(enabled)
947 else
949 break;
950 case "attack2":
951 if(enabled)
953 else
955 break;
956 case "use":
957 if(enabled)
959 else
961 break;
962 case "hook":
963 if(enabled)
965 else
967 break;
968 case "chat":
969 if(enabled)
971 else
973 break;
974 default:
975 break;
976 }
977
978 return CMD_STATUS_FINISHED;
979}
980
982{
983 string key = bot_cmd.bot_cmd_parm_string;
984
985 bot_cmd_keypress_handler(this, key,true);
986
987 return CMD_STATUS_FINISHED;
988}
989
991{
992 string key = bot_cmd.bot_cmd_parm_string;
993
994 return bot_cmd_keypress_handler(this, key,false);
995}
996
998{
1000}
1001
1003{
1004 PHYS_INPUT_BUTTON_DRAG(this) = false;
1005 PHYS_INPUT_BUTTON_USE(this) = false;
1006 PHYS_INPUT_BUTTON_ATCK(this) = false;
1007 PHYS_INPUT_BUTTON_JUMP(this) = false;
1008 PHYS_INPUT_BUTTON_HOOK(this) = false;
1009 PHYS_INPUT_BUTTON_CHAT(this) = false;
1010 PHYS_INPUT_BUTTON_ATCK2(this) = false;
1011 PHYS_INPUT_BUTTON_CROUCH(this) = false;
1012
1013 CS(this).movement = '0 0 0';
1015
1018 return CMD_STATUS_FINISHED;
1019}
1020
1022{
1023 return this.cmd_moveto(this, bot_cmd.bot_cmd_parm_vector);
1024}
1025
1027{
1028 entity e = bot_getplace(this, bot_cmd.bot_cmd_parm_string);
1029 if(!e)
1030 return CMD_STATUS_ERROR;
1031 return this.cmd_moveto(this, e.origin + (e.mins + e.maxs) * 0.5);
1032}
1033
1035{
1036 return this.cmd_resetgoal(this);
1037}
1038
1039
1041{
1042 string f = bot_cmd.bot_cmd_parm_string;
1043
1044 float n = tokenizebyseparator(f, " ");
1045
1046 string sample = f;
1047 float chan = CH_WEAPON_B;
1048 float vol = VOL_BASE;
1049 float atten = ATTEN_MIN;
1050
1051 if(n >= 1)
1052 sample = argv(n - 1);
1053 if(n >= 2)
1054 chan = stof(argv(0));
1055 if(n >= 3)
1056 vol = stof(argv(1));
1057 if(n >= 4)
1058 atten = stof(argv(2));
1059
1060 precache_sound(f);
1061 _sound(this, chan, sample, vol, atten);
1062
1063 return CMD_STATUS_FINISHED;
1064}
1065
1068{
1069 float f = bot_cmd.bot_cmd_parm_float;
1070
1071 int slot = 0; // TODO: unhardcode?
1072 .entity weaponentity = weaponentities[slot];
1073 if(this.(weaponentity).state != WS_READY)
1074 {
1075 if(f)
1076 {
1077 this.colormod = '0 8 8';
1078 LOG_INFO("Bot ", this.netname, " using ", this.(weaponentity).weaponname, " wants to fire, inhibited by weaponentity state");
1079 }
1080 }
1081 else if(ATTACK_FINISHED(this, weaponentity) > time)
1082 {
1083 if(f)
1084 {
1085 this.colormod = '8 0 8';
1086 LOG_INFO("Bot ", this.netname, " using ", this.(weaponentity).weaponname, " wants to fire, inhibited by ATTACK_FINISHED (", ftos(ATTACK_FINISHED(this, weaponentity) - time), " seconds left)");
1087 }
1088 }
1089 else if(this.(weaponentity).tuba_note)
1090 {
1091 if(f)
1092 {
1093 this.colormod = '8 0 0';
1094 LOG_INFO("Bot ", this.netname, " using ", this.(weaponentity).weaponname, " wants to fire, bot still has an active tuba note");
1095 }
1096 }
1097 else
1098 {
1099 if(!f)
1100 {
1101 this.colormod = '8 8 0';
1102 LOG_INFO("Bot ", this.netname, " using ", this.(weaponentity).weaponname, " thinks it has fired, but apparently did not; ATTACK_FINISHED says ", ftos(ATTACK_FINISHED(this, weaponentity) - time), " seconds left");
1103 }
1104 }
1105
1106 return CMD_STATUS_FINISHED;
1107}
1108
1109//
1110
1111void bot_command_executed(entity this, bool rm)
1112{
1113 //entity cmd = bot_cmd;
1114
1115 if(rm)
1117
1119}
1120
1122{
1123 bot_cmd = NULL;
1124
1125 if(!this.bot_cmd_current)
1126 {
1128 }
1129
1130 bot_cmd = this.bot_cmd_current;
1131 if(bot_cmd.bot_cmd_index != this.bot_cmd_execution_index || this.bot_cmd_execution_index == 0)
1132 {
1134 {
1135 string cmdstring = bot_readcommand(this, this.bot_cmd_execution_index);
1136 if(bot_decodecommand(cmdstring))
1137 {
1138 bot_cmd.owner = this;
1139 bot_cmd.bot_cmd_index = this.bot_cmd_execution_index;
1140 }
1141 else
1142 {
1143 // Invalid command, remove from queue
1144 bot_cmd = NULL;
1147 }
1148 }
1149 else
1150 bot_cmd = NULL;
1151 }
1152}
1153
1155{
1156 FOREACH_CLIENT(it.isbot, {
1157 it.bot_cmd_execution_index = 0;
1158 bot_clearqueue(it);
1159 // also, cancel all barriers
1160 it.bot_barrier = 0;
1161 for(int i = 0; i < it.bot_places_count; ++i)
1162 {
1163 strfree(it.(bot_placenames[i]));
1164 }
1165 it.bot_places_count = 0;
1166 });
1167
1169}
1170
1171// Here we map commands to functions and deal with complex interactions between commands and execution states
1172// NOTE: Of course you need to include your commands here too :)
1174{
1175 float status, ispressingkey;
1176
1177 // Find command
1179
1180 // Ignore all commands except continue when the bot is paused
1182 {
1183 // if we have no bot command, better return
1184 // old logic kept pressing previously pressed keys, but that has problems
1185 // (namely, it means you cannot make a bot "normal" ever again)
1186 // to keep a bot walking for a while, use the "wait" bot command
1187 if(bot_cmd == world)
1188 return 0;
1189 }
1190 else if(bot_cmd.bot_cmd_type != BOT_CMD_CONTINUE)
1191 {
1192 if(bot_cmd.bot_cmd_type!=BOT_CMD_NULL)
1193 {
1194 bot_command_executed(this, true);
1195 LOG_INFO("WARNING: Commands are ignored while the bot is paused. Use the command 'continue' instead.");
1196 }
1197 return 1;
1198 }
1199
1200 // Keep pressing keys raised by the "presskey" command
1201 ispressingkey = boolean(bot_presskeys(this));
1202
1203 // Handle conditions
1204 if (!(bot_cmd.bot_cmd_type == BOT_CMD_FI || bot_cmd.bot_cmd_type == BOT_CMD_ELSE))
1206 {
1207 bot_command_executed(this, true);
1208 return -1;
1209 }
1211 {
1212 bot_command_executed(this, true);
1213 return -1;
1214 }
1215
1216 // Map commands to functions
1217 switch(bot_cmd.bot_cmd_type)
1218 {
1219 case BOT_CMD_NULL:
1220 return ispressingkey;
1221 //break;
1222 case BOT_CMD_PAUSE:
1223 status = bot_cmd_pause(this);
1224 break;
1225 case BOT_CMD_CONTINUE:
1226 status = bot_cmd_continue(this);
1227 break;
1228 case BOT_CMD_WAIT:
1229 status = bot_cmd_wait(this);
1230 break;
1231 case BOT_CMD_WAIT_UNTIL:
1232 status = bot_cmd_wait_until(this);
1233 break;
1234 case BOT_CMD_TURN:
1235 status = bot_cmd_turn(this);
1236 break;
1237 case BOT_CMD_MOVETO:
1238 status = bot_cmd_moveto(this);
1239 break;
1241 status = bot_cmd_movetotarget(this);
1242 break;
1243 case BOT_CMD_RESETGOAL:
1244 status = bot_cmd_resetgoal(this);
1245 break;
1246 case BOT_CMD_CC:
1247 status = bot_cmd_cc(this);
1248 break;
1249 case BOT_CMD_IF:
1250 status = bot_cmd_if(this);
1251 break;
1252 case BOT_CMD_ELSE:
1253 status = bot_cmd_else(this);
1254 break;
1255 case BOT_CMD_FI:
1256 status = bot_cmd_fi(this);
1257 break;
1258 case BOT_CMD_RESETAIM:
1259 status = bot_cmd_resetaim(this);
1260 break;
1261 case BOT_CMD_AIM:
1262 status = bot_cmd_aim(this);
1263 break;
1264 case BOT_CMD_AIMTARGET:
1265 status = bot_cmd_aimtarget(this);
1266 break;
1267 case BOT_CMD_PRESSKEY:
1268 status = bot_cmd_presskey(this);
1269 break;
1270 case BOT_CMD_RELEASEKEY:
1271 status = bot_cmd_releasekey(this);
1272 break;
1274 status = bot_cmd_select_weapon(this);
1275 break;
1276 case BOT_CMD_IMPULSE:
1277 status = bot_cmd_impulse(this);
1278 break;
1279 case BOT_CMD_BARRIER:
1280 status = bot_cmd_barrier(this);
1281 break;
1282 case BOT_CMD_CONSOLE:
1283 localcmd(strcat(bot_cmd.bot_cmd_parm_string, "\n"));
1284 status = CMD_STATUS_FINISHED;
1285 break;
1286 case BOT_CMD_SOUND:
1287 status = bot_cmd_sound(this);
1288 break;
1290 status = bot_cmd_debug_assert_canfire(this);
1291 break;
1292 default:
1293 LOG_INFOF("ERROR: Invalid command on queue with id '%s'", ftos(bot_cmd.bot_cmd_type));
1294 return 0;
1295 }
1296
1297 if (status==CMD_STATUS_ERROR)
1298 LOG_INFOF("ERROR: The command '%s' returned an error status", bot_cmd_string[bot_cmd.bot_cmd_type]);
1299
1300 // Move execution pointer
1301 if(status==CMD_STATUS_EXECUTING)
1302 {
1303 return 1;
1304 }
1305 else
1306 {
1308 {
1309 string parms;
1310
1311 switch(bot_cmd_parm_type[bot_cmd.bot_cmd_type])
1312 {
1314 parms = ftos(bot_cmd.bot_cmd_parm_float);
1315 break;
1317 parms = bot_cmd.bot_cmd_parm_string;
1318 break;
1320 parms = vtos(bot_cmd.bot_cmd_parm_vector);
1321 break;
1322 default:
1323 parms = "";
1324 break;
1325 }
1326 clientcommand(this,strcat("say ^7", bot_cmd_string[bot_cmd.bot_cmd_type]," ",parms,"\n"));
1327 }
1328
1329 bot_command_executed(this, true);
1330 }
1331
1332 if(status == CMD_STATUS_FINISHED)
1333 return -1;
1334
1335 return CMD_STATUS_ERROR;
1336}
1337
1338// This function should be (the only) called directly from the bot ai loop
1340{
1341 int f;
1342 do
1343 {
1344 f = bot_execute_commands_once(this);
1345 }
1346 while(f < 0);
1347 return f;
1348}
void bot_relinkplayerlist()
Definition bot.qc:424
#define BIT(n)
Only ever assign into the first 24 bits in QC (so max is BIT(23)).
Definition bits.qh:8
#define boolean(value)
Definition bool.qh:9
var entity(vector mins, vector maxs,.entity tofield) findbox_tofield_OrFallback
float GetResource(entity e, Resource res_type)
Returns the current amount of resource the given entity has.
string netname
Definition powerups.qc:20
vector colormod
Definition powerups.qc:21
#define PHYS_INPUT_BUTTON_CROUCH(s)
Definition player.qh:156
#define PHYS_INPUT_BUTTON_JUMP(s)
Definition player.qh:153
#define PHYS_INPUT_BUTTON_HOOK(s)
Definition player.qh:157
#define PHYS_INPUT_BUTTON_CHAT(s)
Definition player.qh:161
vector v_angle
Definition player.qh:236
float autocvar_sv_maxspeed
Definition player.qh:53
#define PHYS_INPUT_BUTTON_USE(s)
Definition player.qh:160
#define PHYS_INPUT_BUTTON_ATCK(s)
Definition player.qh:152
#define PHYS_INPUT_BUTTON_ATCK2(s)
Definition player.qh:154
#define PHYS_INPUT_BUTTON_DRAG(s)
Definition player.qh:159
const int FL_CLIENT
Definition constants.qh:72
float flags
vector velocity
float time
vector origin
bool autocvar_g_debug_bot_commands
Definition cvars.qh:59
int state
#define strstrofs
#define strlen
#define tokenizebyseparator
#define buf_create
Weapons
Definition guide.qh:113
#define SV_ParseClientCommand
Definition _all.inc:284
#define LOG_HELP(...)
Definition log.qh:85
#define LOG_INFO(...)
Definition log.qh:65
#define LOG_TRACE(...)
Definition log.qh:76
#define LOG_INFOF(...)
Definition log.qh:66
string name
Definition menu.qh:30
void localcmd(string command,...)
void cvar_set(string name, string value)
float stof(string val,...)
string substring(string s, float start, float length)
float cvar(string name)
entity find(entity start,.string field, string match)
vector stov(string s)
void clientcommand(float client, string s)
const string cvar_string(string name)
float vlen(vector v)
vector vectoangles(vector v)
string precache_sound(string sample)
string vtos(vector v)
float min(float f,...)
string ftos(float f)
entity findchainflags(.float field, float match)
float floor(float f)
string strzone(string s)
string argv(float n)
spree_inf s1 s2 s3loc s2 spree_inf s1 s2 s3loc s2 spree_inf s1 s2 s3loc s2 s1 s2loc s1 s2loc s1 s2loc s1 s2loc s1 s2loc s1 s2loc s1 s2 f1points s1 s2
Definition all.inc:471
strcat(_("^F4Countdown stopped!"), "\n^BG", _("Teams are too unbalanced."))
#define new_pure(class)
purely logical entities (not linked to the area grid)
Definition oo.qh:67
#define NULL
Definition post.qh:14
#define world
Definition post.qh:15
#define error
Definition pre.qh:6
vector view_ofs
Definition progsdefs.qc:151
#define REGISTRY_GET(id, i)
Definition registry.qh:43
float bot_cmd_eval(entity this, string expr)
Definition scripting.qc:609
const int BOT_CMD_KEY_ATTACK2
Definition scripting.qc:832
void bot_resetqueues()
float bot_cmd_keypress_handler(entity this, string key, float enabled)
Definition scripting.qc:887
float bot_cmd_sound(entity this)
entity find_bot_by_number(float number)
Definition scripting.qc:246
void bot_setcurrentcommand(entity this)
float bot_cmd_fi(entity this)
Definition scripting.qc:713
float bot_cmd_resetaim(entity this)
Definition scripting.qc:719
float bot_cmd_barrier(entity this)
Definition scripting.qc:531
const int BOT_CMD_KEY_RIGHT
Definition scripting.qc:828
float bot_cmd_turn(entity this)
Definition scripting.qc:566
entity find_bot_by_name(string name)
Definition scripting.qc:235
float bot_cmdqueuebuf_start
Definition scripting.qc:18
float bot_cmd_wait(entity this)
Definition scripting.qc:502
const int BOT_CMD_KEY_ATTACK1
Definition scripting.qc:831
const int BOT_CMD_KEY_BACKWARD
Definition scripting.qc:827
void bot_queuecommand(entity bot, string cmdstring)
Definition scripting.qc:30
bool bot_havecommand(entity this, int idx)
Definition scripting.qc:98
float bot_cmd_aim(entity this)
Definition scripting.qc:730
float bot_cmd_movetotarget(entity this)
float bot_places_count
Definition scripting.qc:110
float bot_cmd_aimtarget(entity this)
Definition scripting.qc:779
const int CMD_CONDITION_true_BLOCK
Definition scripting.qc:606
void bot_cmdhelp(string scmd)
Definition scripting.qc:331
const int CMD_CONDITION_true
Definition scripting.qc:604
const int MAX_BOT_PLACES
Definition scripting.qc:109
void bot_clearqueue(entity bot)
Definition scripting.qc:21
const int CMD_CONDITION_false_BLOCK
Definition scripting.qc:607
const int CMD_CONDITION_false
Definition scripting.qc:605
void bot_commands_init()
Definition scripting.qc:157
float bot_cmd_presskey(entity this)
Definition scripting.qc:981
entity bot_getplace(entity this, string placename)
Definition scripting.qc:113
float bot_cmd_resetgoal(entity this)
float bot_cmdqueuebuf
Definition scripting.qc:17
entity bot_places[MAX_BOT_PLACES]
Definition scripting.qc:111
float bot_cmd_wait_until(entity this)
Definition scripting.qc:520
const int BOT_CMD_KEY_CROUCH
Definition scripting.qc:835
float bot_cmd_continue(entity this)
Definition scripting.qc:494
vector bot_cmd_aim_end
Definition scripting.qc:728
float bot_cmdqueuebuf_end
Definition scripting.qc:19
int bot_cmd_keys
Definition scripting.qc:823
const int BOT_CMD_KEY_FORWARD
Definition scripting.qc:826
float bot_cmd_moveto(entity this)
float bot_cmd_select_weapon(entity this)
Definition scripting.qc:573
float bot_cmd_debug_assert_canfire(entity this)
string bot_placenames[MAX_BOT_PLACES]
Definition scripting.qc:112
float bot_cmd_cc(entity this)
Definition scripting.qc:482
string bot_readcommand(entity bot, float idx)
Definition scripting.qc:87
float bot_cmd_if(entity this)
Definition scripting.qc:635
float bot_execute_commands_once(entity this)
const int BOT_CMD_KEY_HOOK
Definition scripting.qc:834
const int CMD_CONDITION_NONE
Definition scripting.qc:603
void bot_list_commands()
Definition scripting.qc:444
const int BOT_CMD_KEY_CHAT
Definition scripting.qc:836
bool bot_presskeys(entity this)
Definition scripting.qc:838
float bot_cmd_releasekey(entity this)
Definition scripting.qc:990
bool bot_ispaused(entity this)
Definition scripting.qc:997
const int BOT_CMD_KEY_JUMP
Definition scripting.qc:830
int bot_exec_status
Definition scripting.qc:480
const int BOT_CMD_KEY_NONE
Definition scripting.qc:825
float bot_cmd_else(entity this)
Definition scripting.qc:706
int bot_execute_commands(entity this)
float bot_cmd_pause(entity this)
vector bot_cmd_aim_begin
Definition scripting.qc:727
int bot_cmd_condition_status
Definition scripting.qc:601
const int BOT_CMD_KEY_LEFT
Definition scripting.qc:829
const int BOT_CMD_KEY_USE
Definition scripting.qc:833
float bot_decodecommand(string cmdstring)
Definition scripting.qc:268
void bot_command_executed(entity this, bool rm)
float bot_cmd_aim_endtime
Definition scripting.qc:726
float bot_cmd_impulse(entity this)
Definition scripting.qc:488
float bot_cmd_aim_begintime
Definition scripting.qc:725
float bot_cmd_wait_time
Definition scripting.qc:501
void bot_dequeuecommand(entity bot, float idx)
Definition scripting.qc:72
float bot_cmdqueuebuf_allocated
Definition scripting.qc:16
const int BOT_CMD_AIMTARGET
Definition scripting.qh:32
const int BOT_CMD_AIM
Definition scripting.qh:25
entity bot_cmd_current
Definition scripting.qh:60
const int BOT_CMD_PARAMETER_FLOAT
Definition scripting.qh:50
const int BOT_CMD_PARAMETER_NONE
Definition scripting.qh:49
const int BOT_CMD_CC
Definition scripting.qh:20
const int BOT_CMD_PRESSKEY
Definition scripting.qh:26
#define BOT_EXEC_STATUS_PAUSED
Definition scripting.qh:4
const int BOT_CMD_WAIT_UNTIL
Definition scripting.qh:30
#define BOT_EXEC_STATUS_WAITING
Definition scripting.qh:5
const int BOT_CMD_CONSOLE
Definition scripting.qh:34
const int BOT_CMD_ELSE
Definition scripting.qh:22
#define CMD_STATUS_ERROR
Definition scripting.qh:9
const int BOT_CMD_IF
Definition scripting.qh:21
const int BOT_CMD_DEBUG_ASSERT_CANFIRE
Definition scripting.qh:36
#define CMD_STATUS_FINISHED
Definition scripting.qh:8
string bot_cmd_string[BOT_CMD_COUNTER]
Definition scripting.qh:56
const int BOT_CMD_FI
Definition scripting.qh:23
const int BOT_CMD_SOUND
Definition scripting.qh:35
const int BOT_CMD_NULL
Definition scripting.qh:13
#define CMD_STATUS_EXECUTING
Definition scripting.qh:7
const int BOT_CMD_RELEASEKEY
Definition scripting.qh:27
float bot_barrier
Definition scripting.qh:69
const int BOT_CMD_MOVETOTARGET
Definition scripting.qh:31
const int BOT_CMD_PAUSE
Definition scripting.qh:14
const int BOT_CMD_CONTINUE
Definition scripting.qh:15
const int BOT_CMD_MOVETO
Definition scripting.qh:18
const int BOT_CMD_SELECTWEAPON
Definition scripting.qh:28
float bot_barriertime
Definition scripting.qh:68
const int BOT_CMD_PARAMETER_VECTOR
Definition scripting.qh:52
const int BOT_CMD_COUNTER
Definition scripting.qh:41
const int BOT_CMD_PARAMETER_STRING
Definition scripting.qh:51
const int BOT_CMD_IMPULSE
Definition scripting.qh:29
const int BOT_CMD_BARRIER
Definition scripting.qh:33
const int BOT_CMD_WAIT
Definition scripting.qh:16
entity bot_cmd
Definition scripting.qh:59
const int BOT_CMD_TURN
Definition scripting.qh:17
int bot_cmd_parm_type[BOT_CMD_COUNTER]
Definition scripting.qh:55
const int BOT_CMD_RESETGOAL
Definition scripting.qh:19
const int BOT_CMD_RESETAIM
Definition scripting.qh:24
float bot_cmds_initialized
Definition scripting.qh:54
float bot_cmd_execution_index
Definition scripting.qh:71
bool client_hasweapon(entity this, Weapon wpn,.entity weaponentity, float andammo, bool complain)
Definition selection.qc:47
vector
Definition self.qh:92
int int number
Definition impulse.qc:89
const float VOL_BASE
Definition sound.qh:36
const float ATTEN_MIN
Definition sound.qh:28
#define _sound(e, c, s, v, a)
Definition sound.qh:43
const int CH_WEAPON_B
Definition sound.qh:8
ClientState CS(Client this)
Definition state.qh:47
#define IS_DIGIT(d)
Definition string.qh:575
#define strcpy(this, s)
Definition string.qh:52
entity flagcarried
Definition sv_ctf.qh:93
float atten
Definition triggers.qh:47
string targetname
Definition triggers.qh:56
entity tuba_note
Definition tuba.qc:5
#define FOREACH_CLIENT(cond, body)
Definition utils.qh:52
#define IS_BOT_CLIENT(v)
want: (IS_CLIENT(v) && !IS_REAL_CLIENT(v))
Definition utils.qh:15
#define WEP_LAST
Definition all.qh:343
string weaponname
Definition all.qh:413
const int MAX_WEAPONSLOTS
Definition weapon.qh:16
const int WS_READY
idle frame
Definition weapon.qh:38
entity weaponentities[MAX_WEAPONSLOTS]
Definition weapon.qh:17
#define ATTACK_FINISHED(ent, w)
Weapon m_weapon
Definition wepent.qh:26
Weapon m_switchweapon
Definition wepent.qh:25