]> de.git.xonotic.org Git - xonotic/xonotic-data.pk3dir.git/blobdiff - qcsrc/server/bot/bot.qc
Merge branch 'master' into terencehill/ca_arena_freezetag_bugfixes
[xonotic/xonotic-data.pk3dir.git] / qcsrc / server / bot / bot.qc
index 44ece52bfd9c66cd2caf0d67af3ce2c4c0256d4e..45107b833a1b16221b8082da7c55c143d7b9244b 100644 (file)
@@ -12,7 +12,7 @@
 
 entity bot_spawn()
 {
-       local entity oldself, bot;
+       entity oldself, bot;
        bot = spawnclient();
        if (bot)
        {
@@ -25,7 +25,7 @@ entity bot_spawn()
                self = oldself;
        }
        return bot;
-};
+}
 
 void bot_think()
 {
@@ -33,19 +33,19 @@ void bot_think()
                return;
 
        self.flags &~= FL_GODMODE;
-       if(cvar("bot_god"))
+       if(autocvar_bot_god)
                self.flags |= FL_GODMODE;
 
-       self.bot_nextthink = self.bot_nextthink + cvar("bot_ai_thinkinterval") * pow(0.5, self.bot_aiskill);
+       self.bot_nextthink = self.bot_nextthink + autocvar_bot_ai_thinkinterval * pow(0.5, self.bot_aiskill);
        //if (self.bot_painintensity > 0)
        //      self.bot_painintensity = self.bot_painintensity - (skill + 1) * 40 * frametime;
 
        //self.bot_painintensity = self.bot_painintensity + self.bot_oldhealth - self.health;
        //self.bot_painintensity = bound(0, self.bot_painintensity, 100);
 
-       if(time < game_starttime || ((cvar("g_campaign") && !campaign_bots_may_start)))
+       if (autocvar_g_campaign && !campaign_bots_may_start)
        {
-               self.nextthink = time + 0.5;
+               self.bot_nextthink = time + 0.5;
                return;
        }
 
@@ -81,6 +81,14 @@ void bot_think()
        self.BUTTON_CHAT = 0;
        self.BUTTON_USE = 0;
 
+       if (time < game_starttime)
+       {
+               // block the bot during the countdown to game start
+               self.movement = '0 0 0';
+               self.bot_nextthink = game_starttime;
+               return;
+       }
+
        // if dead, just wait until we can respawn
        if (self.deadflag)
        {
@@ -90,10 +98,12 @@ void bot_think()
                        self.bot_strategytime = 0;
                }
        }
+       else if(self.aistatus & AI_STATUS_STUCK)
+               navigation_unstuck();
 
        // now call the current bot AI (havocbot for example)
        self.bot_ai();
