Xonotic
bot.qc File Reference
+ Include dependency graph for bot.qc:
+ This graph shows which files directly or indirectly include this file:

Go to the source code of this file.

Macros

#define PARSE_WEAPON_PRIORITIES(dist)
 
#define READSKILL(f, w, r)
 

Functions

void autoskill (float factor)
 
void bot_calculate_stepheightvec ()
 
void bot_clientconnect (entity this)
 
void bot_clientdisconnect (entity this)
 
void bot_custom_weapon_priority_setup ()
 
void bot_endgame ()
 
bool bot_fixcount (bool multiple_per_frame)
 
void bot_relinkplayerlist ()
 
void bot_removefromlargestteam ()
 
void bot_removenewest ()
 
void bot_serverframe ()
 
void bot_setclientfields (entity this)
 
void bot_setnameandstuff (entity this)
 
entity bot_spawn ()
 
void bot_think (entity this)
 
 STATIC_INIT (bot)
 

Macro Definition Documentation

◆ PARSE_WEAPON_PRIORITIES

#define PARSE_WEAPON_PRIORITIES (   dist)
Value:
if (bot_priority_##dist##_prev != autocvar_bot_ai_custom_weapon_priority_##dist) { \
strcpy(bot_priority_##dist##_prev, autocvar_bot_ai_custom_weapon_priority_##dist); \
tokens = tokenizebyseparator(W_NumberWeaponOrder(autocvar_bot_ai_custom_weapon_priority_##dist)," "); \
bot_weapons_##dist[0] = -1; \
c = 0; \
for(i = 0; i < tokens && c < REGISTRY_COUNT(Weapons); ++i) { \
w = stof(argv(i)); \
if (w >= WEP_FIRST && w <= WEP_LAST) { \
bot_weapons_##dist[c] = w; \
++c; \
} \
} \
if (c < REGISTRY_COUNT(Weapons)) \
bot_weapons_##dist[c] = -1; \
} \
MACRO_END
const int WEP_FIRST
Definition: all.qh:304
#define WEP_LAST
Definition: all.qh:305
#define REGISTRY_COUNT(id)
Definition: registry.qh:18
string W_NumberWeaponOrder(string order)
Definition: all.qc:136
#define tokenizebyseparator
Definition: dpextensions.qh:21
if(IS_DEAD(this))
Definition: impulse.qc:92

Referenced by bot_custom_weapon_priority_setup().

◆ READSKILL

#define READSKILL (   f,
  w,
 
)
Value:
if(argv(prio) != "") \
this.f = stof(argv(prio)) * w; \
else \
this.f = (!autocvar_g_campaign) * (2 * random() - 1) * r * w; \
prio++; \
MACRO_END
bool autocvar_g_campaign
Definition: campaign.qh:6
if(IS_DEAD(this))
Definition: impulse.qc:92

Referenced by bot_setnameandstuff().

Function Documentation

◆ autoskill()

void autoskill ( float  factor)

Definition at line 537 of file bot.qc.

References autocvar_skill, bprint(), cvar_set(), FOREACH_CLIENT, ftos(), IS_PLAYER, IS_REAL_CLIENT, LOG_DEBUG, max(), and strcat().

Referenced by bot_serverframe().

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 }
#define FOREACH_CLIENT(cond, body)
Definition: utils.qh:49
#define IS_REAL_CLIENT(v)
Definition: utils.qh:17
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"))
#define IS_PLAYER(v)
Definition: utils.qh:9
#define LOG_DEBUG(...)
Definition: log.qh:85
#define autocvar_skill
Definition: cvars.qh:64
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ bot_calculate_stepheightvec()

void bot_calculate_stepheightvec ( )

Definition at line 583 of file bot.qc.

References jumpheight_time, jumpheight_vec, jumpstepheightvec, and stepheightvec.

Referenced by bot_serverframe(), STATIC_INIT(), and waypoint_think().

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 }
+ Here is the caller graph for this function:

◆ bot_clientconnect()

void bot_clientconnect ( entity  this)

Definition at line 426 of file bot.qc.

References bot_config_loaded, bot_forced_team, bot_lagfunc(), bot_nextthink, bot_preferredcolors, bot_setclientfields(), bot_setnameandstuff(), clientcolors, createdtime, havocbot_setupbot(), IS_BOT_CLIENT, isbot, random(), SetPlayerTeam(), TEAM_CHANGE_MANUAL, Team_IsValidIndex(), TeamBalance_JoinBestTeam(), teamplay, and time.

