]> git.xonotic.org Git - xonotic/xonotic-data.pk3dir.git/blobdiff - qcsrc/server/bot/default/bot.qc
Improve end of warmup countdown abort (when player count drops too low)
[xonotic/xonotic-data.pk3dir.git] / qcsrc / server / bot / default / bot.qc
index 9ba2a333b3d57ccfc5b893af9fe59232052211c4..d7cf429d74e150a8324c0c30bfe0245d683658f1 100644 (file)
@@ -28,6 +28,7 @@
 #include <server/scores_rules.qh>
 #include <server/teamplay.qh>
 #include <server/weapons/accuracy.qh>
+#include <server/weapons/selection.qh>
 #include <server/world.qh>
 
 STATIC_INIT(bot) { bot_calculate_stepheightvec(); }
@@ -63,10 +64,16 @@ void bot_think(entity this)
        if(autocvar_bot_god)
                this.flags |= FL_GODMODE;
 
-       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));
+       this.bot_nextthink = max(time, this.bot_nextthink) + max(0.01, autocvar_bot_ai_thinkinterval * min(14 / (skill + this.bot_aiskill + 14), 1));
 
        if (!IS_PLAYER(this) || (autocvar_g_campaign && !campaign_bots_may_start))
        {
+               if (IS_PLAYER(this))
+               {
+                       .entity weaponentity = weaponentities[0];
+                       if(this.(weaponentity).m_weapon == WEP_Null)
+                               W_NextWeapon(this, 0, weaponentity);
+               }
                CS(this).movement = '0 0 0';
                this.bot_nextthink = time + 0.5;
                return;
@@ -106,23 +113,33 @@ void bot_think(entity this)
 
        if (time < game_starttime)
        {
+               .entity weaponentity = weaponentities[0];
+               if(this.(weaponentity).m_weapon == WEP_Null)
+                       W_NextWeapon(this, 0, weaponentity);
                // block the bot during the countdown to game start
                CS(this).movement = '0 0 0';
-               this.bot_nextthink = game_starttime;
                return;
        }
 
        // if dead, just wait until we can respawn
-       if (IS_DEAD(this))
+       if (IS_DEAD(this) || IS_OBSERVER(this))
        {
                if (bot_waypoint_queue_owner == this)
                        bot_waypoint_queue_owner = NULL;
                this.aistatus = 0;
                CS(this).movement = '0 0 0';
-               if (this.deadflag == DEAD_DEAD)
+               if (IS_OBSERVER(this))
+                       return;
+               if (IS_DEAD(this))
                {
-                       PHYS_INPUT_BUTTON_JUMP(this) = true; // press jump to respawn
-                       navigation_goalrating_timeout_force(this);
+                       if (!navigation_goalrating_timeout(this))
+                               navigation_goalrating_timeout_force(this);
+                       // jump must not be pressed for at least one frame in order for
+                       // PlayerThink to detect the key down event
+                       if (this.deadflag == DEAD_DYING)
+                               PHYS_INPUT_BUTTON_JUMP(this) = false;
+                       else if (this.deadflag == DEAD_DEAD)
+                               PHYS_INPUT_BUTTON_JUMP(this) = true; // press jump to respawn
                }
        }
        else if(this.aistatus & AI_STATUS_STUCK)
@@ -152,7 +169,7 @@ void bot_setnameandstuff(entity this)
                int smallest_count = -1;
                if (teamplay)
                {
-                       for (int i = 1; i <= AvailableTeams(); ++i)
+                       for (int i = 1; i <= AVAILABLE_TEAMS; ++i)
                        {
                                // NOTE if (autocvar_g_campaign && autocvar_g_campaign_forceteam == i)
                                // TeamBalance_GetNumberOfPlayers(balance, i); returns the number of players + 1
@@ -192,7 +209,7 @@ void bot_setnameandstuff(entity this)
                        });
                        if (!conflict)
                                prio += 1;
-                       if (teamplay && !(autocvar_bot_vs_human && AvailableTeams() == 2))
+                       if (teamplay && !(autocvar_bot_vs_human && AVAILABLE_TEAMS == 2))
                        {
                                int forced_team = stof(argv(5));
                                if (!Team_IsValidIndex(forced_team))
@@ -224,8 +241,12 @@ void bot_setnameandstuff(entity this)
        if(argv(4) != "" && stof(argv(4)) >= 0) bot_pants = argv(4);
        else bot_pants = ftos(floor(random() * 15));
 
-       if (teamplay && !(autocvar_bot_vs_human && AvailableTeams() == 2))
+       if (teamplay && !(autocvar_bot_vs_human && AVAILABLE_TEAMS == 2))
+       {
                this.bot_forced_team = stof(argv(5));
+               if (!Team_IsValidIndex(this.bot_forced_team))
+                       this.bot_forced_team = 0;
+       }
        else
                this.bot_forced_team = 0;
 
@@ -383,17 +404,19 @@ void bot_relinkplayerlist()
 
                if(IS_BOT_CLIENT(it))
                {
-                       if(prevbot)
-                               prevbot.nextbot = it;
-                       else
-                               bot_list = it;
-                       prevbot = it;
+                       if (!IS_OBSERVER(it) && !bot_ispaused(it))
+                       {
+                               if(prevbot)
+                                       prevbot.nextbot = it;
+                               else
+                                       bot_list = it;
+                               prevbot = it;
+                       }
                        ++currentbots;
                }
        });
        if(prevbot)
                prevbot.nextbot = NULL;