-};
+}
 
 void bot_setnameandstuff()
 {
@@ -104,39 +114,42 @@ void bot_setnameandstuff()
        string bot_name, bot_model, bot_skin, bot_shirt, bot_pants;
        string name, prefix, suffix;
 
-       if(cvar("g_campaign"))
+       if(autocvar_g_campaign)
        {
                prefix = "";
                suffix = "";
        }
        else
        {
-               prefix = cvar_string("bot_prefix");
-               suffix = cvar_string("bot_suffix");
+               prefix = autocvar_bot_prefix;
+               suffix = autocvar_bot_suffix;
        }
 
-       file = fopen(cvar_string("bot_config_file"), FILE_READ);
+       file = fopen(autocvar_bot_config_file, FILE_READ);
 
        if(file < 0)
-               print(strcat("Error: Can not open the bot configuration file '",cvar_string("bot_config_file"),"'\n"));
+       {
+               print(strcat("Error: Can not open the bot configuration file '",autocvar_bot_config_file,"'\n"));
+               readfile = "";
+       }
        else
        {
                RandomSelection_Init();
-               for(;;)
+               while((readfile = fgets(file)))
                {
-                       readfile = fgets(file);
-                       if(!readfile)
-                               break;
                        if(substring(readfile, 0, 2) == "//")
                                continue;
                        if(substring(readfile, 0, 1) == "#")
                                continue;
                        tokens = tokenizebyseparator(readfile, "\t");
+                       if(tokens == 0)
+                               continue;
                        s = argv(0);
                        prio = 1;
                        FOR_EACH_CLIENT(p)
                        {
-                               if(strcat(prefix, s, suffix) == p.netname)
+                               if(clienttype(p) == CLIENTTYPE_BOT)
+                               if(s == p.cleanname)
                                {
                                        prio = 0;
                                        break;
@@ -168,7 +181,7 @@ void bot_setnameandstuff()
 
        prio = 6;
 
-       #define READSKILL(f,w,r) if(argv(prio) != "") self.f = stof(argv(prio)) * (w); else self.f = (!cvar("g_campaign")) * (2 * random() - 1) * (r) * (w); ++prio
+       #define READSKILL(f,w,r) if(argv(prio) != "") self.f = stof(argv(prio)) * (w); else self.f = (!autocvar_g_campaign) * (2 * random() - 1) * (r) * (w); ++prio
        //print(bot_name, ": ping=", argv(9), "\n");
 
        READSKILL(havocbot_keyboardskill, 0.5, 0.5); // keyboard skill
@@ -195,13 +208,11 @@ void bot_setnameandstuff()
        self.bot_preferredcolors = self.clientcolors;
 
        // pick the name
-       if (cvar("bot_usemodelnames"))
+       if (autocvar_bot_usemodelnames)
                name = bot_model;
        else
                name = bot_name;
 
-       self.cleanname = strzone(name);
-
        // number bots with identical names
        float i;
        i = 0;
@@ -212,7 +223,11 @@ void bot_setnameandstuff()
                                ++i;
        }
        if (i)
-               name = strcat(name, "(", ftos(i), ")");
+               self.netname = self.netname_freeme = strzone(strcat(prefix, name, "(", ftos(i), ")", suffix));
+       else
+               self.netname = self.netname_freeme = strzone(strcat(prefix, name, suffix));
+
+       self.cleanname = strzone(name);
 
        // pick the model and skin
        if(substring(bot_model, -4, 1) != ".")
@@ -220,27 +235,25 @@ void bot_setnameandstuff()
        self.playermodel = self.playermodel_freeme = strzone(strcat("models/player/", bot_model));
        self.playerskin = self.playerskin_freeme = strzone(bot_skin);
 
-       self.netname = self.netname_freeme = strzone(strcat(prefix, name, suffix));
-
        self.cvar_cl_accuracy_data_share = 1;  // share the bots weapon accuracy data with the world
        self.cvar_cl_accuracy_data_receive = 0;  // don't receive any weapon accuracy data
-};
+}
 
 void bot_custom_weapon_priority_setup()
 {
-       local float tokens, i, c, w;
+       float tokens, i, c, w;
 
        bot_custom_weapon = FALSE;
 
-       if(     cvar_string("bot_ai_custom_weapon_priority_far") == "" ||
-               cvar_string("bot_ai_custom_weapon_priority_mid") == "" ||
-               cvar_string("bot_ai_custom_weapon_priority_close") == "" ||
-               cvar_string("bot_ai_custom_weapon_priority_distances") == ""
+       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 == ""
        )
                return;
 
        // Parse distances
-       tokens = tokenizebyseparator(cvar_string("bot_ai_custom_weapon_priority_distances")," ");
+       tokens = tokenizebyseparator(autocvar_bot_ai_custom_weapon_priority_distances," ");
 
        if (tokens!=2)
                return;
@@ -259,7 +272,7 @@ void bot_custom_weapon_priority_setup()
        bot_weapons_close[0] = -1;
 
        // Parse far distance weapon priorities
-       tokens = tokenizebyseparator(W_NumberWeaponOrder(cvar_string("bot_ai_custom_weapon_priority_far"))," ");
+       tokens = tokenizebyseparator(W_NumberWeaponOrder(autocvar_bot_ai_custom_weapon_priority_far)," ");
 
        c = 0;
        for(i=0; i < tokens && c < WEP_COUNT; ++i){
@@ -273,7 +286,7 @@ void bot_custom_weapon_priority_setup()
                bot_weapons_far[c] = -1;
 
        // Parse mid distance weapon priorities
-       tokens = tokenizebyseparator(W_NumberWeaponOrder(cvar_string("bot_ai_custom_weapon_priority_mid"))," ");
+       tokens = tokenizebyseparator(W_NumberWeaponOrder(autocvar_bot_ai_custom_weapon_priority_mid)," ");
 
        c = 0;
        for(i=0; i < tokens && c < WEP_COUNT; ++i){
@@ -287,7 +300,7 @@ void bot_custom_weapon_priority_setup()
                bot_weapons_mid[c] = -1;
 
        // Parse close distance weapon priorities
-       tokens = tokenizebyseparator(W_NumberWeaponOrder(cvar_string("bot_ai_custom_weapon_priority_close"))," ");
+       tokens = tokenizebyseparator(W_NumberWeaponOrder(autocvar_bot_ai_custom_weapon_priority_close)," ");
 
        c = 0;
        for(i=0; i < tokens && i < WEP_COUNT; ++i){
@@ -301,11 +314,11 @@ void bot_custom_weapon_priority_setup()
                bot_weapons_close[c] = -1;
 
        bot_custom_weapon = TRUE;
-};
+}
 
 void bot_endgame()
 {
-       local entity e;
+       entity e;
        //dprint("bot_endgame\n");
        e = bot_list;
        while (e)
@@ -314,12 +327,12 @@ void bot_endgame()
                e = e.nextbot;
        }
        // if dynamic waypoints are ever implemented, save them here
-};
+}
 
 void bot_relinkplayerlist()
 {
-       local entity e;
-       local entity prevbot;
+       entity e;
+       entity prevbot;
        player_count = 0;
        currentbots = 0;
        player_list = e = findchainflags(flags, FL_CLIENT);
@@ -346,12 +359,13 @@ void bot_relinkplayerlist()
        dprint(strcat("relink: ", ftos(currentbots), " bots seen.\n"));
        bot_strategytoken = bot_list;
        bot_strategytoken_taken = TRUE;
-};
+}
 
 void bot_clientdisconnect()
 {
        if (clienttype(self) != CLIENTTYPE_BOT)
                return;
+       bot_clearqueue(self);
        if(self.cleanname)
                strunzone(self.cleanname);
        if(self.netname_freeme)
@@ -364,7 +378,10 @@ void bot_clientdisconnect()
        self.netname_freeme = string_null;
        self.playermodel_freeme = string_null;
        self.playerskin_freeme = string_null;
-       remove(self.bot_cmd_current);
+       if(self.bot_cmd_current)
+               remove(self.bot_cmd_current);
+       if(bot_waypoint_queue_owner==self)
+               bot_waypoint_queue_owner = world;
 }
 
 void bot_clientconnect()
@@ -375,7 +392,7 @@ void bot_clientconnect()
        self.bot_nextthink = time - random();
        self.lag_func = bot_lagfunc;
        self.isbot = TRUE;
-       self.createdtime = self.nextthink;
+       self.createdtime = self.bot_nextthink;
 
        if(!self.bot_config_loaded) // This is needed so team overrider doesn't break between matches
                bot_setnameandstuff();
@@ -392,12 +409,12 @@ void bot_clientconnect()
                JoinBestTeam(self, FALSE, TRUE);
 
        havocbot_setupbot();
-};
+}
 
 void bot_removefromlargestteam()
 {
-       local float besttime, bestcount, thiscount;
-       local entity best, head;
+       float besttime, bestcount, thiscount;
+       entity best, head;
        CheckAllowedTeams(world);
        GetTeamCounts(world);
        head = findchainfloat(isbot, TRUE);
@@ -433,14 +450,14 @@ void bot_removefromlargestteam()
        }
        currentbots = currentbots - 1;
        dropclient(best);
