]> git.xonotic.org Git - xonotic/xonotic-data.pk3dir.git/blobdiff - qcsrc/server/bot/default/havocbot/havocbot.qc
Bot AI: anticipate search of a new goal when bot touches a waypoint and search time...
[xonotic/xonotic-data.pk3dir.git] / qcsrc / server / bot / default / havocbot / havocbot.qc
index f5b126ff15adeddf3c9b88117eb138572dfd18f2..69549f8e356b6ece5b1fd5e145258887c34ed540 100644 (file)
@@ -1,5 +1,7 @@
 #include "havocbot.qh"
 
+#include <server/defs.qh>
+#include <server/miscfunctions.qh>
 #include "../cvars.qh"
 
 #include "../aim.qh"
@@ -136,13 +138,20 @@ void havocbot_ai(entity this)
                this.aistatus |= AI_STATUS_ROAMING;
                this.aistatus &= ~AI_STATUS_ATTACKING;
 
-               vector v, now, next;
+               vector v = '0 0 0', now, next;
                float aimdistance,skillblend,distanceblend,blend;
 
-               if(this.goalcurrent.wpisbox && boxesoverlap(this.goalcurrent.absmin, this.goalcurrent.absmax, this.origin, this.origin))
-                       v = this.goalcurrent.origin;
-               else
-                       SET_DESTCOORDS(this.goalcurrent, this.origin, v);
+               SET_DESTCOORDS(this.goalcurrent, this.origin, v);
+               if(this.goalcurrent.wpisbox)
+               {
+                       // avoid a glitch when bot is teleported but teleport waypoint isn't removed yet
+                       if(this.goalstack02 && this.goalcurrent.wpflags & WAYPOINTFLAG_TELEPORT
+                       && this.lastteleporttime > 0 && time - this.lastteleporttime < 0.15)
+                               v = (this.goalstack02.absmin + this.goalstack02.absmax) * 0.5;
+                       // aim to teleport origin if bot is inside teleport waypoint but hasn't touched the real teleport yet
+                       else if(boxesoverlap(this.goalcurrent.absmin, this.goalcurrent.absmax, this.origin, this.origin))
+                               v = this.goalcurrent.origin;
+               }
                next = now = v - (this.origin + this.view_ofs);
                aimdistance = vlen(now);
 
@@ -191,13 +200,13 @@ void havocbot_ai(entity this)
                        if(skill >= 5) // bots can only look for unloaded weapons past this skill
                        if(this.(weaponentity).clip_load >= 0) // only if we're not reloading a weapon already
                        {
-                               FOREACH(Weapons, it != WEP_Null, LAMBDA(
+                               FOREACH(Weapons, it != WEP_Null, {
                                        if((this.weapons & (it.m_wepset)) && (it.spawnflags & WEP_FLAG_RELOADABLE) && (this.(weaponentity).weapon_load[it.m_id] < it.reloading_ammo))
                                        {
                                                this.(weaponentity).m_switchweapon = it;
                                                break;
                                        }
-                               ));
+                               });
                        }
                }
        }
@@ -277,7 +286,7 @@ void havocbot_bunnyhop(entity this, vector dir)
        float bunnyhopdistance;
        vector deviation;
        float maxspeed;
-       vector gco, gno;
+       vector gco = '0 0 0', gno;
 
        // Don't jump when attacking
        if(this.aistatus & AI_STATUS_ATTACKING)
@@ -367,7 +376,7 @@ void havocbot_bunnyhop(entity this, vector dir)
                                        if(checkdistance)
                                        {
                                                this.aistatus &= ~AI_STATUS_RUNNING;
-                                               // increase stop distance in case the goal is on a slope or a lower platform 
+                                               // increase stop distance in case the goal is on a slope or a lower platform
                                                if(bunnyhopdistance > autocvar_bot_ai_bunnyhop_stopdistance + (this.origin.z - gco.z))
                                                        PHYS_INPUT_BUTTON_JUMP(this) = true;
                                        }