Referenced by ClientConnect(), and PlayerState_detach().

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 }
bool SetPlayerTeam(entity player, int team_index, int type)
Sets the team of the player.
Definition: teamplay.qc:237
float bot_preferredcolors
Definition: bot.qh:60
bool Team_IsValidIndex(int index)
Returns whether the team index is valid.
Definition: teams.qh:151
void bot_setclientfields(entity this)
Definition: bot.qc:36
void TeamBalance_JoinBestTeam(entity player)
Assigns the given player to a team that will make the game most balanced.
Definition: teamplay.qc:451
Player has manually selected their team.
Definition: teamplay.qh:112
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
float bot_nextthink
Definition: bot.qh:57
float teamplay
Definition: progsdefs.qc:31
void bot_setnameandstuff(entity this)
Definition: bot.qc:143
float bot_config_loaded
Definition: bot.qh:73
float bot_forced_team
Definition: api.qh:41
void havocbot_setupbot(entity this)
Definition: havocbot.qc:1683
#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
float time
Definition: csprogsdefs.qc:16
float isbot
Definition: api.qh:49
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ bot_clientdisconnect()

void bot_clientdisconnect ( entity  this)

Definition at line 411 of file bot.qc.

References bot_clearqueue(), bot_cmd_current, bot_waypoint_queue_owner, cleanname, IS_BOT_CLIENT, netname_freeme, NULL, playermodel_freeme, playerskin_freeme, and strfree.

Referenced by ClientState_attach(), and ClientState_detach().

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 }
entity bot_cmd_current
Definition: scripting.qh:60
string netname_freeme
Definition: bot.qh:53
string playermodel_freeme
Definition: bot.qh:54
string cleanname
Definition: api.qh:45
#define NULL
Definition: post.qh:17
string playerskin_freeme
Definition: bot.qh:55
#define IS_BOT_CLIENT(v)
want: (IS_CLIENT(v) && !IS_REAL_CLIENT(v))
Definition: utils.qh:15
#define strfree(this)
Definition: string.qh:56
void bot_clearqueue(entity bot)
Definition: scripting.qc:21
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ bot_custom_weapon_priority_setup()

void bot_custom_weapon_priority_setup ( )

Definition at line 308 of file bot.qc.

References argv(), autocvar_bot_ai_custom_weapon_priority_close, autocvar_bot_ai_custom_weapon_priority_distances, autocvar_bot_ai_custom_weapon_priority_far, autocvar_bot_ai_custom_weapon_priority_mid, bot_custom_weapon, bot_distance_close, bot_distance_far, PARSE_WEAPON_PRIORITIES, stof(), strcpy, and tokenizebyseparator.

Referenced by bot_serverframe().

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 }
string autocvar_bot_ai_custom_weapon_priority_mid
Definition: cvars.qh:32
float bot_distance_far
Definition: bot.qh:45
string autocvar_bot_ai_custom_weapon_priority_close
Definition: cvars.qh:29
#define PARSE_WEAPON_PRIORITIES(dist)
string autocvar_bot_ai_custom_weapon_priority_distances
Definition: cvars.qh:30
#define strcpy(this, s)
Definition: string.qh:49
float bot_distance_close
Definition: bot.qh:46
float bot_custom_weapon
Definition: api.qh:31
#define tokenizebyseparator
Definition: dpextensions.qh:21
string autocvar_bot_ai_custom_weapon_priority_far
Definition: cvars.qh:31
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ bot_endgame()

void bot_endgame ( )

Definition at line 369 of file bot.qc.

References bot_list, bot_relinkplayerlist(), entity(), and setcolor.

Referenced by Shutdown().

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 }
entity() spawn
#define setcolor
Definition: pre.qh:11
void bot_relinkplayerlist()
Definition: bot.qc:381
entity bot_list
Definition: bot.qh:48
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ bot_fixcount()

bool bot_fixcount ( bool  multiple_per_frame)

Definition at line 591 of file bot.qc.

References autocvar_bot_join_empty, autocvar_bot_number, autocvar_bot_vs_human, autocvar_minplayers, autocvar_minplayers_per_team, AvailableTeams(), bot_removenewest(), bot_spawn(), botframe_nextthink, bots_would_leave, bprint(), ceil(), currentbots, fabs(), floor(), FOREACH_CLIENT, GetPlayerLimit(), IS_PLAYER, IS_REAL_CLIENT, M_ARGV, max(), maxclients, min(), MUTATOR_CALLHOOK, NULL, teamplay, and time.

