#include "../../race.qh"
#include <common/t_items.qh>
-#include "../../mutators/_mod.qh"
+#include <server/mutators/_mod.qh>
#include "../../weapons/accuracy.qh"
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));
- //if (this.bot_painintensity > 0)
- // this.bot_painintensity = this.bot_painintensity - (skill + 1) * 40 * frametime;
-
- //this.bot_painintensity = this.bot_painintensity + this.bot_oldhealth - this.health;
- //this.bot_painintensity = bound(0, this.bot_painintensity, 100);
-
if (!IS_PLAYER(this) || (autocvar_g_campaign && !campaign_bots_may_start))
{
CS(this).movement = '0 0 0';
if (this.deadflag == DEAD_DEAD)
{
PHYS_INPUT_BUTTON_JUMP(this) = true; // press jump to respawn
- this.bot_strategytime = 0;
+ navigation_goalrating_timeout_force(this);
}
}
else if(this.aistatus & AI_STATUS_STUCK)
this.bot_config_loaded = true;
- // this is really only a default, JoinBestTeam is called later
+ // this is really only a default, TeamBalance_JoinBestTeam is called later
setcolor(this, stof(bot_shirt) * 16 + stof(bot_pants));
this.bot_preferredcolors = this.clientcolors;
name = bot_name;
// number bots with identical names
- int j = 0;
- FOREACH_CLIENT(IS_BOT_CLIENT(it), {
- if(it.cleanname == name)
- ++j;
- });
- if (j)
- this.netname = this.netname_freeme = strzone(strcat(prefix, name, "(", ftos(j), ")", suffix));
- else
+ if (name == "")
+ {
+ name = ftos(etof(this));
this.netname = this.netname_freeme = strzone(strcat(prefix, name, suffix));
-
+ }
+ else
+ {
+ int j = 0;
+ FOREACH_CLIENT(IS_BOT_CLIENT(it), {
+ if(it.cleanname == name)
+ ++j;
+ });
+ if (j)
+ this.netname = this.netname_freeme = strzone(strcat(prefix, name, "(", ftos(j), ")", suffix));
+ else
+ this.netname = this.netname_freeme = strzone(strcat(prefix, name, suffix));
+ }
this.cleanname = strzone(name);
// pick the model and skin
if (!IS_BOT_CLIENT(this))
return;
bot_clearqueue(this);
- if(this.cleanname)
- strunzone(this.cleanname);
- if(this.netname_freeme)
- strunzone(this.netname_freeme);
- if(this.playermodel_freeme)
- strunzone(this.playermodel_freeme);
- if(this.playerskin_freeme)
- strunzone(this.playerskin_freeme);
- this.cleanname = string_null;
- this.netname_freeme = string_null;
- this.playermodel_freeme = string_null;
- this.playerskin_freeme = string_null;
+ strfree(this.cleanname);
+ strfree(this.netname_freeme);
+ strfree(this.playermodel_freeme);
+ strfree(this.playerskin_freeme);
if(this.bot_cmd_current)
delete(this.bot_cmd_current);
if(bot_waypoint_queue_owner == this)
else if(this.bot_forced_team==4)
this.team = NUM_TEAM_4;
else
- JoinBestTeam(this, false, true);
+ TeamBalance_JoinBestTeam(this);
havocbot_setupbot(this);
}
void bot_removefromlargestteam()
{
- CheckAllowedTeams(NULL);
- GetTeamCounts(NULL);
+ entity balance = TeamBalance_CheckAllowedTeams(NULL);
+ TeamBalance_GetTeamCounts(balance, NULL);
entity best = NULL;
float besttime = 0;
int thiscount = 0;
- switch(it.team)
+ if (Team_IsValidTeam(it.team))
{
- case NUM_TEAM_1: thiscount = c1; break;
- case NUM_TEAM_2: thiscount = c2; break;
- case NUM_TEAM_3: thiscount = c3; break;
- case NUM_TEAM_4: thiscount = c4; break;
+ thiscount = TeamBalance_GetNumberOfPlayers(balance,
+ Team_TeamToIndex(it.team));
}
if(thiscount > bestcount)
best = it;
}
});
+ TeamBalance_Destroy(balance);
if(!bcount)
return; // no bots to remove
currentbots = currentbots - 1;
void bot_calculate_stepheightvec()
{
stepheightvec = autocvar_sv_stepheight * '0 0 1';
- jumpstepheightvec = stepheightvec +
- ((autocvar_sv_jumpvelocity * autocvar_sv_jumpvelocity) / (2 * autocvar_sv_gravity)) * '0 0 0.85';
- // 0.75 factor is for safety to make the jumps easy
+ 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
}
float bot_fixcount()
void bot_serverframe()
{
+ if (intermission_running && currentbots > 0)
+ {
+ // after the end of the match all bots stay unless all human players disconnect
+ int realplayers = 0;
+ FOREACH_CLIENT(IS_REAL_CLIENT(it), { ++realplayers; });
+ if (!realplayers)
+ {
+ FOREACH_CLIENT(IS_BOT_CLIENT(it), { dropclient(it); });
+ currentbots = 0;
+ }
+ return;
+ }
+
if (game_stopped)
return;
- if (time < 2)
+ // Added 0.5 to avoid possible addition + immediate removal of bots that would make them appear as
+ // spectators in the scoreboard and never go away. This issue happens at time 2 if map is changed
+ // with the gotomap command, minplayers is > 1 and human clients join as players very soon
+ // either intentionally or automatically (sv_spectate 0)
+ if (time < 2.5)
+ {
+ currentbots = -1;
return;
+ }
+
+ if (currentbots == -1)
+ {
+ // count bots already in the server from the previous match
+ currentbots = 0;
+ FOREACH_CLIENT(IS_BOT_CLIENT(it), { ++currentbots; });
+ }
+
+ if(autocvar_skill != skill)
+ {
+ float wpcost_update = false;
+ if(skill >= autocvar_bot_ai_bunnyhop_skilloffset && autocvar_skill < autocvar_bot_ai_bunnyhop_skilloffset)
+ wpcost_update = true;
+ if(skill < autocvar_bot_ai_bunnyhop_skilloffset && autocvar_skill >= autocvar_bot_ai_bunnyhop_skilloffset)
+ wpcost_update = true;
+
+ skill = autocvar_skill;
+ if (wpcost_update)
+ waypoint_updatecost_foralllinks();
+ }
bot_calculate_stepheightvec();
bot_navigation_movemode = ((autocvar_bot_navigation_ignoreplayers) ? MOVE_NOMONSTERS : MOVE_NORMAL);
botframe_nextthink = time + 10;
}
- bot_ignore_bots = autocvar_bot_ignore_bots;
-
if(botframe_spawnedwaypoints)
{
if(autocvar_waypoint_benchmark)
{
botframe_spawnedwaypoints = true;
waypoint_loadall();
- if(!waypoint_load_links())
- waypoint_schedulerelinkall();
+ waypoint_load_links();
}
if (bot_list)
// frame, which causes choppy framerates)
if (bot_strategytoken_taken)
{
+ // give goal token to the first bot without goals; if all bots don't have
+ // any goal (or are dead/frozen) simply give it to the next one
bot_strategytoken_taken = false;
- if (bot_strategytoken)
- bot_strategytoken = bot_strategytoken.nextbot;
- if (!bot_strategytoken)
- bot_strategytoken = bot_list;
+ entity bot_strategytoken_save = bot_strategytoken;
+ while (true)
+ {
+ if (bot_strategytoken)
+ bot_strategytoken = bot_strategytoken.nextbot;
+ if (!bot_strategytoken)
+ bot_strategytoken = bot_list;
+
+ if (!(IS_DEAD(bot_strategytoken) || STAT(FROZEN, bot_strategytoken))
+ && !bot_strategytoken.goalcurrent)
+ break;
+
+ if (!bot_strategytoken_save) // break loop if all the bots are dead or frozen
+ break;
+ if (bot_strategytoken == bot_strategytoken_save)
+ bot_strategytoken_save = NULL; // looped through all the bots
+ }
}
if (botframe_nextdangertime < time)