@@ -421,7 +430,8 @@ void havocbot_bunnyhop(entity this, vector dir)
 bool havocbot_checkgoaldistance(entity this, vector gco)
 {
        float curr_dist = vlen(this.origin - gco);
-       if(curr_dist > this.goalcurrent_distance)
+       float curr_dist_2d = vlen(vec2(this.origin - gco));
+       if(curr_dist > this.goalcurrent_distance && curr_dist_2d > this.goalcurrent_distance_2d)
        {
                if(!this.goalcurrent_distance_time)
                        this.goalcurrent_distance_time = time;
@@ -431,7 +441,8 @@ bool havocbot_checkgoaldistance(entity this, vector gco)
        else
        {
                // reduce it a little bit so it works even with very small approaches to the goal
-               this.goalcurrent_distance = max(20, curr_dist - 15);
+               this.goalcurrent_distance = max(20, curr_dist - 10);
+               this.goalcurrent_distance_2d = max(20, curr_dist_2d - 10);
                this.goalcurrent_distance_time = 0;
        }
        return false;
@@ -439,7 +450,7 @@ bool havocbot_checkgoaldistance(entity this, vector gco)
 
 void havocbot_movetogoal(entity this)
 {
-       vector destorg;
+       vector destorg = '0 0 0';
        vector diff;
        vector dir;
        vector flatdir;
@@ -725,23 +736,32 @@ void havocbot_movetogoal(entity this)
                                this.goalentity.bot_pickup_respawning = false;
                        else if(time < this.goalentity.scheduledrespawntime - 10) // item already taken (by someone else)
                        {
-                               this.goalentity.bot_pickup_respawning = false;
-                               navigation_clearroute(this);
-                               this.bot_strategytime = 0;
-                               return;
+                               if(checkpvs(this.origin, this.goalentity))
+                               {
+                                       this.goalentity.bot_pickup_respawning = false;
+                                       navigation_clearroute(this);
+                                       this.bot_strategytime = 0;
+                                       return;
+                               }
                        }
                        else if(this.goalentity == this.goalcurrent)
                                locked_goal = true; // wait for item to respawn
                }
-               else if(!this.goalentity.solid)
+               else if(!this.goalentity.solid && !boxesoverlap(this.goalentity.absmin, this.goalentity.absmax, this.absmin, this.absmax))
                {
-                       navigation_clearroute(this);
-                       this.bot_strategytime = 0;
-                       return;
+                       if(checkpvs(this.origin, this.goalentity))
+                       {
+                               navigation_clearroute(this);
+                               this.bot_strategytime = 0;
+                               return;
+                       }
                }
        }
        if(!locked_goal)
-               navigation_poptouchedgoals(this);
+       {
+               if(navigation_poptouchedgoals(this) && this.bot_strategytime < time + 1)
+                       this.bot_strategytime = 0;
+       }
 
        // if ran out of goals try to use an alternative goal or get a new strategy asap
        if(this.goalcurrent == NULL)
@@ -759,7 +779,7 @@ void havocbot_movetogoal(entity this)
 
        // in case bot ends up inside the teleport waypoint without touching
        // the teleport itself, head to the teleport origin
-       if(this.goalcurrent.wpisbox && boxesoverlap(this.goalcurrent.absmin, this.goalcurrent.absmax, this.absmin, this.absmax))
+       if(this.goalcurrent.wpisbox && boxesoverlap(this.goalcurrent.absmin, this.goalcurrent.absmax, this.origin + eZ * this.mins.z, this.origin + eZ * this.maxs.z))
        {
                bunnyhop_forbidden = true;
                destorg = this.goalcurrent.origin;
@@ -1093,10 +1113,10 @@ float havocbot_chooseweapon_checkreload(entity this, .entity weaponentity, int n
        // if this weapon is scheduled for reloading, don't switch to it during combat
        if (this.(weaponentity).weapon_load[new_weapon] < 0)
        {
-               FOREACH(Weapons, it != WEP_Null, LAMBDA(
+               FOREACH(Weapons, it != WEP_Null, {
                        if(it.wr_checkammo1(it, this, weaponentity) + it.wr_checkammo2(it, this, weaponentity))
                                return true; // other weapon available
-               ));
+               });
        }
 
        return false;
@@ -1118,13 +1138,13 @@ void havocbot_chooseweapon(entity this, .entity weaponentity)
        {
                // If no weapon was chosen get the first available weapon
                if(this.(weaponentity).m_weapon==WEP_Null)
-               FOREACH(Weapons, it != WEP_Null, LAMBDA(
+               FOREACH(Weapons, it != WEP_Null, {
                        if(client_hasweapon(this, it, weaponentity, true, false))
                        {
                                this.(weaponentity).m_switchweapon = it;
                                return;
                        }
-               ));
+               });
                return;
        }
 
@@ -1278,7 +1298,7 @@ float havocbot_moveto(entity this, vector pos)
                        debuggoalstack(this);
 
                // Heading
-               vector dir;
+               vector dir = '0 0 0';
                SET_DESTCOORDS(this.goalcurrent, this.origin, dir);
                dir = dir - (this.origin + this.view_ofs);
                dir.z = 0;