]> de.git.xonotic.org Git - xonotic/xonotic-data.pk3dir.git/blobdiff - qcsrc/server/bot/havocbot/havocbot.qc
Merge branch 'terencehill/menu_optimization' into 'master'
[xonotic/xonotic-data.pk3dir.git] / qcsrc / server / bot / havocbot / havocbot.qc
index fcf1c04d29cda938cd5f8e3454bf1fc2150a13e7..134e78401f9c2d5eba5ff53c8d366a36d9eb250f 100644 (file)
@@ -1,5 +1,4 @@
 #include "havocbot.qh"
-#include "../../_all.qh"
 
 #include "../aim.qh"
 #include "../bot.qh"
@@ -7,14 +6,17 @@
 #include "../scripting.qh"
 #include "../waypoints.qh"
 
-#include "../../../common/constants.qh"
+#include <common/constants.qh>
+#include <common/items/all.qh>
 
-#include "../../../common/triggers/trigger/jumppads.qh"
+#include <common/triggers/trigger/jumppads.qh>
 
-#include "../../../warpzonelib/common.qh"
+#include <lib/warpzone/common.qh>
+
+.float speed;
 
 void havocbot_ai()
-{
+{SELFPARAM();
        if(self.draggedby)
                return;
 
@@ -36,7 +38,7 @@ void havocbot_ai()
 
                // TODO: tracewalk() should take care of this job (better path finding under water)
                // if we don't have a goal and we're under water look for a waypoint near the "shore" and push it
-               if(self.deadflag != DEAD_NO)
+               if(IS_DEAD(self))
                if(self.goalcurrent==world)
                if(self.waterlevel==WATERLEVEL_SWIMMING || (self.aistatus & AI_STATUS_OUT_WATER))
                {
@@ -84,7 +86,7 @@ void havocbot_ai()
                bot_strategytoken_taken = true;
        }
 
-       if(self.deadflag != DEAD_NO)
+       if(IS_DEAD(self))
                return;
 
        havocbot_chooseenemy();
@@ -102,7 +104,8 @@ void havocbot_ai()
 
                if(self.weapons)
                {
-                       WEP_ACTION(self.weapon, WR_AIM);
+                       Weapon w = PS(self).m_weapon;
+                       w.wr_aim(w);
                        if (autocvar_bot_nofire || IS_INDEPENDENT_PLAYER(self))
                        {
                                self.BUTTON_ATCK = false;
@@ -111,7 +114,7 @@ void havocbot_ai()
                        else
                        {
                                if(self.BUTTON_ATCK||self.BUTTON_ATCK2)
-                                       self.lastfiredweapon = self.weapon;
+                                       self.lastfiredweapon = PS(self).m_weapon.m_id;
                        }
                }
                else
@@ -168,18 +171,16 @@ void havocbot_ai()
                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 (int i = WEP_FIRST; i <= WEP_LAST; ++i)
-                       {
-                               entity e = get_weaponinfo(i);
-                               if ((self.weapons & WepSet_FromWeapon(i)) && (e.spawnflags & WEP_FLAG_RELOADABLE) && (self.weapon_load[i] < e.reloading_ammo))
-                                       self.switchweapon = i;
-                       }
+                       FOREACH(Weapons, it != WEP_Null, LAMBDA(
+                               if((self.weapons & (it.m_wepset)) && (it.spawnflags & WEP_FLAG_RELOADABLE) && (self.weapon_load[it.m_id] < it.reloading_ammo))
+                                       PS(self).m_switchweapon = it;
+                       ));
                }
        }
 }
 
 void havocbot_keyboard_movement(vector destorg)
