Xonotic
bot.qc
Go to the documentation of this file.
1 #include "bot.qh"
2 
3 #include <common/constants.qh>
4 #include <common/mapinfo.qh>
5 #include <common/net_linked.qh>
7 #include <common/stats.qh>
8 #include <common/teams.qh>
9 #include <common/util.qh>
10 #include <common/weapons/_all.qh>
12 #include <lib/warpzone/common.qh>
14 #include <server/antilag.qh>
22 #include <server/campaign.qh>
23 #include <server/client.qh>
24 #include <server/damage.qh>
25 #include <server/items/items.qh>
26 #include <server/mutators/_mod.qh>
27 #include <server/race.qh>
28 #include <server/scores_rules.qh>
29 #include <server/teamplay.qh>
31 #include <server/world.qh>
32 
34 
35 // TODO: remove this function! its only purpose is to update these fields since bot_setnameandstuff is called before ClientState
37 {
38  CS_CVAR(this).cvar_cl_accuracy_data_share = 1; // share the bots weapon accuracy data with the world
39  CS_CVAR(this).cvar_cl_accuracy_data_receive = 0; // don't receive any weapon accuracy data
40 }
41 
43 {
44  entity bot = spawnclient();
45  if (bot)
46  {
50  ClientConnect(bot);
52  PutClientInServer(bot);
53  }
54  return bot;
55 }
56 
57 void bot_think(entity this)
58 {
59  if (this.bot_nextthink > time)
60  return;
61 
62  this.flags &= ~FL_GODMODE;
64  this.flags |= FL_GODMODE;
65 
66  this.bot_nextthink = max(time, this.bot_nextthink) + max(0.01, autocvar_bot_ai_thinkinterval * (0.5 ** this.bot_aiskill) * min(14 / (skill + 14), 1));
67 
69  {
70  CS(this).movement = '0 0 0';
71  this.bot_nextthink = time + 0.5;
72  return;
73  }
74 
75  if (this.fixangle)
76  {
77  this.v_angle = this.angles;
78  this.v_angle_z = 0;
79  this.fixangle = false;
80  }
81 
82  this.dmg_take = 0;
83  this.dmg_save = 0;
84  this.dmg_inflictor = NULL;
85 
86  // calculate an aiming latency based on the skill setting
87  // (simulated network latency + naturally delayed reflexes)
88  //this.ping = 0.7 - bound(0, 0.05 * skill, 0.5); // moved the reflexes to bot_aimdir (under the name 'think')
89  // minimum ping 20+10 random
90  CS(this).ping = bound(0,0.07 - bound(0, (skill + this.bot_pingskill) * 0.005,0.05)+random()*0.01,0.65); // Now holds real lag to server, and higer skill players take a less laggy server
91  // skill 10 = ping 0.2 (adrenaline)
92  // skill 0 = ping 0.7 (slightly drunk)
93 
94  // clear buttons
95  PHYS_INPUT_BUTTON_ATCK(this) = false;
96  // keep jump button pressed for a short while, useful with ramp jumps
97  PHYS_INPUT_BUTTON_JUMP(this) = (!IS_DEAD(this) && time < this.bot_jump_time + 0.2);
98  PHYS_INPUT_BUTTON_ATCK2(this) = false;
99  PHYS_INPUT_BUTTON_ZOOM(this) = false;
100  PHYS_INPUT_BUTTON_CROUCH(this) = false;
101  PHYS_INPUT_BUTTON_HOOK(this) = false;
102  PHYS_INPUT_BUTTON_INFO(this) = false;
103  PHYS_INPUT_BUTTON_DRAG(this) = false;
104  PHYS_INPUT_BUTTON_CHAT(this) = false;
105  PHYS_INPUT_BUTTON_USE(this) = false;
106 
107  if (time < game_starttime)
108  {
109  // block the bot during the countdown to game start
110  CS(this).movement = '0 0 0';
111  this.bot_nextthink = game_starttime;
112  return;
113  }
114 
115  // if dead, just wait until we can respawn
116  if (IS_DEAD(this) || IS_OBSERVER(this))
117  {
118  if (bot_waypoint_queue_owner == this)
120  this.aistatus = 0;
121  CS(this).movement = '0 0 0';
122  if (IS_OBSERVER(this))
123  return;
124  if (IS_DEAD(this))
125  {
128  // jump must not be pressed for at least one frame in order for
129  // PlayerThink to detect the key down event
130  if (this.deadflag == DEAD_DYING)
131  PHYS_INPUT_BUTTON_JUMP(this) = false;
132  else if (this.deadflag == DEAD_DEAD)
133  PHYS_INPUT_BUTTON_JUMP(this) = true; // press jump to respawn
134  }
135  }
136  else if(this.aistatus & AI_STATUS_STUCK)
137  navigation_unstuck(this);
138 
139  // now call the current bot AI (havocbot for example)
140  this.bot_ai(this);
141 }
142 
144 {
145  string readfile, s;
146  int file, tokens, prio;
147 
149 
150  if(file < 0)
151  {
152  LOG_INFOF("Error: Can not open the bot configuration file '%s'", autocvar_bot_config_file);
153  readfile = "";
154  }
155  else
156  {
159  int smallest_team = -1;
160  int smallest_count = -1;
161  if (teamplay)
162  {
163  for (int i = 1; i <= AvailableTeams(); ++i)
164  {
165  // NOTE if (autocvar_g_campaign && autocvar_g_campaign_forceteam == i)
166  // TeamBalance_GetNumberOfPlayers(balance, i); returns the number of players + 1
167  // since it keeps a spot for the real player in the desired team
168  int count = TeamBalance_GetNumberOfPlayers(balance, i);
169  if (smallest_count < 0 || count < smallest_count)
170  {
171  smallest_team = i;
172  smallest_count = count;
173  }
174  }
175  }
176  TeamBalance_Destroy(balance);
178  while((readfile = fgets(file)))
179  {
180  if(substring(readfile, 0, 2) == "//")
181  continue;
182  if(substring(readfile, 0, 1) == "#")
183  continue;
184  // NOTE if the line is empty tokenizebyseparator(readfile, "\t")
185  // will create 1 empty token because there's no separator (bug?)
186  if (readfile == "")
187  continue;
188  tokens = tokenizebyseparator(readfile, "\t");
189  if(tokens == 0)
190  continue;
191  s = argv(0);
192  prio = 0;
193  bool conflict = false;
195  if (s == it.cleanname)
196  {
197  conflict = true;
198  break;
199  }
200  });
201  if (!conflict)
202  prio += 1;
203  if (teamplay && !(autocvar_bot_vs_human && AvailableTeams() == 2))
204  {
205  int forced_team = stof(argv(5));
206  if (!Team_IsValidIndex(forced_team))
207  forced_team = 0;
208  if (!forced_team || forced_team == smallest_team)
209  prio += 2;
210  }
211  RandomSelection_AddString(readfile, 1, prio);
212  }
214  fclose(file);
215  }
216 
217  string bot_name, bot_model, bot_skin, bot_shirt, bot_pants;
218 
219  tokens = tokenizebyseparator(readfile, "\t");
220  if(argv(0) != "") bot_name = argv(0);
221  else bot_name = "Bot";
222 
223  if(argv(1) != "") bot_model = argv(1);
224  else bot_model = "";
225 
226  if(argv(2) != "") bot_skin = argv(2);
227  else bot_skin = "0";
228 
229  if(argv(3) != "" && stof(argv(3)) >= 0) bot_shirt = argv(3);
230  else bot_shirt = ftos(floor(random() * 15));
231 
232  if(argv(4) != "" && stof(argv(4)) >= 0) bot_pants = argv(4);
233  else bot_pants = ftos(floor(random() * 15));
234 
235  if (teamplay && !(autocvar_bot_vs_human && AvailableTeams() == 2))
236  this.bot_forced_team = stof(argv(5));
237  else
238  this.bot_forced_team = 0;
239 
240  prio = 6;
241 
242  #define READSKILL(f, w, r) MACRO_BEGIN \
243  if(argv(prio) != "") \
244  this.f = stof(argv(prio)) * w; \
245  else \
246  this.f = (!autocvar_g_campaign) * (2 * random() - 1) * r * w; \
247  prio++; \
248  MACRO_END
249  //print(bot_name, ": ping=", argv(9), "\n");
250 
251  READSKILL(havocbot_keyboardskill, 0.5, 0.5); // keyboard skill
252  READSKILL(bot_moveskill, 2, 0); // move skill
253  READSKILL(bot_dodgeskill, 2, 0); // dodge skill
254 
255  READSKILL(bot_pingskill, 0.5, 0); // ping skill
256 
257  READSKILL(bot_weaponskill, 2, 0); // weapon skill
258  READSKILL(bot_aggresskill, 1, 0); // aggre skill
259  READSKILL(bot_rangepreference, 1, 0); // read skill
260 
261  READSKILL(bot_aimskill, 2, 0); // aim skill
262  READSKILL(bot_offsetskill, 2, 0.5); // offset skill
263  READSKILL(bot_mouseskill, 1, 0.5); // mouse skill
264 
265  READSKILL(bot_thinkskill, 1, 0.5); // think skill
266  READSKILL(bot_aiskill, 2, 0); // "ai" skill
267 
268  if (file >= 0 && argv(prio) != "")
269  LOG_INFOF("^1Warning^7: too many parameters for bot %s, please check format of %s", bot_name, autocvar_bot_config_file);
270 
271  this.bot_config_loaded = true;
272 
273  // this is really only a default, TeamBalance_JoinBestTeam is called later
274  setcolor(this, stof(bot_shirt) * 16 + stof(bot_pants));
275  this.bot_preferredcolors = this.clientcolors;
276 
277  string prefix = (autocvar_g_campaign ? "" : autocvar_bot_prefix);
278  string suffix = (autocvar_g_campaign ? "" : autocvar_bot_suffix);
279  string name = (autocvar_bot_usemodelnames ? bot_model : bot_name);
280 
281  if (name == "")
282  {
283  name = ftos(etof(this));
284  this.netname = this.netname_freeme = strzone(strcat(prefix, name, suffix));
285  }
286  else
287  {
288  // number bots with identical names
289  int j = 0;
291  if(it.cleanname == name)
292  ++j;
293  });
294  if (j)
295  this.netname = this.netname_freeme = strzone(strcat(prefix, name, "(", ftos(j), ")", suffix));
296  else
297  this.netname = this.netname_freeme = strzone(strcat(prefix, name, suffix));
298  }
299  this.cleanname = strzone(name);
300 
301  // pick the model and skin
302  if(substring(bot_model, -4, 1) != ".")
303  bot_model = strcat(bot_model, ".iqm");
304  this.playermodel = this.playermodel_freeme = strzone(strcat("models/player/", bot_model));
305  this.playerskin = this.playerskin_freeme = strzone(bot_skin);
306 }
307 
309 {
310  static string bot_priority_far_prev;
311  static string bot_priority_mid_prev;
312  static string bot_priority_close_prev;
313  static string bot_priority_distances_prev;
314  float tokens, i, w;
315 
316  bot_custom_weapon = false;
317 
322  )
323  return;
324 
325  if (bot_priority_distances_prev != autocvar_bot_ai_custom_weapon_priority_distances)
326  {
327  strcpy(bot_priority_distances_prev, autocvar_bot_ai_custom_weapon_priority_distances);
329 
330  if (tokens!=2)
331  return;
332 
333  bot_distance_far = stof(argv(0));
335 
337  bot_distance_far = stof(argv(1));
339  }
340  }
341 
342  int c;
343 
344  #define PARSE_WEAPON_PRIORITIES(dist) MACRO_BEGIN \
345  if (bot_priority_##dist##_prev != autocvar_bot_ai_custom_weapon_priority_##dist) { \
346  strcpy(bot_priority_##dist##_prev, autocvar_bot_ai_custom_weapon_priority_##dist); \
347  tokens = tokenizebyseparator(W_NumberWeaponOrder(autocvar_bot_ai_custom_weapon_priority_##dist)," "); \
348  bot_weapons_##dist[0] = -1; \
349  c = 0; \
350  for(i = 0; i < tokens && c < REGISTRY_COUNT(Weapons); ++i) { \
351  w = stof(argv(i)); \
352  if (w >= WEP_FIRST && w <= WEP_LAST) { \
353  bot_weapons_##dist[c] = w; \
354  ++c; \
355  } \
356  } \
357  if (c < REGISTRY_COUNT(Weapons)) \
358  bot_weapons_##dist[c] = -1; \
359  } \
360  MACRO_END
361 
365 
366  bot_custom_weapon = true;
367 }
368 
370 {
372  entity e = bot_list;
373  while (e)
374  {
375  setcolor(e, e.bot_preferredcolors);
376  e = e.nextbot;
377  }
378  // if dynamic waypoints are ever implemented, save them here
379 }
380 
382 {
383  player_count = 0;
384  currentbots = 0;
385  bot_list = NULL;
386 
387  entity prevbot = NULL;
388  FOREACH_CLIENT(true,
389  {
390  ++player_count;
391 
392  if(IS_BOT_CLIENT(it))
393  {
394  if (!IS_OBSERVER(it) && !bot_ispaused(it))
395  {
396  if(prevbot)
397  prevbot.nextbot = it;
398  else
399  bot_list = it;
400  prevbot = it;
401  }
402  ++currentbots;
403  }
404  });
405  if(prevbot)
406  prevbot.nextbot = NULL;
409 }
410 
412 {
413  if (!IS_BOT_CLIENT(this))
414  return;
415  bot_clearqueue(this);
416  strfree(this.cleanname);
417  strfree(this.netname_freeme);
420  if(this.bot_cmd_current)
421  delete(this.bot_cmd_current);
422  if(bot_waypoint_queue_owner == this)
424 }
425 
427 {
428  if (!IS_BOT_CLIENT(this)) return;
429  this.bot_preferredcolors = this.clientcolors;
430  this.bot_nextthink = time - random();
431  this.lag_func = bot_lagfunc;
432  this.isbot = true;
433  this.createdtime = this.bot_nextthink;
434 
435  if(!this.bot_config_loaded) // This is needed so team overrider doesn't break between matches
436  {
437  bot_setnameandstuff(this);
438  bot_setclientfields(this);
439  }
440 
442  {
444  }
445  else
446  {
447  this.bot_forced_team = 0;
449  }
450 
451  havocbot_setupbot(this);
452 }
453 
455 {
458 
459  entity best = NULL;
460  float besttime = 0;
461  int bestcount = 0;
462 
463  int bcount = 0;
464  FOREACH_CLIENT(it.isbot,
465  {
466  ++bcount;
467 
468  if(!best)
469  {
470  best = it;
471  besttime = it.createdtime;
472  }
473 
474  int thiscount = 0;
475 
476  if (Team_IsValidTeam(it.team))
477  {
478  thiscount = TeamBalance_GetNumberOfPlayers(balance,
479  Team_TeamToIndex(it.team));
480  }
481 
482  if(thiscount > bestcount)
483  {
484  bestcount = thiscount;
485  besttime = it.createdtime;
486  best = it;
487  }
488  else if(thiscount == bestcount && besttime < it.createdtime)
489  {
490  besttime = it.createdtime;
491  best = it;
492  }
493  });
494  TeamBalance_Destroy(balance);
495  if(!bcount)
496  return; // no bots to remove
497  currentbots = currentbots - 1;
498  dropclient(best);
499 }
500 
502 {
503  if(teamplay)
504  {
506  return;
507  }
508 
509  float besttime = 0;
510  entity best = NULL;
511  int bcount = 0;
512 
513  FOREACH_CLIENT(it.isbot,
514  {
515  ++bcount;
516 
517  if(!best)
518  {
519  best = it;
520  besttime = it.createdtime;
521  }
522 
523  if(besttime < it.createdtime)
524  {
525  besttime = it.createdtime;
526  best = it;
527  }
528  });
529 
530  if(!bcount)
531  return; // no bots to remove
532 
533  currentbots = currentbots - 1;
534  dropclient(best);
535 }
536 
537 void autoskill(float factor)
538 {
539  int bestbot = -1;
540  int bestplayer = -1;
542  if(IS_REAL_CLIENT(it))
543  bestplayer = max(bestplayer, it.totalfrags - it.totalfrags_lastcheck);
544  else
545  bestbot = max(bestbot, it.totalfrags - it.totalfrags_lastcheck);
546  });
547 
548  string msg = strcat("autoskill: best player got ", ftos(bestplayer), ", ""best bot got ", ftos(bestbot), "; ");
549  if(bestbot < 0 || bestplayer < 0)
550  {
551  msg = strcat(msg, "not doing anything");
552  // don't return, let it reset all counters below
553  }
554  else if(bestbot <= bestplayer * factor - 2)
555  {
556  if(autocvar_skill < 17)
557  {
558  msg = strcat(msg, "2 frags difference, increasing skill");
559  cvar_set("skill", ftos(autocvar_skill + 1));
560  bprint("^2BOT SKILL UP!^7 Now at level ", ftos(autocvar_skill), "\n");
561  }
562  }
563  else if(bestbot >= bestplayer * factor + 2)
564  {
565  if(autocvar_skill > 0)
566  {
567  msg = strcat(msg, "2 frags difference, decreasing skill");
568  cvar_set("skill", ftos(autocvar_skill - 1));
569  bprint("^1BOT SKILL DOWN!^7 Now at level ", ftos(autocvar_skill), "\n");
570  }
571  }
572  else
573  {
574  msg = strcat(msg, "not doing anything");
575  return;
576  // don't reset counters, wait for them to accumulate
577  }
578  LOG_DEBUG(msg);
579 
580  FOREACH_CLIENT(IS_PLAYER(it), { it.totalfrags_lastcheck = it.totalfrags; });
581 }
582 
584 {
585  stepheightvec = autocvar_sv_stepheight * '0 0 1';
586  jumpheight_vec = (autocvar_sv_jumpvelocity ** 2) / (2 * autocvar_sv_gravity) * '0 0 1';
587  jumpstepheightvec = stepheightvec + jumpheight_vec * 0.85; // reduce it a bit to make the jumps easy
588  jumpheight_time = autocvar_sv_jumpvelocity / autocvar_sv_gravity;
589 }
590 
591 bool bot_fixcount(bool multiple_per_frame)
592 {
593  int activerealplayers = 0;
594  int realplayers = 0;
595  if (MUTATOR_CALLHOOK(Bot_FixCount, activerealplayers, realplayers)) {
596  activerealplayers = M_ARGV(0, int);
597  realplayers = M_ARGV(1, int);
598  } else {
600  if(IS_PLAYER(it))
601  ++activerealplayers;
602  ++realplayers;
603  });
604  }
605 
606  int bots;
607  // But don't remove bots immediately on level change, as the real players
608  // usually haven't rejoined yet
609  bots_would_leave = false;
611  bots = min(ceil(fabs(autocvar_bot_vs_human) * activerealplayers), maxclients - realplayers);
612  else if ((realplayers || autocvar_bot_join_empty || (currentbots > 0 && time < 5)))
613  {
614  int minplayers = max(0, floor(autocvar_minplayers));
615  if (teamplay)
617  int minbots = max(0, floor(autocvar_bot_number));
618 
619  // add bots to reach minplayers if needed
620  bots = max(minbots, minplayers - activerealplayers);
621  // cap bots to the max players allowed by the server
622  int player_limit = GetPlayerLimit();
623  if(player_limit)
624  bots = min(bots, max(player_limit - activerealplayers, 0));
625  bots = min(bots, maxclients - realplayers);
626 
627  if(bots > minbots)
628  bots_would_leave = true;
629  }
630  else
631  {
632  // if there are no players, remove bots
633  bots = 0;
634  }
635 
636  // only add one bot per frame to avoid utter chaos
638  {
639  while (currentbots < bots)
640  {
641  if (bot_spawn() == NULL)
642  {
643  bprint("Can not add bot, server full.\n");
644  return false;
645  }
646  if (!multiple_per_frame)
647  {
648  break;
649  }
650  }
651  while (currentbots > bots && bots >= 0)
653  }
654 
655  return true;
656 }
657 
659 {
661  {
662  // after the end of the match all bots stay unless all human players disconnect
663  int realplayers = 0;
664  FOREACH_CLIENT(IS_REAL_CLIENT(it), { ++realplayers; });
665  if (!realplayers)
666  {
667  FOREACH_CLIENT(IS_BOT_CLIENT(it), { dropclient(it); });
668  currentbots = 0;
669  }
670  return;
671  }
672 
673  if (game_stopped)
674  return;
675 
676  // Added 0.5 to avoid possible addition + immediate removal of bots that would make them appear as
677  // spectators in the scoreboard and never go away. This issue happens at time 2 if map is changed
678  // with the gotomap command, minplayers is > 1 and human clients join as players very soon
679  // either intentionally or automatically (sv_spectate 0)
680  if (time < 2.5)
681  {
682  currentbots = -1;
683  return;
684  }
685 
686  if (currentbots == -1)
687  {
688  // count bots already in the server from the previous match
689  currentbots = 0;
691  }
692 
693  if(autocvar_skill != skill)
694  {
695  float wpcost_update = false;
697  wpcost_update = true;
698  if(skill < autocvar_bot_ai_bunnyhop_skilloffset && autocvar_skill >= autocvar_bot_ai_bunnyhop_skilloffset)
699  wpcost_update = true;
700 
702  if (wpcost_update)
704  }
705 
708 
710  {
711  float a;
713  if(a)
714  autoskill(a);
716  }
717 
719  {
720  if(!bot_fixcount(false))
721  botframe_nextthink = time + 10;
722  }
723 
725  {
727  localcmd("quit\n");
728  }
729 
732  {
734  {
737  }
738  else
739  {
740  // TODO: Make this check cleaner
741  IL_EACH(g_waypoints, time - it.nextthink > 10,
742  {
743  waypoint_save_links();
744  break;
745  });
746  }
747  }
748  else
749  {
753  }
754 
755  if (bot_list)
756  {
757  // cycle the goal token from one bot to the next each frame
758  // (this prevents them from all doing spawnfunc_waypoint searches on the same
759  // frame, which causes choppy framerates)
761  {
762  // give goal token to the first bot without goals; if all bots don't have
763  // any goal (or are dead/frozen) simply give it to the next one
764  bot_strategytoken_taken = false;
765  entity bot_strategytoken_save = bot_strategytoken;
766  while (true)
767  {
768  if (bot_strategytoken)
770  if (!bot_strategytoken)
772 
773  if (!(IS_DEAD(bot_strategytoken) || STAT(FROZEN, bot_strategytoken))
774  && !bot_strategytoken.goalcurrent)
775  break;
776 
777  if (!bot_strategytoken_save) // break loop if all the bots are dead or frozen
778  break;
779  if (bot_strategytoken == bot_strategytoken_save)
780  bot_strategytoken_save = NULL; // looped through all the bots
781  }
782  }
783 
785  {
786  float interval;
788  if (botframe_nextdangertime < time - interval * 1.5)
792  }
793  }
794 
797 
800 
801  if (currentbots > 0)
803 }
#define PHYS_INPUT_BUTTON_ATCK2(s)
Definition: player.qh:148
float DEAD_DYING
Definition: progsdefs.qc:275
float bot_pingskill
Definition: bot.qh:29
#define PHYS_INPUT_BUTTON_JUMP(s)
Definition: player.qh:147
#define IL_EACH(this, cond, body)
float dmg_take
Definition: view.qh:119
void waypoint_updatecost_foralllinks()
Definition: waypoints.qc:973
#define PHYS_INPUT_BUTTON_CHAT(s)
Definition: player.qh:155
#define PHYS_INPUT_BUTTON_CROUCH(s)
Definition: player.qh:150
bool autocvar_bot_usemodelnames
Definition: cvars.qh:57
entity bot_cmd_current
Definition: scripting.qh:60
float botframe_loadedforcedlinks
Definition: waypoints.qh:23
bool SetPlayerTeam(entity player, int team_index, int type)
Sets the team of the player.
Definition: teamplay.qc:237
#define PHYS_INPUT_BUTTON_DRAG(s)
Definition: player.qh:153
string RandomSelection_chosen_string
Definition: random.qh:7
void ClientConnect(entity this)
ClientConnect
Definition: client.qc:1096
#define PHYS_INPUT_BUTTON_HOOK(s)
Definition: player.qh:151
float bot_dodgeskill
Definition: bot.qh:27
float bot_weaponskill
Definition: bot.qh:31
float autoskill_nextthink
Definition: bot.qh:23
float bot_preferredcolors
Definition: bot.qh:60
float botframe_nextthink
Definition: bot.qh:79
ERASEABLE void RandomSelection_Init()
Definition: random.qc:4
string autocvar_bot_ai_custom_weapon_priority_mid
Definition: cvars.qh:32
void TeamBalance_Destroy(entity balance)
Destroy the team balance entity.
Definition: teamplay.qc:627
float bot_thinkskill
Definition: bot.qh:39
float bot_aggresskill
Definition: bot.qh:32
int aistatus
Definition: bot.qh:20
entity() spawn
int player_count
Definition: api.qh:103
const float MOVE_NORMAL
Definition: csprogsdefs.qc:252
ClientState CS(Client this)
Definition: state.qh:47
#define FOREACH_CLIENT(cond, body)
Definition: utils.qh:49
vector v_angle
Definition: progsdefs.qc:161
float skill
Definition: api.qh:35
#define IS_OBSERVER(v)
Definition: utils.qh:11
#define PHYS_INPUT_BUTTON_ZOOM(s)
Definition: player.qh:149
int GetPlayerLimit()
Definition: client.qc:1990
#define CS_CVAR(this)
Definition: state.qh:51
bool Team_IsValidIndex(int index)
Returns whether the team index is valid.
Definition: teams.qh:151
string playerskin
string netname
Definition: powerups.qc:20
const float FILE_READ
Definition: csprogsdefs.qc:231
float bot_distance_far
Definition: bot.qh:45
string netname_freeme
Definition: bot.qh:53
string autocvar_bot_config_file
Definition: cvars.qh:49
void bot_setclientfields(entity this)
Definition: bot.qc:36
float maxclients
Definition: csprogsdefs.qc:21
#define RandomSelection_AddString(s, weight, priority)
Definition: random.qh:16
void bot_endgame()
Definition: bot.qc:369
string autocvar_bot_ai_custom_weapon_priority_close
Definition: cvars.qh:29
bool bot_fixcount(bool multiple_per_frame)
Definition: bot.qc:591
void TeamBalance_JoinBestTeam(entity player)
Assigns the given player to a team that will make the game most balanced.
Definition: teamplay.qc:451
float FL_GODMODE
Definition: progsdefs.qc:237
int autocvar_bot_vs_human
Definition: cvars.qh:70
int autocvar_minplayers
Definition: cvars.qh:71
#define PARSE_WEAPON_PRIORITIES(dist)
#define autocvar_bot_prefix
Definition: cvars.qh:55
void bot_serverframe()
Definition: bot.qc:658
string autocvar_bot_ai_custom_weapon_priority_distances
Definition: cvars.qh:30
void navigation_goalrating_timeout_force(entity this)
Definition: navigation.qc:28
Player has manually selected their team.
Definition: teamplay.qh:112
string playermodel_freeme
Definition: bot.qh:54
float botframe_cachedwaypointlinks
Definition: waypoints.qh:24
float DEAD_DEAD
Definition: progsdefs.qc:276
void bot_calculate_stepheightvec()
Definition: bot.qc:583
entity bot_spawn()
Definition: bot.qc:42
#define IS_REAL_CLIENT(v)
Definition: utils.qh:17
void bot_removefromlargestteam()
Definition: bot.qc:454
float autocvar_bot_ai_dangerdetectioninterval
Definition: cvars.qh:33
float autocvar_bot_ai_bunnyhop_skilloffset
Definition: cvars.qh:22
#define strcpy(this, s)
Definition: string.qh:49
int autocvar_g_waypointeditor_auto
Definition: cvars.qh:63
int currentbots
Definition: api.qh:104
float botframe_spawnedwaypoints
Definition: bot.qh:78
float bot_moveskill
Definition: api.qh:42
void bot_clientconnect(entity this)
Definition: bot.qc:426
void bot_lagfunc(entity this, float t, float f1, float f2, entity e1, vector v1, vector v2, vector v3, vector v4)
Definition: aim.qc:151
bool waypointeditor_enabled
Definition: waypoints.qh:3
float bot_rangepreference
Definition: bot.qh:33
float bot_distance_close
Definition: bot.qh:46
float autocvar_bot_ai_dangerdetectionupdates
Definition: cvars.qh:34
const float MOVE_NOMONSTERS
Definition: csprogsdefs.qc:253
void waypoint_load_hardwiredlinks()
Definition: waypoints.qc:1470
void bot_think(entity this)
Definition: bot.qc:57
string cleanname
Definition: api.qh:45
float dmg_save
Definition: progsdefs.qc:199
bool waypoint_load_links()
Definition: waypoints.qc:1322
float waypoint_loadall()
Definition: waypoints.qc:1842
entity dmg_inflictor
Definition: progsdefs.qc:200
void bot_clientdisconnect(entity this)
Definition: bot.qc:411
#define LOG_INFOF(...)
Definition: log.qh:71
bool navigation_goalrating_timeout(entity this)
Definition: navigation.qc:43
spree_cen s1 spree_cen s1 spree_cen s1 spree_cen s1 spree_cen s1 spree_cen s1 spree_cen s1 f1 s1 strcat(_("Level %s: "), "^BG%s\3\, _("^BGPress ^F2%s^BG to enter the game"))
void autoskill(float factor)
Definition: bot.qc:537
#define PHYS_INPUT_BUTTON_USE(s)
Definition: player.qh:154
void TeamBalance_GetTeamCounts(entity balance, entity ignore)
Counts the number of players and various other information about each team.
Definition: teamplay.qc:681
#define autocvar_bot_suffix
Definition: cvars.qh:56
void bot_custom_weapon_priority_setup()
Definition: bot.qc:308
float fixangle
Definition: progsdefs.qc:160
float botframe_nextdangertime
Definition: bot.qh:80
bool bots_would_leave
Definition: api.qh:101
#define NULL
Definition: post.qh:17
STATIC_INIT(bot)
Definition: bot.qc:33
string playerskin_freeme
Definition: bot.qh:55
float bot_mouseskill
Definition: bot.qh:37
int autocvar_bot_number
Definition: cvars.qh:67
#define PHYS_INPUT_BUTTON_ATCK(s)
Definition: player.qh:146
int AvailableTeams()
Definition: scores_rules.qc:22
bool autocvar_bot_god
Definition: cvars.qh:50
#define setcolor
Definition: pre.qh:11
float bot_nextthink
Definition: bot.qh:57
float teamplay
Definition: progsdefs.qc:31
void bot_setnameandstuff(entity this)
Definition: bot.qc:143
#define M_ARGV(x, type)
Definition: events.qh:17
#define IS_DEAD(s)
Definition: utils.qh:26
bool autocvar_bot_join_empty
Definition: cvars.qh:52
IntrusiveList g_waypoints
Definition: api.qh:148
float bot_custom_weapon
Definition: api.qh:31
float bot_jump_time
Definition: bot.qh:70
float bot_config_loaded
Definition: bot.qh:73
bool campaign_bots_may_start
campaign mode: bots shall spawn but wait for the player to spawn before they do anything in other gam...
Definition: campaign.qh:27
float bot_forced_team
Definition: api.qh:41
float deadflag
Definition: progsdefs.qc:149
float autocvar_skill_auto
Definition: cvars.qh:65
float flags
Definition: csprogsdefs.qc:129
float count
Definition: powerups.qc:22
float bot_aiskill
Definition: bot.qh:40
void bot_relinkplayerlist()
Definition: bot.qc:381
void havocbot_setupbot(entity this)
Definition: havocbot.qc:1683
float bot_offsetskill
Definition: bot.qh:36
float bot_aimskill
Definition: bot.qh:35
#define tokenizebyseparator
Definition: dpextensions.qh:21
#define MUTATOR_CALLHOOK(id,...)
Definition: base.qh:140
#define IS_BOT_CLIENT(v)
want: (IS_CLIENT(v) && !IS_REAL_CLIENT(v))
Definition: utils.qh:15
float createdtime
Definition: bot.qh:59
float clientcolors
void setItemGroupCount()
Definition: items.qc:1205
float havocbot_keyboardskill
Definition: havocbot.qh:7
void botframe_showwaypointlinks()
Definition: waypoints.qc:2156
entity bot_list
Definition: bot.qh:48
int TeamBalance_GetNumberOfPlayers(entity balance, int index)
Returns the number of players (both humans and bots) in a team.
Definition: teamplay.qc:763
bool intermission_running
Definition: intermission.qh:9
string autocvar_bot_ai_custom_weapon_priority_far
Definition: cvars.qh:31
#define strfree(this)
Definition: string.qh:56
vector angles
Definition: csprogsdefs.qc:104
PutClientInServer(this)
bool autocvar_g_campaign
Definition: campaign.qh:6
best
Definition: all.qh:77
const int AI_STATUS_STUCK
Definition: bot.qh:17
bool bot_ispaused(entity this)
Definition: scripting.qc:1003
entity TeamBalance_CheckAllowedTeams(entity for_whom)
Checks whether the player can join teams according to global configuration and mutator settings...
Definition: teamplay.qc:487
int autocvar_minplayers_per_team
Definition: cvars.qh:72
entity bot_strategytoken
Definition: bot.qh:76
if(IS_DEAD(this))
Definition: impulse.qc:92
void bot_removenewest()
Definition: bot.qc:501
float time
Definition: csprogsdefs.qc:16
void bot_clearqueue(entity bot)
Definition: scripting.qc:21
#define READSKILL(f, w, r)
#define PHYS_INPUT_BUTTON_INFO(s)
Definition: player.qh:152
float isbot
Definition: api.qh:49
bool autocvar_waypoint_benchmark
Definition: cvars.qh:66
string playermodel
bool Team_IsValidTeam(int team_num)
Returns whether team value is valid.
Definition: teams.qh:133
#define IS_PLAYER(v)
Definition: utils.qh:9
float autocvar_bot_ai_thinkinterval
Definition: cvars.qh:44
float bot_strategytoken_taken
Definition: bot.qh:75
#define LOG_DEBUG(...)
Definition: log.qh:85
#define autocvar_skill
Definition: cvars.qh:64
bool autocvar_bot_navigation_ignoreplayers
Definition: cvars.qh:53
void botframe_autowaypoints()
Definition: waypoints.qc:2472