#include <lib/warpzone/common.qh>
#include <lib/warpzone/util_server.qh>
+STATIC_INIT(bot) { bot_calculate_stepheightvec(); }
+
// TODO: remove this function! its only purpose is to update these fields since bot_setnameandstuff is called before ClientState
void bot_setclientfields(entity this)
{
}
else
{
+ entity balance = TeamBalance_CheckAllowedTeams(NULL);
+ TeamBalance_GetTeamCounts(balance, NULL);
+ int smallest_team = -1;
+ int smallest_count = -1;
+ if (teamplay)
+ {
+ for (int i = 1; i <= AvailableTeams(); ++i)
+ {
+ // NOTE if (autocvar_g_campaign && autocvar_g_campaign_forceteam == i)
+ // TeamBalance_GetNumberOfPlayers(balance, i); returns the number of players + 1
+ // since it keeps a spot for the real player in the desired team
+ int count = TeamBalance_GetNumberOfPlayers(balance, i);
+ if (smallest_count < 0 || count < smallest_count)
+ {
+ smallest_team = i;
+ smallest_count = count;
+ }
+ }
+ }
+ TeamBalance_Destroy(balance);
RandomSelection_Init();
while((readfile = fgets(file)))
{
continue;
if(substring(readfile, 0, 1) == "#")
continue;
+ // NOTE if the line is empty tokenizebyseparator(readfile, "\t")
+ // will create 1 empty token because there's no separator (bug?)
+ if (readfile == "")
+ continue;
tokens = tokenizebyseparator(readfile, "\t");
if(tokens == 0)
continue;
s = argv(0);
- prio = 1;
+ prio = 0;
+ bool conflict = false;
FOREACH_CLIENT(IS_BOT_CLIENT(it), {
- if(s == it.cleanname)
+ if (s == it.cleanname)
{
- prio = 0;
+ conflict = true;
break;
}
});
+ if (!conflict)
+ prio += 1;
+ if (teamplay && !(autocvar_bot_vs_human && AvailableTeams() == 2))
+ {
+ int forced_team = stof(argv(5));
+ if (!Team_IsValidIndex(forced_team))
+ forced_team = 0;
+ if (!forced_team || forced_team == smallest_team)
+ prio += 2;
+ }
RandomSelection_AddString(readfile, 1, prio);
}
readfile = RandomSelection_chosen_string;
if(argv(4) != "" && stof(argv(4)) >= 0) bot_pants = argv(4);
else bot_pants = ftos(floor(random() * 15));
- this.bot_forced_team = stof(argv(5));
+ if (teamplay && !(autocvar_bot_vs_human && AvailableTeams() == 2))
+ this.bot_forced_team = stof(argv(5));
+ else
+ this.bot_forced_team = 0;
prio = 6;
- #define READSKILL(f, w, r) MACRO_BEGIN { \
+ #define READSKILL(f, w, r) MACRO_BEGIN \
if(argv(prio) != "") \
this.f = stof(argv(prio)) * w; \
else \
this.f = (!autocvar_g_campaign) * (2 * random() - 1) * r * w; \
prio++; \
- } MACRO_END
+ MACRO_END
//print(bot_name, ": ping=", argv(9), "\n");
READSKILL(havocbot_keyboardskill, 0.5, 0.5); // keyboard skill
READSKILL(bot_thinkskill, 1, 0.5); // think skill
READSKILL(bot_aiskill, 2, 0); // "ai" skill
+ if (file >= 0 && argv(prio) != "")
+ LOG_INFOF("^1Warning^7: too many parameters for bot %s, please check format of %s", bot_name, autocvar_bot_config_file);
+
this.bot_config_loaded = true;
// this is really only a default, TeamBalance_JoinBestTeam is called later
void bot_custom_weapon_priority_setup()
{
+ static string bot_priority_far_prev;
+ static string bot_priority_mid_prev;
+ static string bot_priority_close_prev;
+ static string bot_priority_distances_prev;
float tokens, i, w;
bot_custom_weapon = false;
if( autocvar_bot_ai_custom_weapon_priority_far == "" ||
autocvar_bot_ai_custom_weapon_priority_mid == "" ||
autocvar_bot_ai_custom_weapon_priority_close == "" ||
- autocvar_bot_ai_custom_weapon_priority_distances == ""
+ autocvar_bot_ai_custom_weapon_priority_distances == ""
)
return;
- // Parse distances
- tokens = tokenizebyseparator(autocvar_bot_ai_custom_weapon_priority_distances," ");
-
- if (tokens!=2)
- return;
-
- bot_distance_far = stof(argv(0));
- bot_distance_close = stof(argv(1));
-
- if(bot_distance_far < bot_distance_close){
- bot_distance_far = stof(argv(1));
- bot_distance_close = stof(argv(0));
- }
-
- // Initialize list of weapons
- bot_weapons_far[0] = -1;
- bot_weapons_mid[0] = -1;
- bot_weapons_close[0] = -1;
-
- // Parse far distance weapon priorities
- tokens = tokenizebyseparator(W_NumberWeaponOrder(autocvar_bot_ai_custom_weapon_priority_far)," ");
+ if (bot_priority_distances_prev != autocvar_bot_ai_custom_weapon_priority_distances)
+ {
+ strcpy(bot_priority_distances_prev, autocvar_bot_ai_custom_weapon_priority_distances);
+ tokens = tokenizebyseparator(autocvar_bot_ai_custom_weapon_priority_distances," ");
- int c = 0;
- for(i=0; i < tokens && c < Weapons_COUNT; ++i){
- w = stof(argv(i));
- if ( w >= WEP_FIRST && w <= WEP_LAST) {
- bot_weapons_far[c] = w;
- ++c;
- }
- }
- if(c < Weapons_COUNT)
- bot_weapons_far[c] = -1;
+ if (tokens!=2)
+ return;
- // Parse mid distance weapon priorities
- tokens = tokenizebyseparator(W_NumberWeaponOrder(autocvar_bot_ai_custom_weapon_priority_mid)," ");
+ bot_distance_far = stof(argv(0));
+ bot_distance_close = stof(argv(1));
- c = 0;
- for(i=0; i < tokens && c < Weapons_COUNT; ++i){
- w = stof(argv(i));
- if ( w >= WEP_FIRST && w <= WEP_LAST) {
- bot_weapons_mid[c] = w;
- ++c;
+ if(bot_distance_far < bot_distance_close){
+ bot_distance_far = stof(argv(1));
+ bot_distance_close = stof(argv(0));
}
}
- if(c < Weapons_COUNT)
- bot_weapons_mid[c] = -1;
- // Parse close distance weapon priorities
- tokens = tokenizebyseparator(W_NumberWeaponOrder(autocvar_bot_ai_custom_weapon_priority_close)," ");
-
- c = 0;
- for(i=0; i < tokens && i < Weapons_COUNT; ++i){
- w = stof(argv(i));
- if ( w >= WEP_FIRST && w <= WEP_LAST) {
- bot_weapons_close[c] = w;
- ++c;
- }
- }
- if(c < Weapons_COUNT)
- bot_weapons_close[c] = -1;
+ int c;
+
+ #define PARSE_WEAPON_PRIORITIES(dist) MACRO_BEGIN \
+ 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 < Weapons_COUNT; ++i) { \
+ w = stof(argv(i)); \
+ if (w >= WEP_FIRST && w <= WEP_LAST) { \
+ bot_weapons_##dist[c] = w; \
+ ++c; \
+ } \
+ } \
+ if (c < Weapons_COUNT) \
+ bot_weapons_##dist[c] = -1; \
+ } \
+ MACRO_END
+
+ PARSE_WEAPON_PRIORITIES(far);
+ PARSE_WEAPON_PRIORITIES(mid);
+ PARSE_WEAPON_PRIORITIES(close);
bot_custom_weapon = true;
}
bot_setclientfields(this);
}
- if(this.bot_forced_team==1)
- this.team = NUM_TEAM_1;
- else if(this.bot_forced_team==2)
- this.team = NUM_TEAM_2;
- else if(this.bot_forced_team==3)
- this.team = NUM_TEAM_3;
- else if(this.bot_forced_team==4)
- this.team = NUM_TEAM_4;
+ if (teamplay && Team_IsValidIndex(this.bot_forced_team))
+ {
+ SetPlayerTeam(this, this.bot_forced_team, TEAM_CHANGE_MANUAL);
+ }
else
+ {
+ this.bot_forced_team = 0;
TeamBalance_JoinBestTeam(this);
+ }
havocbot_setupbot(this);
}
stepheightvec = autocvar_sv_stepheight * '0 0 1';
jumpheight_vec = (autocvar_sv_jumpvelocity ** 2) / (2 * autocvar_sv_gravity) * '0 0 1';
jumpstepheightvec = stepheightvec + jumpheight_vec * 0.85; // reduce it a bit to make the jumps easy
+ jumpheight_time = autocvar_sv_jumpvelocity / autocvar_sv_gravity;
}
-float bot_fixcount()
+bool bot_fixcount()
{
int activerealplayers = 0;
int realplayers = 0;
else if ((realplayers || autocvar_bot_join_empty || (currentbots > 0 && time < 5)))
{
int minplayers = max(0, floor(autocvar_minplayers));
+ if (teamplay)
+ minplayers = max(0, floor(autocvar_minplayers_per_team) * AvailableTeams());
int minbots = max(0, floor(autocvar_bot_number));
// add bots to reach minplayers if needed
bots = max(minbots, minplayers - activerealplayers);
// cap bots to the max players allowed by the server
+ int player_limit = GetPlayerLimit();
+ if(player_limit)
+ bots = min(bots, max(player_limit - activerealplayers, 0));
bots = min(bots, maxclients - realplayers);
if(bots > minbots)
return false;
}
}
- while (currentbots > bots)
+ while (currentbots > bots && bots >= 0)
bot_removenewest();
}
if(botframe_cachedwaypointlinks)
{
if(!botframe_loadedforcedlinks)
- waypoint_load_links_hardwired();
+ waypoint_load_hardwiredlinks();
}
else
{
if (autocvar_g_waypointeditor_auto)
botframe_autowaypoints();
- if(time > bot_cvar_nextthink)
- {
- if(currentbots>0)
- bot_custom_weapon_priority_setup();
- bot_cvar_nextthink = time + 5;
- }
+ if (currentbots > 0)
+ bot_custom_weapon_priority_setup();
}