]> de.git.xonotic.org Git - xonotic/xonotic-data.pk3dir.git/blobdiff - qcsrc/server/bot/havocbot/havocbot.qc
Merge branch 'master' into terencehill/arena_stuff
[xonotic/xonotic-data.pk3dir.git] / qcsrc / server / bot / havocbot / havocbot.qc
index 7d1ef3a08f4bd82a7071045ddbf54c5c0f8f39f9..1d9054952d24bbdde4d63981bc5a86689f3a44f4 100644 (file)
@@ -2,6 +2,8 @@
 #include "role_ctf.qc"
 #include "role_onslaught.qc"
 #include "role_keyhunt.qc"
+#include "role_freezetag.qc"
+#include "role_keepaway.qc"
 #include "roles.qc"
 
 void havocbot_ai()
@@ -118,7 +120,7 @@ void havocbot_ai()
 
                local vector now,v,next;//,heading;
                local float aimdistance,skillblend,distanceblend,blend;
-               next = now = self.goalcurrent.origin - (self.origin + self.view_ofs);
+               next = now = ( (self.goalcurrent.absmin + self.goalcurrent.absmax) * 0.5) - (self.origin + self.view_ofs);
                aimdistance = vlen(now);
                //heading = self.velocity;
                //dprint(self.goalstack01.classname,etos(self.goalstack01),"\n");
@@ -126,7 +128,7 @@ void havocbot_ai()
                        self.goalstack01 != self && self.goalstack01 != world && self.aistatus & AI_STATUS_RUNNING == 0 &&
                        !(self.goalcurrent.wpflags & WAYPOINTFLAG_TELEPORT)
                )
-                       next = self.goalstack01.origin - (self.origin + self.view_ofs);
+                       next = ((self.goalstack01.absmin + self.goalstack01.absmax) * 0.5) - (self.origin + self.view_ofs);
 
                skillblend=bound(0,(skill+self.bot_moveskill-2.5)*0.5,1); //lower skill player can't preturn
                distanceblend=bound(0,aimdistance/autocvar_bot_ai_keyboard_distance,1);
@@ -146,26 +148,26 @@ void havocbot_ai()
        }
        havocbot_movetogoal();
 