-};
+}
 
 void bot_removenewest()
 {
-       local float besttime;
-       local entity best, head;
+       float besttime;
+       entity best, head;
 
-       if(teams_matter)
+       if(teamplay)
        {
                bot_removefromlargestteam();
                return;
@@ -462,7 +479,7 @@ void bot_removenewest()
        }
        currentbots = currentbots - 1;
        dropclient(best);
-};
+}
 
 void autoskill(float factor)
 {
@@ -489,20 +506,20 @@ void autoskill(float factor)
        }
        else if(bestbot <= bestplayer * factor - 2)
        {
-               if(cvar("skill") < 17)
+               if(autocvar_skill < 17)
                {
                        dprint("2 frags difference, increasing skill\n");
-                       cvar_set("skill", ftos(cvar("skill") + 1));
-                       bprint("^2SKILL UP!^7 Now at level ", ftos(cvar("skill")), "\n");
+                       cvar_set("skill", ftos(autocvar_skill + 1));
+                       bprint("^2SKILL UP!^7 Now at level ", ftos(autocvar_skill), "\n");
                }
        }
        else if(bestbot >= bestplayer * factor + 2)
        {
-               if(cvar("skill") > 0)
+               if(autocvar_skill > 0)
                {
                        dprint("2 frags difference, decreasing skill\n");
-                       cvar_set("skill", ftos(cvar("skill") - 1));
-                       bprint("^1SKILL DOWN!^7 Now at level ", ftos(cvar("skill")), "\n");
+                       cvar_set("skill", ftos(autocvar_skill - 1));
+                       bprint("^1SKILL DOWN!^7 Now at level ", ftos(autocvar_skill), "\n");
                }
        }
        else
@@ -516,35 +533,25 @@ void autoskill(float factor)
                head.totalfrags_lastcheck = head.totalfrags;
 }
 
-void bot_serverframe()
+void bot_calculate_stepheightvec(void)
 {
-       float realplayers, bots, activerealplayers;
-       entity head;
-
-       if (intermission_running)
-               return;
-
-       if (time < 2)
-               return;
-
-       stepheightvec = cvar("sv_stepheight") * '0 0 1';
-       bot_navigation_movemode = ((cvar("bot_navigation_ignoreplayers")) ? MOVE_NOMONSTERS : MOVE_NORMAL);
+       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
+}
 