Referenced by bot_serverframe(), GameCommand_bot_cmd(), and GameCommand_setbots().

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 }
float botframe_nextthink
Definition: bot.qh:79
#define FOREACH_CLIENT(cond, body)
Definition: utils.qh:49
int GetPlayerLimit()
Definition: client.qc:1990
float maxclients
Definition: csprogsdefs.qc:21
int autocvar_bot_vs_human
Definition: cvars.qh:70
int autocvar_minplayers
Definition: cvars.qh:71
entity bot_spawn()
Definition: bot.qc:42
#define IS_REAL_CLIENT(v)
Definition: utils.qh:17
int currentbots
Definition: api.qh:104
bool bots_would_leave
Definition: api.qh:101
#define NULL
Definition: post.qh:17
int autocvar_bot_number
Definition: cvars.qh:67
int AvailableTeams()
Definition: scores_rules.qc:22
float teamplay
Definition: progsdefs.qc:31
#define M_ARGV(x, type)
Definition: events.qh:17
bool autocvar_bot_join_empty
Definition: cvars.qh:52
#define MUTATOR_CALLHOOK(id,...)
Definition: base.qh:140
int autocvar_minplayers_per_team
Definition: cvars.qh:72
void bot_removenewest()
Definition: bot.qc:501
float time
Definition: csprogsdefs.qc:16
#define IS_PLAYER(v)
Definition: utils.qh:9
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ bot_relinkplayerlist()

void bot_relinkplayerlist ( )

Definition at line 381 of file bot.qc.

References bot_ispaused(), bot_list, bot_strategytoken, bot_strategytoken_taken, currentbots, entity(), FOREACH_CLIENT, IS_BOT_CLIENT, IS_OBSERVER, NULL, and player_count.

Referenced by bot_cmd_continue(), bot_cmd_pause(), bot_endgame(), ClientConnect(), ClientDisconnect(), PutClientInServer(), and PutObserverInServer().

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 }
entity() spawn
int player_count
Definition: api.qh:103
#define FOREACH_CLIENT(cond, body)
Definition: utils.qh:49
#define IS_OBSERVER(v)
Definition: utils.qh:11
int currentbots
Definition: api.qh:104
#define NULL
Definition: post.qh:17
#define IS_BOT_CLIENT(v)
want: (IS_CLIENT(v) && !IS_REAL_CLIENT(v))
Definition: utils.qh:15
entity bot_list
Definition: bot.qh:48
bool bot_ispaused(entity this)
Definition: scripting.qc:1003
entity bot_strategytoken
Definition: bot.qh:76
float bot_strategytoken_taken
Definition: bot.qh:75
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ bot_removefromlargestteam()

void bot_removefromlargestteam ( )

Definition at line 454 of file bot.qc.

References best, entity(), FOREACH_CLIENT, if(), NULL, Team_IsValidTeam(), TeamBalance_CheckAllowedTeams(), and TeamBalance_GetTeamCounts().

Referenced by bot_removenewest().

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 }
void TeamBalance_Destroy(entity balance)
Destroy the team balance entity.
Definition: teamplay.qc:627
entity() spawn
#define FOREACH_CLIENT(cond, body)
Definition: utils.qh:49
int currentbots
Definition: api.qh:104
void TeamBalance_GetTeamCounts(entity balance, entity ignore)
Counts the number of players and various other information about each team.
Definition: teamplay.qc:681
#define NULL
Definition: post.qh:17
best
Definition: all.qh:77
entity TeamBalance_CheckAllowedTeams(entity for_whom)
Checks whether the player can join teams according to global configuration and mutator settings...
Definition: teamplay.qc:487
if(IS_DEAD(this))
Definition: impulse.qc:92
bool Team_IsValidTeam(int team_num)
Returns whether team value is valid.
Definition: teams.qh:133
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ bot_removenewest()

void bot_removenewest ( )

Definition at line 501 of file bot.qc.

References best, bot_removefromlargestteam(), currentbots, entity(), FOREACH_CLIENT, NULL, and teamplay.