-       // if the bot is not attacking, go through all weapons that aren't fully loaded and reload them to keep them ready
+       // if the bot is not attacking, consider reloading weapons
        if not(self.aistatus & AI_STATUS_ATTACKING)
        {
                float i;
                entity e;
 
                // we are currently holding a weapon that's not fully loaded, reload it
-               if(self.clip_load < self.clip_size && self.weapon)
-               {
+               if(skill >= 2) // bots can only reload the held weapon on purpose past this skill
+               if(self.clip_load < self.clip_size)
                        self.impulse = 20; // "press" the reload button, not sure if this is done right
-                       self.clip_load = -1;
-               }
 
                // if we're not reloading a weapon, switch to any weapon in our invnetory that's not fully loaded to reload it next
-               if(self.clip_load >= 0)
+               // the code above executes next frame, starting the reloading then
+               if(skill >= 5) // bots can only look for unloaded weapons past this skill
+               if(self.clip_load >= 0) // only if we're not reloading a weapon already
                {
-                       for(i = WEP_FIRST; i <= WEP_LAST ; ++i)
+                       for(i = WEP_FIRST; i <= WEP_LAST; ++i)
                        {
                                e = get_weaponinfo(i);
-                               if(self.weapon_load[i] < cvar(strcat("g_balance_", e.netname, "_reload_ammo")))
+                               if ((e.spawnflags & WEP_FLAG_RELOADABLE) && (self.weapon_load[i] < cvar(strcat("g_balance_", e.netname, "_reload_ammo"))))
                                        self.switchweapon = i;
                        }
                }
@@ -252,6 +254,7 @@ void havocbot_bunnyhop(vector dir)
        local float bunnyhopdistance;
        local vector deviation;
        local float maxspeed;
+       vector gco, gno;
 
        if(autocvar_g_midair)
                return;
@@ -259,7 +262,7 @@ void havocbot_bunnyhop(vector dir)
        // Don't jump when using some weapons
        /*
        if(self.aistatus & AI_STATUS_ATTACKING)
-       if(self.weapon == WEP_SNIPERRIFLE)
+       if(self.weapon == WEP_RIFLE)
                return;
 
        if(self.goalcurrent.classname == "player")
@@ -289,7 +292,8 @@ void havocbot_bunnyhop(vector dir)
                self.bot_timelastseengoal = 0;
        }
 
-       bunnyhopdistance = vlen(self.origin - self.goalcurrent.origin);
+       gco = (self.goalcurrent.absmin + self.goalcurrent.absmax) * 0.5;
+       bunnyhopdistance = vlen(self.origin - gco);
 
        // Run only to visible goals
        if(self.flags & FL_ONGROUND)
@@ -322,18 +326,19 @@ void havocbot_bunnyhop(vector dir)
                                        if(self.aistatus & AI_STATUS_ROAMING)
                                        if(self.goalcurrent.classname=="waypoint")
                                        if not(self.goalcurrent.wpflags & WAYPOINTFLAG_PERSONAL)
-                                       if(fabs(self.goalcurrent.origin_z - self.origin_z) < self.maxs_z - self.mins_z)
+                                       if(fabs(gco_z - self.origin_z) < self.maxs_z - self.mins_z)
                                        if(self.goalstack01!=world)
                                        {
-                                               deviation = vectoangles(self.goalstack01.origin - self.origin) - vectoangles(self.goalcurrent.origin - self.origin);
+                                               gno = (self.goalstack01.absmin + self.goalstack01.absmax) * 0.5;
+                                               deviation = vectoangles(gno - self.origin) - vectoangles(gco - self.origin);
                                                while (deviation_y < -180) deviation_y = deviation_y + 360;
                                                while (deviation_y > 180) deviation_y = deviation_y - 360;
 
                                                if(fabs(deviation_y) < 20)
-                                               if(bunnyhopdistance < vlen(self.origin - self.goalstack01.origin))
-                                               if(fabs(self.goalstack01.origin_z - self.goalcurrent.origin_z) < self.maxs_z - self.mins_z)
+                                               if(bunnyhopdistance < vlen(self.origin - gno))
+                                               if(fabs(gno_z - gco_z) < self.maxs_z - self.mins_z)
                                                {
-                                                       if(vlen(self.goalcurrent.origin - self.goalstack01.origin) > autocvar_bot_ai_bunnyhop_startdistance)
+                                                       if(vlen(gco - gno) > autocvar_bot_ai_bunnyhop_startdistance)
                                                        if(checkpvs(self.origin + self.view_ofs, self.goalstack01))
                                                        {
                                                                checkdistance = FALSE;
@@ -403,6 +408,7 @@ void havocbot_movetogoal()
        local vector evadelava;
        local float s;
        local float maxspeed;
+       local vector gco;
        //local float dist;
        local vector dodge;
        //if (self.goalentity)
@@ -416,10 +422,11 @@ void havocbot_movetogoal()
        if(self.goalcurrent==self.navigation_jetpack_goal)
        if(self.ammo_fuel)
        {
-               #ifdef DEBUG_BOT_GOALSTACK
+               if(autocvar_bot_debug_goalstack)
+               {
                        debuggoalstack();
                        te_wizspike(self.navigation_jetpack_point);
-               #endif
+               }
 
                // Take off
                if not(self.aistatus & AI_STATUS_JETPACK_FLYING)
@@ -440,7 +447,7 @@ void havocbot_movetogoal()
                        float db, v, d;
                        vector dxy;
 
-                       dxy = self.origin - self.goalcurrent.origin; dxy_z = 0;
+                       dxy = self.origin - ( ( self.goalcurrent.absmin + self.goalcurrent.absmax ) * 0.5 ); dxy_z = 0;
                        d = vlen(dxy);
                        v = vlen(self.velocity -  self.velocity_z * '0 0 1');
                        db = (pow(v,2) / (autocvar_g_jetpack_acceleration_side * 2)) + 100;
@@ -498,7 +505,7 @@ void havocbot_movetogoal()
                                        if(distance>1000)
                                                continue;
 
-                                       traceline(self.origin + self.view_ofs , head.origin, TRUE, world);
+                                       traceline(self.origin + self.view_ofs , ( ( head.absmin + head.absmax ) * 0.5 ), TRUE, world);
 
                                        if(trace_fraction<1)
                                                continue;
@@ -568,7 +575,7 @@ void havocbot_movetogoal()
                        if(self.goalcurrent==world)
                                dir = v_forward;
                        else
-                               dir = normalize(self.goalcurrent.origin - self.origin);
+                               dir = normalize(( ( self.goalcurrent.absmin + self.goalcurrent.absmax ) * 0.5 ) - self.origin);
 
                        local vector xyvelocity = self.velocity; xyvelocity_z = 0;
                        local float xyspeed = xyvelocity * dir;
@@ -653,9 +660,9 @@ void havocbot_movetogoal()
                return;
        }
 
-#ifdef DEBUG_BOT_GOALSTACK
-       debuggoalstack();
-#endif
+
+       if(autocvar_bot_debug_goalstack)
+               debuggoalstack();
 
        m1 = self.goalcurrent.origin + self.goalcurrent.mins;
        m2 = self.goalcurrent.origin + self.goalcurrent.maxs;
@@ -668,6 +675,7 @@ void havocbot_movetogoal()
        dir = normalize(diff);
        flatdir = diff;flatdir_z = 0;
        flatdir = normalize(flatdir);
+       gco = (self.goalcurrent.absmin + self.goalcurrent.absmax) * 0.5;
 
        //if (self.bot_dodgevector_time < time)
        {
@@ -685,7 +693,7 @@ void havocbot_movetogoal()
                        }
                        else
                        {
-                               if(self.velocity_z >= 0 && !(self.watertype == CONTENT_WATER && self.goalcurrent.origin_z < self.origin_z) &&
+                               if(self.velocity_z >= 0 && !(self.watertype == CONTENT_WATER && gco_z < self.origin_z) &&
                                        ( !(self.waterlevel == WATERLEVEL_WETFEET && self.watertype == CONTENT_WATER) || self.aistatus & AI_STATUS_OUT_WATER))
                                        self.BUTTON_JUMP = TRUE;
                                else
@@ -706,12 +714,12 @@ void havocbot_movetogoal()
                        if (trace_plane_normal_z < 0.7)
                        {
                                s = trace_fraction;
-                               tracebox(self.origin + '0 0 16', self.mins, self.maxs, self.origin + self.velocity * 0.2 + '0 0 16', FALSE, self);
+                               tracebox(self.origin + stepheightvec, self.mins, self.maxs, self.origin + self.velocity * 0.2 + stepheightvec, FALSE, self);
                                if (trace_fraction < s + 0.01)
                                if (trace_plane_normal_z < 0.7)
                                {
                                        s = trace_fraction;
-                                       tracebox(self.origin + '0 0 48', self.mins, self.maxs, self.origin + self.velocity * 0.2 + '0 0 48', FALSE, self);
+                                       tracebox(self.origin + jumpstepheightvec, self.mins, self.maxs, self.origin + self.velocity * 0.2 + jumpstepheightvec, FALSE, self);
                                        if (trace_fraction > s)
                                                self.BUTTON_JUMP = 1;
                                }
@@ -781,7 +789,11 @@ void havocbot_movetogoal()
                                                {
                                                        // Remove dangerous dynamic goals from stack
                                                        if (self.goalcurrent.classname == "player" || self.goalcurrent.classname == "droppedweapon")
+                                                       {
                                                                navigation_poproute();
+                                                               if(self.goalcurrent)
+                                                                       gco = (self.goalcurrent.absmin + self.goalcurrent.absmax) * 0.5;
+                                                       }
                                                        // try to stop
                                                        flatdir = '0 0 0';
                                                        evadeobstacle = normalize(self.velocity) * -1;
@@ -802,7 +814,7 @@ void havocbot_movetogoal()
                dodge = havocbot_dodge();
                dodge = dodge * bound(0,0.5+(skill+self.bot_dodgeskill)*0.1,1);
                evadelava = evadelava * bound(1,3-(skill+self.bot_dodgeskill),3); //Noobs fear lava a lot and take more distance from it
-               traceline(self.origin, self.enemy.origin, TRUE, world);
+               traceline(self.origin, ( ( self.enemy.absmin + self.enemy.absmax ) * 0.5 ), TRUE, world);
                if(trace_ent.classname == "player")
                        dir = dir * bound(0,(skill+self.bot_dodgeskill)/7,1);
 
@@ -872,9 +884,9 @@ void havocbot_chooseenemy()
                        // and not really really far away
                        // and we're not severely injured
                        // then keep tracking for a half second into the future
-                       traceline(self.origin+self.view_ofs, self.enemy.origin+self.enemy.view_ofs*0.5,FALSE,world);
+                       traceline(self.origin+self.view_ofs, ( self.enemy.absmin + self.enemy.absmax ) * 0.5,FALSE,world);
                        if (trace_ent == self.enemy || trace_fraction == 1)
-                       if (vlen(self.enemy.origin - self.origin) < 1000)
+                       if (vlen((( self.enemy.absmin + self.enemy.absmax ) * 0.5) - self.origin) < 1000)
                        if (self.health > 30)
                        {
                                // remain tracking him for a shot while (case he went after a small corner or pilar
@@ -918,7 +930,7 @@ void havocbot_chooseenemy()
 
                // I want to do a second scan if no enemy was found or I don't have weapons
                // TODO: Perform the scan when using the rifle (requires changes on the rifle code)
-               if(best || self.weapons) // || self.weapon == WEP_SNIPERRIFLE
+               if(best || self.weapons) // || self.weapon == WEP_RIFLE
                        break;
                if(i)
                        break;
@@ -939,6 +951,31 @@ void havocbot_chooseenemy()
        self.havocbot_stickenemy = TRUE;
 };
 
+float havocbot_chooseweapon_checkreload(float new_weapon)
+{
+       // bots under this skill cannot find unloaded weapons to reload idly when not in combat,
+       // so skip this for them, or they'll never get to reload their weapons at all.
+       // this also allows bots under this skill to be more stupid, and reload more often during combat :)
+       if(skill < 5)
+               return FALSE;
+
+       // if this weapon is scheduled for reloading, don't switch to it during combat
+       if (self.weapon_load[new_weapon] < 0)
+       {
+               local float i, other_weapon_available;
+               for(i = WEP_FIRST; i <= WEP_LAST; ++i)
+               {
+                       // if we are out of ammo for all other weapons, it's an emergency to switch to anything else
+                       if (weapon_action(i, WR_CHECKAMMO1) + weapon_action(i, WR_CHECKAMMO2))
+                               other_weapon_available = TRUE;
+               }
+               if(other_weapon_available)
+                       return TRUE;
+       }
+
+       return FALSE;
+}
+
 void havocbot_chooseweapon()
 {
        local float i;
@@ -966,11 +1003,6 @@ void havocbot_chooseweapon()
                return;
        }
 
-       // Do not change weapon while reloading. If we do, the bot risks switching betwen empty weapons
-       // before getting to reload them, shooting none of them at all, giving lower overall performance
-       if(self.weapon && self.clip_load < 1)
-               return;
-
        // Do not change weapon during the next second after a combo
        i = time - self.lastcombotime;
        if(i < 1)
@@ -1008,8 +1040,9 @@ void havocbot_chooseweapon()
                if ( distance > bot_distance_far ) {
                        for(i=0; i < WEP_COUNT && bot_weapons_far[i] != -1 ; ++i){
                                w = bot_weapons_far[i];
-                               if ( client_hasweapon(self, w, TRUE, FALSE) ){
-                                       if ( self.weapon == w && combo)
+                               if ( client_hasweapon(self, w, TRUE, FALSE) )
+                               {
+                                       if ((self.weapon == w && combo) || havocbot_chooseweapon_checkreload(w))
                                                continue;
                                        self.switchweapon = w;
                                        return;
@@ -1021,8 +1054,9 @@ void havocbot_chooseweapon()
                if ( distance > bot_distance_close) {
                        for(i=0; i < WEP_COUNT && bot_weapons_mid[i] != -1 ; ++i){
                                w = bot_weapons_mid[i];
-                               if ( client_hasweapon(self, w, TRUE, FALSE) ){
-                                       if ( self.weapon == w && combo)
+                               if ( client_hasweapon(self, w, TRUE, FALSE) )
+                               {
+                                       if ((self.weapon == w && combo) || havocbot_chooseweapon_checkreload(w))
                                                continue;
                                        self.switchweapon = w;
                                        return;
@@ -1033,8 +1067,9 @@ void havocbot_chooseweapon()
                // Choose weapons for close distance
                for(i=0; i < WEP_COUNT && bot_weapons_close[i] != -1 ; ++i){
                        w = bot_weapons_close[i];
-                       if ( client_hasweapon(self, w, TRUE, FALSE) ){
-                               if ( self.weapon == w && combo)
+                       if ( client_hasweapon(self, w, TRUE, FALSE) )
+                       {
+                               if ((self.weapon == w && combo) || havocbot_chooseweapon_checkreload(w))
                                        continue;
                                self.switchweapon = w;
                                return;
@@ -1059,10 +1094,10 @@ void havocbot_aim()
                enemyvel = self.enemy.velocity;
                if (!self.enemy.waterlevel)
                        enemyvel_z = 0;
-               lag_additem(time + self.ping, 0, 0, self.enemy, self.origin, selfvel, self.enemy.origin, enemyvel);
+               lag_additem(time + self.ping, 0, 0, self.enemy, self.origin, selfvel, (self.enemy.absmin + self.enemy.absmax) * 0.5, enemyvel);
        }
        else
-               lag_additem(time + self.ping, 0, 0, world, self.origin, selfvel, self.goalcurrent.origin, '0 0 0');
+               lag_additem(time + self.ping, 0, 0, world, self.origin, selfvel, ( self.goalcurrent.absmin + self.goalcurrent.absmax ) * 0.5, '0 0 0');
 };
 
 float havocbot_moveto_refresh_route()
@@ -1116,12 +1151,11 @@ float havocbot_moveto(vector pos)
                        }
                }
 
-               #ifdef DEBUG_BOT_GOALSTACK
+               if(autocvar_bot_debug_goalstack)
                        debuggoalstack();
-               #endif
 
                // Heading
-               local vector dir = self.goalcurrent.origin - (self.origin + self.view_ofs);
+               local vector dir = ( ( self.goalcurrent.absmin + self.goalcurrent.absmax ) * 0.5 ) - (self.origin + self.view_ofs);
                dir_z = 0;
                bot_aimdir(dir, -1);