-       if(time > autoskill_nextthink)
-       {
-               float a;
-               a = cvar("skill_auto");
-               if(a)
-                       autoskill(a);
-               autoskill_nextthink = time + 5;
-       }
+float bot_fixcount()
+{
+       entity head;
+       float realplayers, bots, activerealplayers;
 
        activerealplayers = 0;
        realplayers = 0;
 
        FOR_EACH_REALCLIENT(head)
        {
-               if(head.classname == "player" || g_lms || g_arena)
+               if(head.classname == "player" || g_lms || g_arena || g_ca)
                        ++activerealplayers;
                ++realplayers;
        }
@@ -554,17 +561,16 @@ void bot_serverframe()
        // But don't remove bots immediately on level change, as the real players
        // usually haven't rejoined yet
        bots_would_leave = FALSE;
-       if ((realplayers || cvar("bot_join_empty") || (currentbots > 0 && time < 5)))
+       if (teamplay && autocvar_bot_vs_human && (c3==-1 && c4==-1))
+               bots = min(ceil(fabs(autocvar_bot_vs_human) * activerealplayers), maxclients - realplayers);
+       else if ((realplayers || autocvar_bot_join_empty || (currentbots > 0 && time < 5)))
        {
                float realminplayers, minplayers;
-               realminplayers = cvar("minplayers");
+               realminplayers = autocvar_minplayers;
                minplayers = max(0, floor(realminplayers));
 
                float realminbots, minbots;
-               if(teamplay && cvar("bot_vs_human"))
-                       realminbots = ceil(fabs(cvar("bot_vs_human")) * activerealplayers);
-               else
-                       realminbots = cvar("bot_number");
+               realminbots = autocvar_bot_number;
                minbots = max(0, floor(realminbots));
 
                bots = min(max(minbots, minplayers - activerealplayers), maxclients - realplayers);
@@ -577,8 +583,6 @@ void bot_serverframe()
                bots = 0;
        }
 
-       bot_ignore_bots = cvar("bot_ignore_bots");
-
        // only add one bot per frame to avoid utter chaos
        if(time > botframe_nextthink)
        {
@@ -588,21 +592,51 @@ void bot_serverframe()
                        if (bot_spawn() == world)
                        {
                                bprint("Can not add bot, server full.\n");
-                               botframe_nextthink = time + 10;
-                               break;
+                               return FALSE;
                        }
                }
                while (currentbots > bots)
                        bot_removenewest();
        }
 
+       return TRUE;
+}
+
+void bot_serverframe()
+{
+       if (intermission_running)
+               return;
+
+       if (time < 2)
+               return;
+
+       bot_calculate_stepheightvec();
+       bot_navigation_movemode = ((autocvar_bot_navigation_ignoreplayers) ? MOVE_NOMONSTERS : MOVE_NORMAL);
+
+       if(time > autoskill_nextthink)
+       {
+               float a;
+               a = autocvar_skill_auto;
+               if(a)
+                       autoskill(a);
+               autoskill_nextthink = time + 5;
+       }
+
+       if(time > botframe_nextthink)
+       {
+               if(!bot_fixcount())
+                       botframe_nextthink = time + 10;
+       }
+
+       bot_ignore_bots = autocvar_bot_ignore_bots;
+
        if(botframe_spawnedwaypoints)
        {
-               if(cvar("waypoint_benchmark"))
+               if(autocvar_waypoint_benchmark)
                        localcmd("quit\n");
        }
 
-       if (currentbots > 0 || cvar("g_waypointeditor"))
+       if (currentbots > 0 || autocvar_g_waypointeditor || autocvar_g_waypointeditor_auto)
        if (botframe_spawnedwaypoints)
        {
                if(botframe_cachedwaypointlinks)
@@ -613,7 +647,7 @@ void bot_serverframe()
                else
                {
                        // TODO: Make this check cleaner
-                       local entity wp = findchain(classname, "waypoint");
+                       entity wp = findchain(classname, "waypoint");
                        if(time - wp.nextthink > 10)
                                waypoint_save_links();
                }
@@ -642,22 +676,25 @@ void bot_serverframe()
 
                if (botframe_nextdangertime < time)
                {
-                       local float interval;
-                       interval = cvar("bot_ai_dangerdetectioninterval");
+                       float interval;
+                       interval = autocvar_bot_ai_dangerdetectioninterval;
                        if (botframe_nextdangertime < time - interval * 1.5)
                                botframe_nextdangertime = time;
                        botframe_nextdangertime = botframe_nextdangertime + interval;
-                       botframe_updatedangerousobjects(cvar("bot_ai_dangerdetectionupdates"));
+                       botframe_updatedangerousobjects(autocvar_bot_ai_dangerdetectionupdates);
                }
        }
 
-       if (cvar("g_waypointeditor"))
+       if (autocvar_g_waypointeditor)
                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;
        }
-};
+}