Referenced by bot_fixcount().

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 }
entity() spawn
#define FOREACH_CLIENT(cond, body)
Definition: utils.qh:49
void bot_removefromlargestteam()
Definition: bot.qc:454
int currentbots
Definition: api.qh:104
#define NULL
Definition: post.qh:17
float teamplay
Definition: progsdefs.qc:31
best
Definition: all.qh:77
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ bot_serverframe()

void bot_serverframe ( )

Definition at line 658 of file bot.qc.

References autocvar_bot_ai_bunnyhop_skilloffset, autocvar_bot_ai_dangerdetectioninterval, autocvar_bot_ai_dangerdetectionupdates, autocvar_bot_navigation_ignoreplayers, autocvar_g_waypointeditor_auto, autocvar_skill, autocvar_skill_auto, autocvar_waypoint_benchmark, autoskill(), autoskill_nextthink, bot_calculate_stepheightvec(), bot_custom_weapon_priority_setup(), bot_fixcount(), bot_list, bot_navigation_movemode, bot_strategytoken, bot_strategytoken_taken, botframe_autowaypoints(), botframe_cachedwaypointlinks, botframe_loadedforcedlinks, botframe_nextdangertime, botframe_nextthink, botframe_showwaypointlinks(), botframe_spawnedwaypoints, botframe_updatedangerousobjects(), currentbots, entity(), FOREACH_CLIENT, g_waypoints, IL_EACH, intermission_running, IS_BOT_CLIENT, IS_DEAD, IS_REAL_CLIENT, localcmd, MOVE_NOMONSTERS, MOVE_NORMAL, NULL, skill, time, waypoint_load_hardwiredlinks(), waypoint_load_links(), waypoint_loadall(), waypoint_updatecost_foralllinks(), and waypointeditor_enabled.

Referenced by StartFrame().

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 IL_EACH(this, cond, body)
void waypoint_updatecost_foralllinks()
Definition: waypoints.qc:973
float botframe_loadedforcedlinks
Definition: waypoints.qh:23
float autoskill_nextthink
Definition: bot.qh:23
float botframe_nextthink
Definition: bot.qh:79
entity() spawn
const float MOVE_NORMAL
Definition: csprogsdefs.qc:252
#define FOREACH_CLIENT(cond, body)
Definition: utils.qh:49
float skill
Definition: api.qh:35
bool bot_fixcount(bool multiple_per_frame)
Definition: bot.qc:591
float botframe_cachedwaypointlinks
Definition: waypoints.qh:24
void bot_calculate_stepheightvec()
Definition: bot.qc:583
#define IS_REAL_CLIENT(v)
Definition: utils.qh:17
float autocvar_bot_ai_dangerdetectioninterval
Definition: cvars.qh:33
float autocvar_bot_ai_bunnyhop_skilloffset
Definition: cvars.qh:22
int autocvar_g_waypointeditor_auto
Definition: cvars.qh:63
int currentbots
Definition: api.qh:104
float botframe_spawnedwaypoints
Definition: bot.qh:78
bool waypointeditor_enabled
Definition: waypoints.qh:3
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
bool waypoint_load_links()
Definition: waypoints.qc:1322
float waypoint_loadall()
Definition: waypoints.qc:1842
void autoskill(float factor)
Definition: bot.qc:537
void bot_custom_weapon_priority_setup()
Definition: bot.qc:308
float botframe_nextdangertime
Definition: bot.qh:80
#define NULL
Definition: post.qh:17
#define IS_DEAD(s)
Definition: utils.qh:26
IntrusiveList g_waypoints
Definition: api.qh:148
float autocvar_skill_auto
Definition: cvars.qh:65
#define IS_BOT_CLIENT(v)
want: (IS_CLIENT(v) && !IS_REAL_CLIENT(v))
Definition: utils.qh:15
void botframe_showwaypointlinks()
Definition: waypoints.qc:2156
entity bot_list
Definition: bot.qh:48
bool intermission_running
Definition: intermission.qh:9
entity bot_strategytoken
Definition: bot.qh:76
float time
Definition: csprogsdefs.qc:16
bool autocvar_waypoint_benchmark
Definition: cvars.qh:66
float bot_strategytoken_taken
Definition: bot.qh:75
#define autocvar_skill
Definition: cvars.qh:64
bool autocvar_bot_navigation_ignoreplayers
Definition: cvars.qh:53
void botframe_autowaypoints()
Definition: waypoints.qc:2472
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ bot_setclientfields()

void bot_setclientfields ( entity  this)

Definition at line 36 of file bot.qc.