-{
+{SELFPARAM();
        vector keyboard;
        float blend, maxspeed;
        float sk;
@@ -254,7 +255,7 @@ void havocbot_keyboard_movement(vector destorg)
 }
 
 void havocbot_bunnyhop(vector dir)
-{
+{SELFPARAM();
        float bunnyhopdistance;
        vector deviation;
        float maxspeed;
@@ -294,7 +295,7 @@ void havocbot_bunnyhop(vector dir)
        bunnyhopdistance = vlen(self.origin - gco);
 
        // Run only to visible goals
-       if(self.flags & FL_ONGROUND)
+       if(IS_ONGROUND(self))
        if(self.speed==maxspeed)
        if(checkpvs(self.origin + self.view_ofs, self.goalcurrent))
        {
@@ -370,7 +371,7 @@ void havocbot_bunnyhop(vector dir)
 #if 0
        // Release jump button
        if(!cvar("sv_pogostick"))
-       if((self.flags & FL_ONGROUND) == 0)
+       if((IS_ONGROUND(self)) == 0)
        {
                if(self.velocity.z < 0 || vlen(self.velocity)<maxspeed)
                        self.BUTTON_JUMP = false;
@@ -397,7 +398,7 @@ void havocbot_bunnyhop(vector dir)
 }
 
 void havocbot_movetogoal()
-{
+{SELFPARAM();
        vector destorg;
        vector diff;
        vector dir;
@@ -477,7 +478,7 @@ void havocbot_movetogoal()
 
                // Flying
                self.BUTTON_HOOK = true;
-               if(self.navigation_jetpack_point.z - PL_MAX.z + PL_MIN.z < self.origin.z)
+               if(self.navigation_jetpack_point.z - STAT(PL_MAX, NULL).z + STAT(PL_MIN, NULL).z < self.origin.z)
                {
                        self.movement_x = dir * v_forward * maxspeed;
                        self.movement_y = dir * v_right * maxspeed;
@@ -537,7 +538,7 @@ void havocbot_movetogoal()
                                threshold = maxspeed * 0.2;
                                if(sxy < threshold)
                                {
-                                       dprint("Warning: ", self.netname, " got stuck on a jumppad (velocity in xy is ", ftos(sxy), "), trying to get out of it now\n");
+                                       LOG_TRACE("Warning: ", self.netname, " got stuck on a jumppad (velocity in xy is ", ftos(sxy), "), trying to get out of it now\n");
                                        self.aistatus |= AI_STATUS_OUT_JUMPPAD;
                                }
                                return;
@@ -553,7 +554,7 @@ void havocbot_movetogoal()
 
        // If there is a trigger_hurt right below try to use the jetpack or make a rocketjump
        if(skill>6)
-       if (!(self.flags & FL_ONGROUND))
+       if (!(IS_ONGROUND(self)))
        {
                tracebox(self.origin, self.mins, self.maxs, self.origin + '0 0 -65536', MOVE_NOMONSTERS, self);
                if(tracebox_hits_trigger_hurt(self.origin, self.mins, self.maxs, trace_endpos ))
@@ -600,7 +601,7 @@ void havocbot_movetogoal()
                else if(self.health>WEP_CVAR(devastator, damage)*0.5)
                {
                        if(self.velocity.z < 0)
-                       if(client_hasweapon(self, WEP_DEVASTATOR.m_id, true, false))
+                       if(client_hasweapon(self, WEP_DEVASTATOR, true, false))
                        {
                                self.movement_x = maxspeed;
 
@@ -614,7 +615,7 @@ void havocbot_movetogoal()
                                        return;
                                }
 
-                               self.switchweapon = WEP_DEVASTATOR.m_id;
+                               PS(self).m_switchweapon = WEP_DEVASTATOR;
                                self.v_angle_x = 90;
                                self.BUTTON_ATCK = true;
                                self.rocketjumptime = time + WEP_CVAR(devastator, detonatedelay);
@@ -766,7 +767,7 @@ void havocbot_movetogoal()
                        self.aistatus &= ~AI_STATUS_DANGER_AHEAD;
 
                        if(trace_fraction == 1 && self.jumppadcount == 0 && !self.goalcurrent.wphardwired )
-                       if((self.flags & FL_ONGROUND) || (self.aistatus & AI_STATUS_RUNNING) || self.BUTTON_JUMP == true)
+                       if((IS_ONGROUND(self)) || (self.aistatus & AI_STATUS_RUNNING) || self.BUTTON_JUMP == true)
                        {
                                // Look downwards
                                traceline(dst_ahead , dst_down, true, world);
@@ -788,7 +789,7 @@ void havocbot_movetogoal()
                                                if(tracebox_hits_trigger_hurt(dst_ahead, self.mins, self.maxs, trace_endpos))
                                                {
                                                        // Remove dangerous dynamic goals from stack
-                                                       dprint("bot ", self.netname, " avoided the goal ", self.goalcurrent.classname, " ", etos(self.goalcurrent), " because it led to a dangerous path; goal stack cleared\n");
+                                                       LOG_TRACE("bot ", self.netname, " avoided the goal ", self.goalcurrent.classname, " ", etos(self.goalcurrent), " because it led to a dangerous path; goal stack cleared\n");
                                                        navigation_clearroute();
                                                        return;
                                                }
@@ -848,15 +849,15 @@ void havocbot_movetogoal()
        if(skill+self.bot_moveskill >= autocvar_bot_ai_bunnyhop_skilloffset)
                havocbot_bunnyhop(dir);
 
-       if ((dir * v_up) >= autocvar_sv_jumpvelocity*0.5 && (self.flags & FL_ONGROUND)) self.BUTTON_JUMP=1;
+       if ((dir * v_up) >= autocvar_sv_jumpvelocity*0.5 && (IS_ONGROUND(self))) self.BUTTON_JUMP=1;
        if (((dodge * v_up) > 0) && random()*frametime >= 0.2*bound(0,(10-skill-self.bot_dodgeskill)*0.1,1)) self.BUTTON_JUMP=true;
        if (((dodge * v_up) < 0) && random()*frametime >= 0.5*bound(0,(10-skill-self.bot_dodgeskill)*0.1,1)) self.havocbot_ducktime=time+0.3/bound(0.1,skill+self.bot_dodgeskill,10);
 }
 
 void havocbot_chooseenemy()
-{
+{SELFPARAM();
        entity head, best, head2;
-       float rating, bestrating, i, hf;
+       float rating, bestrating, hf;
        vector eye, v;
        if (autocvar_bot_nofire || IS_INDEPENDENT_PLAYER(self))
        {
@@ -908,10 +909,29 @@ void havocbot_chooseenemy()
 
        self.dphitcontentsmask = DPCONTENTS_SOLID | DPCONTENTS_BODY | DPCONTENTS_CORPSE;
 
-       for(i = 0; ; ++i)
+       bool scan_transparent = false;
+       bool scan_secondary_targets = false;
+       bool have_secondary_targets = false;
+       while(true)
        {
-               while (head)
+               scan_secondary_targets = false;
+               :scan_targets
+               for( ; head; head = head.chain)
                {
+                       if(!scan_secondary_targets)
+                       {
+                               if(head.classname == "misc_breakablemodel")
+                               {
+                                       have_secondary_targets = true;
+                                       continue;
+                               }
+                       }
+                       else
+                       {
+                               if(head.classname != "misc_breakablemodel")
+                                       continue;
+                       }
+
                        v = (head.absmin + head.absmax) * 0.5;
                        rating = vlen(v - eye);
                        if (rating<autocvar_bot_ai_enemydetectionradius)
@@ -925,20 +945,29 @@ void havocbot_chooseenemy()
                                        bestrating = rating;
                                }
                        }
-                       head = head.chain;
+               }
+
+               if(!best && have_secondary_targets && !scan_secondary_targets)
+               {
+                       scan_secondary_targets = true;
+                       // restart the loop
+                       head = head2;
+                       bestrating = 100000000;
+                       goto scan_targets;
                }
 
                // 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_RIFLE.m_id
                        break;
-               if(i)
+               if(scan_transparent)
                        break;
 
                // Set flags to see through transparent objects
                self.dphitcontentsmask |= DPCONTENTS_OPAQUE;
 
                head = head2;
+               scan_transparent = true;
        }
 
        // Restore hit flags
@@ -946,10 +975,12 @@ void havocbot_chooseenemy()
 
        self.enemy = best;
        self.havocbot_stickenemy = true;
+       if(best && best.classname == "misc_breakablemodel")
+               self.havocbot_stickenemy = false;
 }
 
 float havocbot_chooseweapon_checkreload(int new_weapon)
-{
+{SELFPARAM();
        // 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 :)
@@ -959,13 +990,11 @@ float havocbot_chooseweapon_checkreload(int new_weapon)
        // if this weapon is scheduled for reloading, don't switch to it during combat
        if (self.weapon_load[new_weapon] < 0)
        {
-               float i, other_weapon_available = false;
-               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 (WEP_ACTION(i, WR_CHECKAMMO1) + WEP_ACTION(i, WR_CHECKAMMO2))
+               bool other_weapon_available = false;
+               FOREACH(Weapons, it != WEP_Null, LAMBDA(
+                       if(it.wr_checkammo1(it) + it.wr_checkammo2(it))
                                other_weapon_available = true;
-               }
+               ));
                if(other_weapon_available)
                        return true;
        }
@@ -974,13 +1003,13 @@ float havocbot_chooseweapon_checkreload(int new_weapon)
 }
 
 void havocbot_chooseweapon()
-{
+{SELFPARAM();
        int i;
 
        // ;)
-       if(g_weaponarena_weapons == WEPSET_TUBA)
+       if(g_weaponarena_weapons == WEPSET(TUBA))
        {
-               self.switchweapon = WEP_TUBA.m_id;
+               PS(self).m_switchweapon = WEP_TUBA;
                return;
        }
 
@@ -988,15 +1017,14 @@ void havocbot_chooseweapon()
        if(self.enemy==world)
        {
                // If no weapon was chosen get the first available weapon
-               if(self.weapon==0)
-               for(i = WEP_FIRST; i <= WEP_LAST; ++i) if(i != WEP_BLASTER.m_id)
-               {
-                       if(client_hasweapon(self, i, true, false))
+               if(PS(self).m_weapon==WEP_Null)
+               FOREACH(Weapons, it != WEP_Null, LAMBDA(
+                       if(client_hasweapon(self, it, true, false))
                        {
-                               self.switchweapon = i;
+                               PS(self).m_switchweapon = it;
                                return;
                        }
-               }
+               ));
                return;
        }
 
@@ -1011,7 +1039,7 @@ void havocbot_chooseweapon()
        // Should it do a weapon combo?
        float af, ct, combo_time, combo;
 
-       af = ATTACK_FINISHED(self);
+       af = ATTACK_FINISHED(self, 0);
        ct = autocvar_bot_ai_weapon_combo_threshold;
 
        // Bots with no skill will be 4 times more slower than "godlike" bots when doing weapon combos
@@ -1021,7 +1049,7 @@ void havocbot_chooseweapon()
        combo = false;
 
        if(autocvar_bot_ai_weapon_combo)
-       if(self.weapon == self.lastfiredweapon)
+       if(PS(self).m_weapon.m_id == self.lastfiredweapon)
        if(af > combo_time)
        {
                combo = true;
@@ -1035,13 +1063,13 @@ void havocbot_chooseweapon()
 
                // Choose weapons for far distance
                if ( distance > bot_distance_far ) {
-                       for(i=0; i < WEP_COUNT && bot_weapons_far[i] != -1 ; ++i){
+                       for(i=0; i < Weapons_COUNT && bot_weapons_far[i] != -1 ; ++i){
                                w = bot_weapons_far[i];
-                               if ( client_hasweapon(self, w, true, false) )
+                               if ( client_hasweapon(self, Weapons_from(w), true, false) )
                                {
-                                       if ((self.weapon == w && combo) || havocbot_chooseweapon_checkreload(w))
+                                       if ((PS(self).m_weapon.m_id == w && combo) || havocbot_chooseweapon_checkreload(w))
                                                continue;
-                                       self.switchweapon = w;
+                                       PS(self).m_switchweapon = Weapons_from(w);
                                        return;
                                }
                        }
@@ -1049,26 +1077,26 @@ void havocbot_chooseweapon()
 
                // Choose weapons for mid distance
                if ( distance > bot_distance_close) {
-                       for(i=0; i < WEP_COUNT && bot_weapons_mid[i] != -1 ; ++i){
+                       for(i=0; i < Weapons_COUNT && bot_weapons_mid[i] != -1 ; ++i){
                                w = bot_weapons_mid[i];
-                               if ( client_hasweapon(self, w, true, false) )
+                               if ( client_hasweapon(self, Weapons_from(w), true, false) )
                                {
-                                       if ((self.weapon == w && combo) || havocbot_chooseweapon_checkreload(w))
+                                       if ((PS(self).m_weapon.m_id == w && combo) || havocbot_chooseweapon_checkreload(w))
                                                continue;
-                                       self.switchweapon = w;
+                                       PS(self).m_switchweapon = Weapons_from(w);
                                        return;
                                }
                        }
                }
 
                // Choose weapons for close distance
-               for(i=0; i < WEP_COUNT && bot_weapons_close[i] != -1 ; ++i){
+               for(i=0; i < Weapons_COUNT && bot_weapons_close[i] != -1 ; ++i){
                        w = bot_weapons_close[i];
-                       if ( client_hasweapon(self, w, true, false) )
+                       if ( client_hasweapon(self, Weapons_from(w), true, false) )
                        {
-                               if ((self.weapon == w && combo) || havocbot_chooseweapon_checkreload(w))
+                               if ((PS(self).m_weapon.m_id == w && combo) || havocbot_chooseweapon_checkreload(w))
                                        continue;
-                               self.switchweapon = w;
+                               PS(self).m_switchweapon = Weapons_from(w);
                                return;
                        }
                }
@@ -1076,7 +1104,7 @@ void havocbot_chooseweapon()
 }
 
 void havocbot_aim()
-{
+{SELFPARAM();
        vector selfvel, enemyvel;
 //     if(self.flags & FL_INWATER)
 //             return;
@@ -1098,7 +1126,7 @@ void havocbot_aim()
 }
 
 float havocbot_moveto_refresh_route()
-{
+{SELFPARAM();
        // Refresh path to goal if necessary
        entity wp;
        wp = self.havocbot_personal_waypoint;
@@ -1109,7 +1137,7 @@ float havocbot_moveto_refresh_route()
 }
 
 float havocbot_moveto(vector pos)
-{
+{SELFPARAM();
        entity wp;
 
        if(self.aistatus & AI_STATUS_WAYPOINT_PERSONAL_GOING)
@@ -1117,7 +1145,7 @@ float havocbot_moveto(vector pos)
                // Step 4: Move to waypoint
                if(self.havocbot_personal_waypoint==world)
                {
-                       dprint("Error: ", self.netname, " trying to walk to a non existent personal waypoint\n");
+                       LOG_TRACE("Error: ", self.netname, " trying to walk to a non existent personal waypoint\n");
                        self.aistatus &= ~AI_STATUS_WAYPOINT_PERSONAL_GOING;
                        return CMD_STATUS_ERROR;
                }
@@ -1128,7 +1156,7 @@ float havocbot_moveto(vector pos)
                        bot_strategytoken_taken = true;
                        if(havocbot_moveto_refresh_route())
                        {
-                               dprint(self.netname, " walking to its personal waypoint (after ", ftos(self.havocbot_personal_waypoint_failcounter), " failed attempts)\n");
+                               LOG_TRACE(self.netname, " walking to its personal waypoint (after ", ftos(self.havocbot_personal_waypoint_failcounter), " failed attempts)\n");
                                self.havocbot_personal_waypoint_searchtime = time + 10;
                                self.havocbot_personal_waypoint_failcounter = 0;
                        }
@@ -1138,13 +1166,13 @@ float havocbot_moveto(vector pos)
                                self.havocbot_personal_waypoint_searchtime = time + 2;
                                if(self.havocbot_personal_waypoint_failcounter >= 30)
                                {
-                                       dprint("Warning: can't walk to the personal waypoint located at ", vtos(self.havocbot_personal_waypoint.origin),"\n");
+                                       LOG_TRACE("Warning: can't walk to the personal waypoint located at ", vtos(self.havocbot_personal_waypoint.origin),"\n");
                                        self.aistatus &= ~AI_STATUS_WAYPOINT_PERSONAL_LINKING;
                                        remove(self.havocbot_personal_waypoint);
                                        return CMD_STATUS_ERROR;
                                }
                                else
-                                       dprint(self.netname, " can't walk to its personal waypoint (after ", ftos(self.havocbot_personal_waypoint_failcounter), " failed attempts), trying later\n");
+                                       LOG_TRACE(self.netname, " can't walk to its personal waypoint (after ", ftos(self.havocbot_personal_waypoint_failcounter), " failed attempts), trying later\n");
                        }
                }
 
@@ -1162,7 +1190,7 @@ float havocbot_moveto(vector pos)
                if(self.aistatus & AI_STATUS_WAYPOINT_PERSONAL_REACHED)
                {
                        // Step 5: Waypoint reached
-                       dprint(self.netname, "'s personal waypoint reached\n");
+                       LOG_TRACE(self.netname, "'s personal waypoint reached\n");
                        remove(self.havocbot_personal_waypoint);
                        self.aistatus &= ~AI_STATUS_WAYPOINT_PERSONAL_REACHED;
                        return CMD_STATUS_FINISHED;
@@ -1177,7 +1205,7 @@ float havocbot_moveto(vector pos)
                // Wait until it is linked
                if(!self.havocbot_personal_waypoint.wplinked)
                {
-                       dprint(self.netname, " waiting for personal waypoint to be linked\n");
+                       LOG_TRACE(self.netname, " waiting for personal waypoint to be linked\n");
                        return CMD_STATUS_EXECUTING;
                }
 
@@ -1186,7 +1214,7 @@ float havocbot_moveto(vector pos)
                self.aistatus |= AI_STATUS_WAYPOINT_PERSONAL_GOING;
 
                // Step 3: Route to waypoint
-               dprint(self.netname, " walking to its personal waypoint\n");
+               LOG_TRACE(self.netname, " walking to its personal waypoint\n");
 
                return CMD_STATUS_EXECUTING;
        }
@@ -1195,7 +1223,7 @@ float havocbot_moveto(vector pos)
        wp = waypoint_spawnpersonal(pos);
        if(wp==world)
        {
-               dprint("Error: Can't spawn personal waypoint at ",vtos(pos),"\n");
+               LOG_TRACE("Error: Can't spawn personal waypoint at ",vtos(pos),"\n");
                return CMD_STATUS_ERROR;
        }
 
@@ -1231,7 +1259,7 @@ float havocbot_resetgoal()
 }
 
 void havocbot_setupbot()
-{
+{SELFPARAM();
        self.bot_ai = havocbot_ai;
        self.cmd_moveto = havocbot_moveto;
        self.cmd_resetgoal = havocbot_resetgoal;
@@ -1240,7 +1268,7 @@ void havocbot_setupbot()
 }
 
 vector havocbot_dodge()
-{
+{SELFPARAM();
        // LordHavoc: disabled because this is too expensive
        return '0 0 0';
 #if 0