-       LOG_TRACE("relink: ", ftos(currentbots), " bots seen.");
        bot_strategytoken = bot_list;
        bot_strategytoken_taken = true;
 }
@@ -418,7 +441,6 @@ void bot_clientconnect(entity this)
        if (!IS_BOT_CLIENT(this)) return;
        this.bot_preferredcolors = this.clientcolors;
        this.bot_nextthink = time - random();
-       this.lag_func = bot_lagfunc;
        this.isbot = true;
        this.createdtime = this.bot_nextthink;
 
@@ -428,16 +450,6 @@ void bot_clientconnect(entity this)
                bot_setclientfields(this);
        }
 
-       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);
 }
 
@@ -526,11 +538,8 @@ void bot_removenewest()
 
 void autoskill(float factor)
 {
-       float bestbot;
-       float bestplayer;
-
-       bestbot = -1;
-       bestplayer = -1;
+       int bestbot = -1;
+       int bestplayer = -1;
        FOREACH_CLIENT(IS_PLAYER(it), {
                if(IS_REAL_CLIENT(it))
                        bestplayer = max(bestplayer, it.totalfrags - it.totalfrags_lastcheck);
@@ -538,37 +547,37 @@ void autoskill(float factor)
                        bestbot = max(bestbot, it.totalfrags - it.totalfrags_lastcheck);
        });
 
-       LOG_DEBUG("autoskill: best player got ", ftos(bestplayer), ", ");
-       LOG_DEBUG("best bot got ", ftos(bestbot), "; ");
+       string msg = strcat("autoskill: best player got ", ftos(bestplayer), ", ""best bot got ", ftos(bestbot), "; ");
        if(bestbot < 0 || bestplayer < 0)
        {
-               LOG_DEBUG("not doing anything");
+               msg = strcat(msg, "not doing anything");
                // don't return, let it reset all counters below
        }
        else if(bestbot <= bestplayer * factor - 2)
        {
                if(autocvar_skill < 17)
                {
-                       LOG_DEBUG("2 frags difference, increasing skill");
+                       msg = strcat(msg, "2 frags difference, increasing skill");
                        cvar_set("skill", ftos(autocvar_skill + 1));
-                       bprint("^2SKILL UP!^7 Now at level ", ftos(autocvar_skill), "\n");
+                       bprint("^2BOT SKILL UP!^7 Now at level ", ftos(autocvar_skill), "\n");
                }
        }
        else if(bestbot >= bestplayer * factor + 2)
        {
                if(autocvar_skill > 0)
                {
-                       LOG_DEBUG("2 frags difference, decreasing skill");
+                       msg = strcat(msg, "2 frags difference, decreasing skill");
                        cvar_set("skill", ftos(autocvar_skill - 1));
-                       bprint("^1SKILL DOWN!^7 Now at level ", ftos(autocvar_skill), "\n");
+                       bprint("^1BOT SKILL DOWN!^7 Now at level ", ftos(autocvar_skill), "\n");
                }
        }
        else
        {
-               LOG_DEBUG("not doing anything");
+               msg = strcat(msg, "not doing anything");
                return;
                // don't reset counters, wait for them to accumulate
        }
+       LOG_DEBUG(msg);
 
        FOREACH_CLIENT(IS_PLAYER(it), { it.totalfrags_lastcheck = it.totalfrags; });
 }
