]> de.git.xonotic.org Git - xonotic/xonotic-data.pk3dir.git/blobdiff - qcsrc/server/bot/default/bot.qc
Merge branch 'master' into terencehill/bot_ai
[xonotic/xonotic-data.pk3dir.git] / qcsrc / server / bot / default / bot.qc
index f8ddd0c637576d81efb058e330c11922d9438a56..1f9b8625f4d6332474949d39d39b951ac85b158f 100644 (file)
@@ -21,7 +21,7 @@
 #include "../../race.qh"
 #include <common/t_items.qh>
 
-#include "../../mutators/_mod.qh"
+#include <server/mutators/_mod.qh>
 
 #include "../../weapons/accuracy.qh"
 
@@ -129,11 +129,14 @@ void bot_think(entity this)
        // if dead, just wait until we can respawn
        if (IS_DEAD(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)
                {
                        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)
@@ -396,18 +399,10 @@ void bot_clientdisconnect(entity this)
        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)
@@ -438,7 +433,7 @@ void bot_clientconnect(entity this)
        else if(this.bot_forced_team==4)
                this.team = NUM_TEAM_4;
        else
-               JoinBestTeam(this, false, true);
+               JoinBestTeam(this, true);
 
        havocbot_setupbot(this);
 }
@@ -579,9 +574,8 @@ void autoskill(float factor)
 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()
@@ -598,16 +592,6 @@ float bot_fixcount()
                        ++realplayers;
                });
        }
-       if(currentbots == -1)
-       {
-               currentbots = 0;
-               // human players joining early may cause weird issues (bots appearing on
-               // the scoreboard as spectators) when switching map with the gotomap
-               // command, as it doesn't remove bots of the previous match, and with
-               // minplayers > 1, so ignore human players in the first bot frame
-               // TODO maybe find a cleaner solution
-               activerealplayers = 0;
-       }
 
        int bots;
        // add/remove bots if needed to make sure there are at least
@@ -690,15 +674,52 @@ void bot_clear(entity this)
 
 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);
 
@@ -717,8 +738,6 @@ void bot_serverframe()
                        botframe_nextthink = time + 10;
        }
 
-       bot_ignore_bots = autocvar_bot_ignore_bots;
-
        if(botframe_spawnedwaypoints)
        {
                if(autocvar_waypoint_benchmark)
@@ -747,8 +766,7 @@ void bot_serverframe()
        {
                botframe_spawnedwaypoints = true;
                waypoint_loadall();
-               if(!waypoint_load_links())
-                       waypoint_schedulerelinkall();
+               waypoint_load_links();
        }
 
        if (bot_list)
@@ -758,11 +776,26 @@ void bot_serverframe()
                //  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)