]> git.xonotic.org Git - xonotic/xonotic-data.pk3dir.git/blobdiff - qcsrc/server/bot/default/bot.qc
Revert "Campaign: give bots a weapon on map start"
[xonotic/xonotic-data.pk3dir.git] / qcsrc / server / bot / default / bot.qc
index cb83896c22317169767e72ca953da029a0852e47..a42b0609a191ccf9e063de4d429807ccdbb2e59b 100644 (file)
@@ -1,53 +1,43 @@
 #include "bot.qh"
 
-#include "cvars.qh"
-
-#include "aim.qh"
-#include "navigation.qh"
-#include "scripting.qh"
-#include "waypoints.qh"
-
-#include "havocbot/havocbot.qh"
-#include "havocbot/scripting.qh"
-
-#include "../../teamplay.qh"
-
-#include "../../antilag.qh"
-#include "../../autocvars.qh"
-#include "../../campaign.qh"
-#include "../../client.qh"
-#include "../../constants.qh"
-#include "../../defs.qh"
-#include "../../race.qh"
-#include <common/t_items.qh>
-
-#include <server/mutators/_mod.qh>
-
-#include "../../weapons/accuracy.qh"
-
-#include <common/physics/player.qh>
 #include <common/constants.qh>
-#include <common/net_linked.qh>
 #include <common/mapinfo.qh>
+#include <common/net_linked.qh>
+#include <common/physics/player.qh>
+#include <common/stats.qh>
 #include <common/teams.qh>
 #include <common/util.qh>
-
-#include <server/scores_rules.qh>
-
 #include <common/weapons/_all.qh>
-
 #include <lib/csqcmodel/sv_model.qh>
-
 #include <lib/warpzone/common.qh>
 #include <lib/warpzone/util_server.qh>
+#include <server/antilag.qh>
+#include <server/bot/default/aim.qh>
+#include <server/bot/default/cvars.qh>
+#include <server/bot/default/havocbot/havocbot.qh>
+#include <server/bot/default/havocbot/scripting.qh>
+#include <server/bot/default/navigation.qh>
+#include <server/bot/default/scripting.qh>
+#include <server/bot/default/waypoints.qh>
+#include <server/campaign.qh>
+#include <server/client.qh>
+#include <server/damage.qh>
+#include <server/items/items.qh>
+#include <server/mutators/_mod.qh>
+#include <server/race.qh>
+#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(); }
 
 // TODO: remove this function! its only purpose is to update these fields since bot_setnameandstuff is called before ClientState
 void bot_setclientfields(entity this)
 {
-       CS(this).cvar_cl_accuracy_data_share = 1;  // share the bots weapon accuracy data with the world
-       CS(this).cvar_cl_accuracy_data_receive = 0;  // don't receive any weapon accuracy data
+       CS_CVAR(this).cvar_cl_accuracy_data_share = 1;  // share the bots weapon accuracy data with the world
+       CS_CVAR(this).cvar_cl_accuracy_data_receive = 0;  // don't receive any weapon accuracy data
 }
 
 entity bot_spawn()
@@ -104,7 +94,8 @@ void bot_think(entity this)
 
        // clear buttons
        PHYS_INPUT_BUTTON_ATCK(this) = false;
-       PHYS_INPUT_BUTTON_JUMP(this) = false;
+       // keep jump button pressed for a short while, useful with ramp jumps
+       PHYS_INPUT_BUTTON_JUMP(this) = (!IS_DEAD(this) && time < this.bot_jump_time + 0.2);
        PHYS_INPUT_BUTTON_ATCK2(this) = false;
        PHYS_INPUT_BUTTON_ZOOM(this) = false;
        PHYS_INPUT_BUTTON_CROUCH(this) = false;
@@ -116,6 +107,9 @@ 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;
@@ -123,16 +117,24 @@ void bot_think(entity this)
        }
 
        // 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)
@@ -145,21 +147,7 @@ void bot_think(entity this)
 void bot_setnameandstuff(entity this)
 {
        string readfile, s;
-       float file, tokens, prio;
-
-       string bot_name, bot_model, bot_skin, bot_shirt, bot_pants;
-       string name, prefix, suffix;
-
-       if(autocvar_g_campaign)
-       {
-               prefix = "";
-               suffix = "";
-       }
-       else
-       {
-               prefix = autocvar_bot_prefix;
-               suffix = autocvar_bot_suffix;
-       }
+       int file, tokens, prio;
 
        file = fopen(autocvar_bot_config_file, FILE_READ);
 
@@ -230,6 +218,8 @@ void bot_setnameandstuff(entity this)
                fclose(file);
        }
 
+       string bot_name, bot_model, bot_skin, bot_shirt, bot_pants;
+
        tokens = tokenizebyseparator(readfile, "\t");
        if(argv(0) != "") bot_name = argv(0);
        else bot_name = "Bot";
@@ -247,7 +237,11 @@ void bot_setnameandstuff(entity this)
        else bot_pants = ftos(floor(random() * 15));
 
        if (teamplay && !(autocvar_bot_vs_human && AvailableTeams() == 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;
 
@@ -288,13 +282,10 @@ void bot_setnameandstuff(entity this)
        setcolor(this, stof(bot_shirt) * 16 + stof(bot_pants));
        this.bot_preferredcolors = this.clientcolors;
 
-       // pick the name
-       if (autocvar_bot_usemodelnames)
-               name = bot_model;
-       else
-               name = bot_name;
+       string prefix = (autocvar_g_campaign ? "" : autocvar_bot_prefix);
+       string suffix = (autocvar_g_campaign ? "" : autocvar_bot_suffix);
+       string name = (autocvar_bot_usemodelnames ? bot_model : bot_name);
 
-       // number bots with identical names
        if (name == "")
        {
                name = ftos(etof(this));
@@ -302,6 +293,7 @@ void bot_setnameandstuff(entity this)
        }
        else
        {
+               // number bots with identical names
                int j = 0;
                FOREACH_CLIENT(IS_BOT_CLIENT(it), {
                        if(it.cleanname == name)
@@ -323,6 +315,10 @@ void bot_setnameandstuff(entity this)
 
 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;
@@ -330,70 +326,50 @@ void bot_custom_weapon_priority_setup()
        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)," ");
+       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 < 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
 
-       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;
+       PARSE_WEAPON_PRIORITIES(far);
+       PARSE_WEAPON_PRIORITIES(mid);
+       PARSE_WEAPON_PRIORITIES(close);
 
        bot_custom_weapon = true;
 }
@@ -423,17 +399,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;
 }
@@ -468,16 +446,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);
 }
 
@@ -566,11 +534,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);
@@ -578,37 +543,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; });
 }
@@ -621,7 +586,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;
@@ -669,13 +634,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();
@@ -684,39 +653,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)
@@ -739,6 +675,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;
@@ -779,7 +716,7 @@ void bot_serverframe()
 
        if(time > botframe_nextthink)
        {
-               if(!bot_fixcount())
+               if(!bot_fixcount(false))
                        botframe_nextthink = time + 10;
        }
 
@@ -789,7 +726,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)
@@ -854,16 +791,12 @@ void bot_serverframe()
                }
        }
 
-       if (autocvar_g_waypointeditor)
+       if (waypointeditor_enabled)
                botframe_showwaypointlinks();
 
        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();
 }