References CS_CVAR.

Referenced by bot_clientconnect(), and bot_spawn().

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 }
#define CS_CVAR(this)
Definition: state.qh:51
+ Here is the caller graph for this function:

◆ bot_setnameandstuff()

void bot_setnameandstuff ( entity  this)

Definition at line 143 of file bot.qc.

References argv(), autocvar_bot_config_file, autocvar_bot_prefix, autocvar_bot_suffix, autocvar_bot_usemodelnames, autocvar_bot_vs_human, autocvar_g_campaign, AvailableTeams(), bot_aggresskill, bot_aimskill, bot_aiskill, bot_config_loaded, bot_dodgeskill, bot_forced_team, bot_mouseskill, bot_moveskill, bot_offsetskill, bot_pingskill, bot_preferredcolors, bot_rangepreference, bot_thinkskill, bot_weaponskill, cleanname, clientcolors, count, entity(), etof(), fclose(), fgets(), FILE_READ, floor(), fopen(), FOREACH_CLIENT, ftos(), havocbot_keyboardskill, IS_BOT_CLIENT, LOG_INFOF, name, netname, netname_freeme, NULL, playermodel, playermodel_freeme, playerskin, playerskin_freeme, random(), RandomSelection_AddString, RandomSelection_chosen_string, RandomSelection_Init(), READSKILL, setcolor, stof(), strcat(), strzone(), substring(), Team_IsValidIndex(), TeamBalance_CheckAllowedTeams(), TeamBalance_Destroy(), TeamBalance_GetNumberOfPlayers(), TeamBalance_GetTeamCounts(), teamplay, and tokenizebyseparator.

Referenced by bot_clientconnect(), and bot_spawn().

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 }
float bot_pingskill
Definition: bot.qh:29
bool autocvar_bot_usemodelnames
Definition: cvars.qh:57
string RandomSelection_chosen_string
Definition: random.qh:7
float bot_dodgeskill
Definition: bot.qh:27
float bot_weaponskill
Definition: bot.qh:31
float bot_preferredcolors
Definition: bot.qh:60
ERASEABLE void RandomSelection_Init()
Definition: random.qc:4
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
entity() spawn
#define FOREACH_CLIENT(cond, body)
Definition: utils.qh:49
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
string netname_freeme
Definition: bot.qh:53
string autocvar_bot_config_file
Definition: cvars.qh:49
#define RandomSelection_AddString(s, weight, priority)
Definition: random.qh:16
int autocvar_bot_vs_human
Definition: cvars.qh:70
#define autocvar_bot_prefix
Definition: cvars.qh:55
string playermodel_freeme
Definition: bot.qh:54
float bot_moveskill
Definition: api.qh:42
float bot_rangepreference
Definition: bot.qh:33
string cleanname
Definition: api.qh:45
#define LOG_INFOF(...)
Definition: log.qh:71
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 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
#define NULL
Definition: post.qh:17
string playerskin_freeme
Definition: bot.qh:55
float bot_mouseskill
Definition: bot.qh:37
int AvailableTeams()
Definition: scores_rules.qc:22
#define setcolor
Definition: pre.qh:11
float teamplay
Definition: progsdefs.qc:31
float bot_config_loaded
Definition: bot.qh:73
float bot_forced_team
Definition: api.qh:41
float count
Definition: powerups.qc:22
float bot_aiskill
Definition: bot.qh:40
float bot_offsetskill
Definition: bot.qh:36
float bot_aimskill
Definition: bot.qh:35
#define tokenizebyseparator
Definition: dpextensions.qh:21
#define IS_BOT_CLIENT(v)
want: (IS_CLIENT(v) && !IS_REAL_CLIENT(v))
Definition: utils.qh:15
float clientcolors
float havocbot_keyboardskill
Definition: havocbot.qh:7
int TeamBalance_GetNumberOfPlayers(entity balance, int index)
Returns the number of players (both humans and bots) in a team.
Definition: teamplay.qc:763
bool autocvar_g_campaign
Definition: campaign.qh:6
entity TeamBalance_CheckAllowedTeams(entity for_whom)
Checks whether the player can join teams according to global configuration and mutator settings...
Definition: teamplay.qc:487
#define READSKILL(f, w, r)
string playermodel
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ bot_spawn()

entity bot_spawn ( )

Definition at line 42 of file bot.qc.

