]> de.git.xonotic.org Git - xonotic/xonotic-data.pk3dir.git/commitdiff
Merge branch 'master' into terencehill/bot_fix
authorterencehill <piuntn@gmail.com>
Thu, 29 Dec 2016 01:59:28 +0000 (02:59 +0100)
committerterencehill <piuntn@gmail.com>
Thu, 29 Dec 2016 01:59:28 +0000 (02:59 +0100)
1  2 
qcsrc/common/t_items.qc
qcsrc/server/bot/default/havocbot/havocbot.qc
qcsrc/server/bot/default/navigation.qc

diff --combined qcsrc/common/t_items.qc
index 5faa6dc939aa56728448719678c323d35d082585,cacbfdcea855fe4bd33cf839e8011b65d5f5f0a6..9d89679e3be9f88817ea8abd9883774c36eca22a
@@@ -393,7 -393,7 +393,7 @@@ bool have_pickup_item(entity this
                if(autocvar_g_pickup_items == 0)
                        return false;
                if(g_weaponarena)
-                       if(this.weapons || (this.items & IT_AMMO)) // no item or ammo pickups in weaponarena
+                       if(this.weapons || this.itemdef.instanceOfAmmo) // no item or ammo pickups in weaponarena
                                return false;
        }
        return true;
@@@ -924,20 -924,17 +924,20 @@@ float generic_pickupevalfunc(entity pla
  float weapon_pickupevalfunc(entity player, entity item)
  {
        float c;
 +      int rating = item.bot_pickupbasevalue;
  
        // See if I have it already
 -      if(item.weapons & ~player.weapons)
 +      if(player.weapons & item.weapons)
        {
                // If I can pick it up
                if(!item.spawnshieldtime)
                        c = 0;
                else if(player.ammo_cells || player.ammo_shells || player.ammo_plasma || player.ammo_nails || player.ammo_rockets)
                {
 +                      if (rating > 0)
 +                              rating = BOT_PICKUP_RATING_LOW * 0.5 * (1 + rating / BOT_PICKUP_RATING_HIGH);
                        // Skilled bots will grab more
 -                      c = bound(0, skill / 10, 1) * 0.5;
 +                      c = 1 + bound(0, skill / 10, 1) * 0.5;
                }
                else
                        c = 0;
        else
                c = 1;
  
 +      if (c <= 0)
 +              return 0;
 +
        // If custom weapon priorities for bots is enabled rate most wanted weapons higher
 -      if( bot_custom_weapon && c )
 -      {
 -              // Find the highest position on any range
 -              int position = -1;
 -              for (int j = 0; j < WEP_LAST ; ++j){
 -                      if(
 -                                      bot_weapons_far[j] == item.weapon ||
 -                                      bot_weapons_mid[j] == item.weapon ||
 -                                      bot_weapons_close[j] == item.weapon
 -                        )
 +      if(bot_custom_weapon)
 +      {
 +              int best_ratio = 0;
 +              int missing = 0;
 +
 +              // evaluate weapon usefulness in all ranges
 +              for(int list = 0; list < 3; list++)
 +              {
 +                      int position = -1;
 +                      int wep_count = 0;
 +                      int wpn = item.weapon;
 +                      for (int j = 0; j < WEP_LAST; ++j)
                        {
 -                              position = j;
 -                              break;
 +                              int list_wpn = 0;
 +                              if (list == 0) list_wpn = bot_weapons_far[j];
 +                              else if (list == 1) list_wpn = bot_weapons_mid[j];
 +                              else list_wpn = bot_weapons_close[j];
 +
 +                              if (weaponsInMap & Weapons_from(list_wpn).m_wepset) // only if available
 +                              {
 +                                      if (list_wpn > 0)
 +                                              wep_count++;
 +                                      if (position == -1 && list_wpn == wpn)
 +                                              position = wep_count;
 +                              }
 +                      }
 +                      if (position == -1)
 +                      {
 +                              missing++;
 +                              position = wep_count; // if missing assume last
 +                      }
 +                      if (wep_count)
 +                      {
 +                              if (!best_ratio || position / wep_count < best_ratio)
 +                                      best_ratio = position / wep_count;
                        }
                }
  
 -              // Rate it
 -              if (position >= 0 )
 -              {
 -                      position = WEP_LAST - position;
 -                      // item.bot_pickupbasevalue is overwritten here
 -                      return (BOT_PICKUP_RATING_LOW + ( (BOT_PICKUP_RATING_HIGH - BOT_PICKUP_RATING_LOW) * (position / WEP_LAST ))) * c;
 -              }
 +              if (missing < 3 && best_ratio)
 +                      c = c - best_ratio * 0.3;
        }
  
 -      return item.bot_pickupbasevalue * c;
 +      return rating * c;
  }
  
  float commodity_pickupevalfunc(entity player, entity item)
  {
 -      float c;
 -      float need_shells = false, need_nails = false, need_rockets = false, need_cells = false, need_plasma = false, need_fuel = false;
 -      c = 0;
 +      bool need_shells = false, need_nails = false, need_rockets = false, need_cells = false, need_plasma = false, need_fuel = false;
 +      float c = 0;
  
        // Detect needed ammo
        FOREACH(Weapons, it != WEP_Null, {
                if(!(player.weapons & (it.m_wepset)))
                        continue;
  
 -              if(it.items & ITEM_Shells.m_itemid)
 -                      need_shells = true;
 -              else if(it.items & ITEM_Bullets.m_itemid)
 -                      need_nails = true;
 -              else if(it.items & ITEM_Rockets.m_itemid)
 -                      need_rockets = true;
 -              else if(it.items & ITEM_Cells.m_itemid)
 -                      need_cells = true;
 -              else if(it.items & ITEM_Plasma.m_itemid)
 -                      need_plasma = true;
 -              else if(it.items & ITEM_JetpackFuel.m_itemid)
 -                      need_fuel = true;
 +              switch(it.ammo_field)
 +              {
 +                      case ammo_shells:  need_shells  = true; break;
 +                      case ammo_nails:   need_nails   = true; break;
 +                      case ammo_rockets: need_rockets = true; break;
 +                      case ammo_cells:   need_cells   = true; break;
 +                      case ammo_plasma:  need_plasma  = true; break;
 +                      case ammo_fuel:    need_fuel    = true; break;
 +              }
        });
  
        // TODO: figure out if the player even has the weapon this ammo is for?
index af6aa3f2a9fba065fb3587c7c544762af9b6aeee,c26d768b038ae55501eb01c011dbb8c07ba74527..d211397d3bb057d108e97852fe212ce6d9d7f6d3
@@@ -29,6 -29,13 +29,6 @@@ void havocbot_ai(entity this
        if(bot_execute_commands(this))
                return;
  
 -      while(this.goalcurrent && wasfreed(this.goalcurrent))
 -      {
 -              navigation_poproute(this);
 -              if(!this.goalcurrent)
 -                      this.bot_strategytime = 0;
 -      }
 -
        if (bot_strategytoken == this)
        if (!bot_strategytoken_taken)
        {
@@@ -343,8 -350,7 +343,8 @@@ void havocbot_bunnyhop(entity this, vec
                                        if(checkdistance)
                                        {
                                                this.aistatus &= ~AI_STATUS_RUNNING;
 -                                              if(bunnyhopdistance > autocvar_bot_ai_bunnyhop_stopdistance)
 +                                              // 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;
                                        }
                                        else
@@@ -474,7 -480,7 +474,7 @@@ void havocbot_movetogoal(entity this
  
                // Flying
                PHYS_INPUT_BUTTON_HOOK(this) = true;
-               if(this.navigation_jetpack_point.z - STAT(PL_MAX, NULL).z + STAT(PL_MIN, NULL).z < this.origin.z)
+               if(this.navigation_jetpack_point.z - STAT(PL_MAX, this).z + STAT(PL_MIN, this).z < this.origin.z)
                {
                        this.movement_x = dir * v_forward * maxspeed;
                        this.movement_y = dir * v_right * maxspeed;
        if(this.jumppadcount)
        {
                // If got stuck on the jump pad try to reach the farthest visible waypoint
 +              // but with some randomness so it can try out different paths
                if(this.aistatus & AI_STATUS_OUT_JUMPPAD)
                {
                        if(fabs(this.velocity.z)<50)
                                        if(trace_fraction < 1)
                                                continue;
  
 -                                      if(!newgoal || vlen2(it.origin - this.origin) > vlen2(newgoal.origin - this.origin))
 +                                      if(!newgoal || ((random() < 0.8) && vlen2(it.origin - this.origin) > vlen2(newgoal.origin - this.origin)))
                                                newgoal = it;
                                });
  
                                        this.ignoregoaltime = time + autocvar_bot_ai_ignoregoal_timeout;
                                        navigation_clearroute(this);
                                        navigation_routetogoal(this, newgoal, this.origin);
 +                                      if(autocvar_bot_debug_goalstack)
 +                                              debuggoalstack(this);
                                        this.aistatus &= ~AI_STATUS_OUT_JUMPPAD;
                                }
                        }
        if (this.goalcurrent == NULL)
                return;
  
 -      if (this.goalcurrent)
 -              navigation_poptouchedgoals(this);
 +      navigation_poptouchedgoals(this);
  
        // if ran out of goals try to use an alternative goal or get a new strategy asap
        if(this.goalcurrent == NULL)
                        }
  
                        // avoiding dangers and obstacles
 -                      vector dst_ahead, dst_down;
 -                      makevectors(this.v_angle.y * '0 1 0');
 -                      dst_ahead = this.origin + this.view_ofs + (this.velocity * 0.4) + (v_forward * 32 * 3);
 -                      dst_down = dst_ahead - '0 0 1500';
 +                      vector dst_ahead = this.origin + this.view_ofs + this.velocity * 0.5;
 +                      vector dst_down = dst_ahead - '0 0 3000';
  
                        // Look ahead
                        traceline(this.origin + this.view_ofs, dst_ahead, true, NULL);
                        this.aistatus &= ~AI_STATUS_DANGER_AHEAD;
  
                        if(trace_fraction == 1 && this.jumppadcount == 0 && !this.goalcurrent.wphardwired )
 -                      if((IS_ONGROUND(this)) || (this.aistatus & AI_STATUS_RUNNING) || PHYS_INPUT_BUTTON_JUMP(this))
 +                      if((IS_ONGROUND(this)) || (this.aistatus & AI_STATUS_RUNNING) || (this.aistatus & AI_STATUS_ROAMING) || PHYS_INPUT_BUTTON_JUMP(this))
                        {
                                // Look downwards
                                traceline(dst_ahead , dst_down, true, NULL);
 -                      //      te_lightning2(NULL, this.origin, dst_ahead);    // Draw "ahead" look
 -                      //      te_lightning2(NULL, dst_ahead, dst_down);               // Draw "downwards" look
 +                              //te_lightning2(NULL, this.origin + this.view_ofs, dst_ahead); // Draw "ahead" look
 +                              //te_lightning2(NULL, dst_ahead, dst_down); // Draw "downwards" look
                                if(trace_endpos.z < this.origin.z + this.mins.z)
                                {
                                        s = pointcontents(trace_endpos + '0 0 1');
                                                evadelava = normalize(this.velocity) * -1;
                                        else if (s == CONTENT_SKY)
                                                evadeobstacle = normalize(this.velocity) * -1;
 -                                      else if (!boxesoverlap(dst_ahead - this.view_ofs + this.mins, dst_ahead - this.view_ofs + this.maxs,
 -                                                              this.goalcurrent.absmin, this.goalcurrent.absmax))
 +                                      else
                                        {
 -                                              // if ain't a safe goal with "holes" (like the jumpad on soylent)
 -                                              // and there is a trigger_hurt below
 -                                              if(tracebox_hits_trigger_hurt(dst_ahead, this.mins, this.maxs, trace_endpos))
 +                                              tracebox(dst_ahead, this.mins, this.maxs, dst_down, true, this);
 +                                              if (tracebox_hits_trigger_hurt(dst_ahead, this.mins, this.maxs, trace_endpos))
                                                {
 -                                                      // Remove dangerous dynamic goals from stack
 -                                                      LOG_TRACE("bot ", this.netname, " avoided the goal ", this.goalcurrent.classname, " ", etos(this.goalcurrent), " because it led to a dangerous path; goal stack cleared");
 -                                                      navigation_clearroute(this);
 -                                                      return;
 +                                                      if (gco.z > this.origin.z + jumpstepheightvec.z)
 +                                                      { 
 +                                                              // the goal is probably on an upper platform, assume bot can't get there
 +                                                              LOG_TRACE("bot ", this.netname, " avoided the goal ", this.goalcurrent.classname, " ", etos(this.goalcurrent), " because it led to a dangerous path; goal stack cleared");
 +                                                              navigation_clearroute(this);
 +                                                              this.bot_strategytime = 0;
 +                                                      }
 +                                                      else
 +                                                              evadelava = normalize(this.velocity) * -1;
                                                }
                                        }
                                }
index 83077e1d400707a97c757599082b426bc656937c,2a2f7753daf2cd22008bd4f776816bc5568fc14e..4c8982e9457a22d9614a6d4aed2d2bec00ab46fa
@@@ -360,12 -360,12 +360,12 @@@ float navigation_waypoint_will_link(vec
                {
                        if (walkfromwp)
                        {
-                               if (tracewalk(ent, v, STAT(PL_MIN, NULL), STAT(PL_MAX, NULL), org, bot_navigation_movemode))
+                               if (tracewalk(ent, v, PL_MIN_CONST, PL_MAX_CONST, org, bot_navigation_movemode))
                                        return true;
                        }
                        else
                        {
-                               if (tracewalk(ent, org, STAT(PL_MIN, NULL), STAT(PL_MAX, NULL), v, bot_navigation_movemode))
+                               if (tracewalk(ent, org, PL_MIN_CONST, PL_MAX_CONST, v, bot_navigation_movemode))
                                        return true;
                        }
                }
@@@ -387,7 -387,7 +387,7 @@@ entity navigation_findnearestwaypoint_w
        });
  
        vector org = ent.origin + 0.5 * (ent.mins + ent.maxs);
-       org.z = ent.origin.z + ent.mins.z - STAT(PL_MIN, NULL).z; // player height
+       org.z = ent.origin.z + ent.mins.z - PL_MIN_CONST.z; // player height
        // TODO possibly make other code have the same support for bboxes
        if(ent.tag_entity)
                org = org + ent.tag_entity.origin;
@@@ -684,7 -684,7 +684,7 @@@ void navigation_routerating(entity this
                        float zdistance, xydistance, cost, t, fuel;
                        vector down, npa, npb;
  
-                       down = '0 0 -1' * (STAT(PL_MAX, NULL).z - STAT(PL_MIN, NULL).z) * 10;
+                       down = '0 0 -1' * (STAT(PL_MAX, this).z - STAT(PL_MIN, this).z) * 10;
  
                        do{
                                npa = pointa + down;
@@@ -842,7 -842,7 +842,7 @@@ bool navigation_routetogoal(entity this
                return true;
  
        // if it can reach the goal there is nothing more to do
-       if (tracewalk(this, startposition, STAT(PL_MIN, NULL), STAT(PL_MAX, NULL), (e.absmin + e.absmax) * 0.5, bot_navigation_movemode))
+       if (tracewalk(this, startposition, STAT(PL_MIN, this), STAT(PL_MAX, this), (e.absmin + e.absmax) * 0.5, bot_navigation_movemode))
                return true;
  
        // see if there are waypoints describing a path to the item
@@@ -876,15 -876,10 +876,15 @@@ void navigation_poptouchedgoals(entity 
        m1 = org + this.mins;
        m2 = org + this.maxs;
  
 +      while(this.goalcurrent && wasfreed(this.goalcurrent))
 +              navigation_poproute(this);
 +
        if(this.goalcurrent.wpflags & WAYPOINTFLAG_TELEPORT)
        {
 +              // make sure jumppad is really hit, don't rely on distance based checks
 +              // as they may report a touch even if it didn't really happen
                if(this.lastteleporttime>0)
 -              if(time-this.lastteleporttime<(this.goalcurrent.wpflags & WAYPOINTFLAG_PERSONAL)?2:0.15)
 +              if(time - this.lastteleporttime < ((this.goalcurrent.wpflags & WAYPOINTFLAG_PERSONAL) ? 2 : 0.15))
                {
                        if(this.aistatus & AI_STATUS_WAYPOINT_PERSONAL_GOING)
                        if(this.goalcurrent.wpflags & WAYPOINTFLAG_PERSONAL && this.goalcurrent.owner==this)
        if(IS_PLAYER(this.goalcurrent))
                navigation_poproute(this);
  
 -      // aid for detecting jump pads better (distance based check fails sometimes)
 -      if(this.goalcurrent.wpflags & WAYPOINTFLAG_TELEPORT && this.jumppadcount > 0 )
 -              navigation_poproute(this);
 -
        // Loose goal touching check when running
        if(this.aistatus & AI_STATUS_RUNNING)
        if(this.speed >= autocvar_sv_maxspeed) // if -really- running
        if(this.goalcurrent.classname=="waypoint")
 +      if(!(this.goalcurrent.wpflags & WAYPOINTFLAG_TELEPORT))
        {
                if(vdist(this.origin - this.goalcurrent.origin, <, 150))
                {
  
        while (this.goalcurrent && boxesoverlap(m1, m2, this.goalcurrent.absmin, this.goalcurrent.absmax))
        {
 +              if((this.goalcurrent.wpflags & WAYPOINTFLAG_TELEPORT))
 +                      break;
 +
                // Detect personal waypoints
                if(this.aistatus & AI_STATUS_WAYPOINT_PERSONAL_GOING)
                if(this.goalcurrent.wpflags & WAYPOINTFLAG_PERSONAL && this.goalcurrent.owner==this)
@@@ -1051,7 -1046,7 +1051,7 @@@ void navigation_unstuck(entity this
                // evaluate the next goal on the queue
                float d = vlen2(this.origin - bot_waypoint_queue_goal.origin);
                LOG_DEBUG(this.netname, " evaluating ", bot_waypoint_queue_goal.classname, " with distance ", ftos(d));
-               if(tracewalk(bot_waypoint_queue_goal, this.origin, STAT(PL_MIN, NULL), STAT(PL_MAX, NULL), bot_waypoint_queue_goal.origin, bot_navigation_movemode))
+               if(tracewalk(bot_waypoint_queue_goal, this.origin, STAT(PL_MIN, this), STAT(PL_MAX, this), bot_waypoint_queue_goal.origin, bot_navigation_movemode))
                {
                        if( d > bot_waypoint_queue_bestgoalrating)
                        {