@@ -581,7 +590,7 @@ void bot_calculate_stepheightvec()
        jumpheight_time = autocvar_sv_jumpvelocity / autocvar_sv_gravity;
 }
 
-bool bot_fixcount()
+bool bot_fixcount(bool multiple_per_frame)
 {
        int activerealplayers = 0;
        int realplayers = 0;
@@ -600,13 +609,13 @@ bool bot_fixcount()
        // But don't remove bots immediately on level change, as the real players
        // usually haven't rejoined yet
        bots_would_leave = false;
-       if (teamplay && autocvar_bot_vs_human && AvailableTeams() == 2)
+       if (autocvar_bot_vs_human && AVAILABLE_TEAMS == 2)
                bots = min(ceil(fabs(autocvar_bot_vs_human) * activerealplayers), maxclients - realplayers);
        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());
+                       minplayers = max(0, floor(autocvar_minplayers_per_team) * AVAILABLE_TEAMS);
                int minbots = max(0, floor(autocvar_bot_number));
 
                // add bots to reach minplayers if needed
@@ -629,13 +638,17 @@ bool bot_fixcount()
        // only add one bot per frame to avoid utter chaos
        if(time > botframe_nextthink)
        {
-               if (currentbots < bots)
+               while (currentbots < bots)
                {
                        if (bot_spawn() == NULL)
                        {
                                bprint("Can not add bot, server full.\n");
                                return false;
                        }
+                       if (!multiple_per_frame)
+                       {
+                               break;
+                       }
                }
                while (currentbots > bots && bots >= 0)
                        bot_removenewest();
@@ -644,39 +657,6 @@ bool bot_fixcount()
        return true;
 }
 
-void bot_remove_from_bot_list(entity this)
-{
-       entity e = bot_list;
-       entity prev_bot = NULL;
-       while (e)
-       {
-               if(e == this)
-               {
-                       if(!prev_bot)
-                               bot_list = this.nextbot;
-                       else
-                               prev_bot.nextbot = this.nextbot;
-                       if(bot_strategytoken == this)
-                       {
-                               bot_strategytoken = this.nextbot;
-                               bot_strategytoken_taken = true;
-                       }
-                       this.nextbot = NULL;
-                       break;
-               }
-               prev_bot = e;
-               e = e.nextbot;
-       }
-}
-
-void bot_clear(entity this)
-{
-       bot_remove_from_bot_list(this);
-       if(bot_waypoint_queue_owner == this)
-               bot_waypoint_queue_owner = NULL;
-       this.aistatus &= ~AI_STATUS_STUCK; // otherwise bot_waypoint_queue_owner will be set again to this by navigation_unstuck
-}
-
 void bot_serverframe()
 {
        if (intermission_running && currentbots > 0)
@@ -699,6 +679,7 @@ void bot_serverframe()
        // 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)
+       // A working workaround for this bug was implemented in commit fbd145044, see entcs_attach
        if (time < 2.5)
        {
                currentbots = -1;
@@ -739,7 +720,7 @@ void bot_serverframe()
 
        if(time > botframe_nextthink)
        {
-               if(!bot_fixcount())
+               if(!bot_fixcount(false))
                        botframe_nextthink = time + 10;
        }
 
@@ -749,7 +730,7 @@ void bot_serverframe()
                        localcmd("quit\n");
        }
 
-       if (currentbots > 0 || autocvar_g_waypointeditor || autocvar_g_waypointeditor_auto)
+       if (currentbots > 0 || waypointeditor_enabled || autocvar_g_waypointeditor_auto)
        if (botframe_spawnedwaypoints)
        {
                if(botframe_cachedwaypointlinks)
@@ -814,7 +795,7 @@ void bot_serverframe()
                }
        }
 
-       if (autocvar_g_waypointeditor)
+       if (waypointeditor_enabled)
                botframe_showwaypointlinks();
 
        if (autocvar_g_waypointeditor_auto)