References bot_setclientfields(), bot_setnameandstuff(), ClientConnect(), currentbots, entity(), PutClientInServer(), and setItemGroupCount().

Referenced by bot_fixcount().

43 {
44  entity bot = spawnclient();
45  if (bot)
46  {
50  ClientConnect(bot);
52  PutClientInServer(bot);
53  }
54  return bot;
55 }
void ClientConnect(entity this)
ClientConnect
Definition: client.qc:1096
entity() spawn
void bot_setclientfields(entity this)
Definition: bot.qc:36
int currentbots
Definition: api.qh:104
void bot_setnameandstuff(entity this)
Definition: bot.qc:143
void setItemGroupCount()
Definition: items.qc:1205
PutClientInServer(this)
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ bot_think()

void bot_think ( entity  this)

Definition at line 57 of file bot.qc.

References AI_STATUS_STUCK, aistatus, angles, autocvar_bot_ai_thinkinterval, autocvar_bot_god, autocvar_g_campaign, bot_aiskill, bot_jump_time, bot_nextthink, bot_pingskill, bot_waypoint_queue_owner, bound(), campaign_bots_may_start, CS(), DEAD_DEAD, DEAD_DYING, deadflag, dmg_inflictor, dmg_save, dmg_take, fixangle, FL_GODMODE, flags, IS_DEAD, IS_OBSERVER, IS_PLAYER, max(), min(), navigation_goalrating_timeout(), navigation_goalrating_timeout_force(), navigation_unstuck(), NULL, PHYS_INPUT_BUTTON_ATCK, PHYS_INPUT_BUTTON_ATCK2, PHYS_INPUT_BUTTON_CHAT, PHYS_INPUT_BUTTON_CROUCH, PHYS_INPUT_BUTTON_DRAG, PHYS_INPUT_BUTTON_HOOK, PHYS_INPUT_BUTTON_INFO, PHYS_INPUT_BUTTON_JUMP, PHYS_INPUT_BUTTON_USE, PHYS_INPUT_BUTTON_ZOOM, random(), skill, time, and v_angle.

Referenced by sys_phys_ai().

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 }
#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
float dmg_take
Definition: view.qh:119
#define PHYS_INPUT_BUTTON_CHAT(s)
Definition: player.qh:155
#define PHYS_INPUT_BUTTON_CROUCH(s)
Definition: player.qh:150
#define PHYS_INPUT_BUTTON_DRAG(s)
Definition: player.qh:153
#define PHYS_INPUT_BUTTON_HOOK(s)
Definition: player.qh:151
int aistatus
Definition: bot.qh:20
ClientState CS(Client this)
Definition: state.qh:47
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
float FL_GODMODE
Definition: progsdefs.qc:237
void navigation_goalrating_timeout_force(entity this)
Definition: navigation.qc:28
float DEAD_DEAD
Definition: progsdefs.qc:276
float dmg_save
Definition: progsdefs.qc:199
entity dmg_inflictor
Definition: progsdefs.qc:200
bool navigation_goalrating_timeout(entity this)
Definition: navigation.qc:43
#define PHYS_INPUT_BUTTON_USE(s)
Definition: player.qh:154
float fixangle
Definition: progsdefs.qc:160
#define NULL
Definition: post.qh:17
#define PHYS_INPUT_BUTTON_ATCK(s)
Definition: player.qh:146
bool autocvar_bot_god
Definition: cvars.qh:50
float bot_nextthink
Definition: bot.qh:57
#define IS_DEAD(s)
Definition: utils.qh:26
float bot_jump_time
Definition: bot.qh:70
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 deadflag
Definition: progsdefs.qc:149
float flags
Definition: csprogsdefs.qc:129
float bot_aiskill
Definition: bot.qh:40
vector angles
Definition: csprogsdefs.qc:104
bool autocvar_g_campaign
Definition: campaign.qh:6
const int AI_STATUS_STUCK
Definition: bot.qh:17
float time
Definition: csprogsdefs.qc:16
#define PHYS_INPUT_BUTTON_INFO(s)
Definition: player.qh:152
#define IS_PLAYER(v)
Definition: utils.qh:9
float autocvar_bot_ai_thinkinterval
Definition: cvars.qh:44
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ STATIC_INIT()

STATIC_INIT ( bot  )

Definition at line 33 of file bot.qc.

References bot_calculate_stepheightvec().

void bot_calculate_stepheightvec()
Definition: bot.qc:583
+ Here is the call graph for this function: