home *** CD-ROM | disk | FTP | other *** search
/ Maximum CD 2007 September / maximum-cd-2007-09.iso / Assets / data / AssaultCube_v0.93.exe / source / src / bot / bot_ai.cpp < prev    next >
Encoding:
C/C++ Source or Header  |  2006-12-06  |  67.8 KB  |  2,027 lines

  1. //
  2. // C++ Implementation: bot_ai
  3. //
  4. // Description: The AI part comes here(navigation, shooting etc)
  5. //
  6. //
  7. // Author:  <rickhelmus@gmail.com>
  8. //
  9.  
  10.  
  11. // Code of CBot - Start
  12.  
  13. #include "bot.h"
  14.  
  15. extern weaponinfo_s WeaponInfoTable[MAX_WEAPONS];
  16.  
  17. vec CBot::GetEnemyPos(playerent *d)
  18. {
  19.      // Aim offset idea by botman
  20.      vec o = d->o, offset;
  21.      float flDist = GetDistance(d->o), flScale;
  22.      
  23.      if (WeaponInfoTable[m_pMyEnt->gunselect].eWeaponType == TYPE_ROCKET)
  24.      {
  25.           // Bot is using a rocket launcher, aim at enemy feet?
  26.           if (m_bShootAtFeet && !OUTBORD(d->o.x, d->o.y))
  27.           {
  28.                // Only do this when enemy is fairly close to the ground
  29.                vec end = o;
  30.                end.z -= 900.0f;
  31.                traceresult_s tr;
  32.                TraceLine(o, end, NULL, false, &tr);
  33.                if ((o.z - tr.end.z) < 8.0f)
  34.                {
  35.                     end = o;
  36.                     end.z = tr.end.z;
  37.                     if (IsVisible(end))
  38.                     {
  39.                          // Target at ground
  40.                          o.z = tr.end.z;
  41.                     }
  42.                }
  43.           }
  44.           
  45.           if (m_pBotSkill->bCanPredict)
  46.           {
  47.                // How higher the skill, how further the bot predicts
  48.                float flPredictTime = RandomFloat(1.25f, 1.7f) / (m_sSkillNr+1);
  49.                o = PredictPos(o, d->vel, flPredictTime);
  50.           }
  51.      }
  52.      else
  53.      {
  54.           if (m_pBotSkill->bCanPredict)
  55.           {
  56.                // How higher the skill, how 'more' the bot predicts
  57.                float flPredictTime = RandomFloat(0.8f, 1.2f) / (m_sSkillNr+1);
  58.                o = PredictPos(o, d->vel, flPredictTime);
  59.           }
  60.      }
  61.           
  62.      if (flDist > 60.0f)
  63.           flScale = 1.0f;
  64.      else if (flDist > 6.0f)
  65.           flScale = flDist / 60.0f;
  66.      else
  67.           flScale = 0.1f;
  68.  
  69.      switch (m_sSkillNr)
  70.      {
  71.      case 0:
  72.           // no offset
  73.           offset.x = 0;  
  74.           offset.y = 0;
  75.           offset.z = 0;
  76.           break;
  77.      case 1:
  78.           // GOOD, offset a little for x, y, and z
  79.           offset.x = RandomFloat(-3, 3) * flScale;
  80.           offset.y = RandomFloat(-3, 3) * flScale;
  81.           offset.z = RandomFloat(-6, 6) * flScale;
  82.           break;
  83.      case 2:
  84.           // FAIR, offset somewhat for x, y, and z
  85.           offset.x = RandomFloat(-8, 8) * flScale;
  86.           offset.y = RandomFloat(-8, 8) * flScale;
  87.           offset.z = RandomFloat(-12, 12) * flScale;
  88.           break;
  89.      case 3:
  90.           // POOR, offset for x, y, and z
  91.           offset.x = RandomFloat(-15, 15) * flScale;
  92.           offset.y = RandomFloat(-15, 15) * flScale;
  93.           offset.z = RandomFloat(-25, 25) * flScale;
  94.           break;
  95.      case 4:
  96.           // BAD, offset lots for x, y, and z
  97.           offset.x = RandomFloat(-20, 20) * flScale;
  98.           offset.y = RandomFloat(-20, 20) * flScale;
  99.           offset.z = RandomFloat(-35, 35) * flScale;
  100.           break;
  101.      }
  102.      
  103.      o.add(offset);
  104.      return o;
  105. }    
  106.  
  107. bool CBot::FindEnemy(void)
  108. {
  109.      // UNDONE: Enemies are now only scored on their distance
  110.           
  111.      if (m_pMyEnt->enemy) // Bot already has an enemy 
  112.      {
  113.           // Check if the enemy is still in game
  114.           bool found = IsInGame(m_pMyEnt->enemy);
  115.           
  116.           // Check if the enemy is still ingame, still alive, not joined my team and is visible
  117.           if (found && !isteam(m_pMyEnt->team, m_pMyEnt->enemy->team))
  118.           {
  119.                if ((m_pMyEnt->enemy->state == CS_ALIVE) && (IsVisible(m_pMyEnt->enemy)))
  120.                     return true;
  121.                else
  122.                     m_pPrevEnemy = m_pMyEnt->enemy;
  123.           }
  124.           else
  125.                m_pMyEnt->enemy = NULL;               
  126.      }
  127.      
  128.      if (m_iEnemySearchDelay > lastmillis) return (m_pMyEnt->enemy!=NULL);
  129.      
  130.      m_pMyEnt->enemy = NULL;
  131.           
  132.      // Add enemy searchy delay
  133.      float MinDelay = m_pBotSkill->flMinEnemySearchDelay;
  134.      float MaxDelay = m_pBotSkill->flMaxEnemySearchDelay;
  135.      m_iEnemySearchDelay = lastmillis + int(RandomFloat(MinDelay, MaxDelay) * 1000.0f);
  136.      
  137.      playerent *pNewEnemy = NULL, *d = NULL;
  138.      float flDist, flNearestDist = 99999.9f;
  139.      short EnemyVal, BestEnemyVal = -100;
  140.      
  141.           // First loop through all players
  142.           loopv(players)
  143.           {
  144.                d = players[i]; // Handy shortcut
  145.           
  146.                if (d == m_pMyEnt || !d || isteam(d->team, m_pMyEnt->team) || (d->state != CS_ALIVE))
  147.                     continue;
  148.           
  149.                // Check if the enemy is visible
  150.                if(!IsInFOV(d) || !IsVisible(d))
  151.                     continue;
  152.           
  153.                flDist = GetDistance(d->o);
  154.                EnemyVal = 1;
  155.           
  156.                if (flDist < flNearestDist)
  157.                {
  158.                     EnemyVal+=2;
  159.                     flNearestDist = flDist;
  160.                }
  161.                
  162.                if (EnemyVal > BestEnemyVal)
  163.                {
  164.                     pNewEnemy = d;
  165.                     BestEnemyVal = EnemyVal;
  166.                }
  167.           }
  168.          
  169.           // Then examine the local player
  170.           if (player1 && !isteam(player1->team, m_pMyEnt->team) &&
  171.               (player1->state == CS_ALIVE))
  172.           {
  173.                // Check if the enemy is visible
  174.                if(IsInFOV(player1) && IsVisible(player1))
  175.                {
  176.                     flDist = GetDistance(player1->o);
  177.                     EnemyVal = 1;
  178.           
  179.                     if (flDist < flNearestDist)
  180.                     {
  181.                          EnemyVal+=2;
  182.                          flNearestDist = flDist;
  183.                     }
  184.                
  185.                     if (EnemyVal > BestEnemyVal)
  186.                     {
  187.                          pNewEnemy = player1;
  188.                          BestEnemyVal = EnemyVal;
  189.                     }
  190.                }
  191.           }    
  192.      //}
  193.           
  194.      if (pNewEnemy)
  195.      {
  196.           if (!m_pMyEnt->enemy) // Add shoot delay if new enemy is found
  197.           {
  198.                float flMinShootDelay = m_pBotSkill->flMinAttackDelay;
  199.                float flMaxShootDelay = m_pBotSkill->flMaxAttackDelay;
  200.                
  201.                m_iShootDelay = lastmillis + int(RandomFloat(flMinShootDelay,
  202.                                                 flMaxShootDelay) * 1000.0f);
  203.           }
  204.      
  205.           if ((m_pMyEnt->enemy != pNewEnemy) && m_pMyEnt->enemy)
  206.                m_pPrevEnemy = m_pMyEnt->enemy;
  207.                
  208.           m_pMyEnt->enemy = pNewEnemy;          
  209.           return true;
  210.      }
  211.  
  212.      return false;
  213. }
  214.  
  215. bool CBot::CheckHunt(void)
  216. {
  217.      if (!BotManager.BotsShoot()) return false;
  218.      
  219.      if (m_pHuntTarget) // Bot already has an enemy to hunt
  220.      {
  221.           // Check if the enemy is still in game
  222.           bool found = IsInGame(m_pHuntTarget);
  223.           
  224.           // Check if the enemy is still ingame, still alive, not joined my team and is visible
  225.           if (found && !isteam(m_pMyEnt->team, m_pHuntTarget->team))
  226.           {
  227.                if ((m_pHuntTarget->state == CS_ALIVE) && IsReachable(m_vHuntLocation))
  228.                     return true;
  229.           }
  230.           else     
  231.                m_pHuntTarget = NULL;               
  232.      }
  233.      
  234.      if (m_iHuntDelay > lastmillis) return (m_pHuntTarget!=NULL);
  235.      
  236.      if (m_vHuntLocation!=g_vecZero)
  237.           m_vPrevHuntLocation = m_vHuntLocation;
  238.           
  239.      m_pHuntTarget = NULL;
  240.      m_vHuntLocation = g_vecZero;
  241.           
  242.      // Add enemy hunt search delay
  243.      m_iHuntDelay = lastmillis + 1500;
  244.           
  245.      playerent *pNewEnemy = NULL, *d = NULL;
  246.      float flDist, flNearestDist = 99999.9f, flNearestOldPosDistToEnemy = 99999.9f;
  247.      float flNearestOldPosDistToBot = 99999.9f;
  248.      short EnemyVal, BestEnemyVal = -100;
  249.      vec BestOldPos;
  250.      
  251.           // First loop through all players
  252.           loopv(players)
  253.           {
  254.                d = players[i]; // Handy shortcut
  255.           
  256.                if (d == m_pMyEnt || !d || isteam(d->team, m_pMyEnt->team) || (d->state != CS_ALIVE))
  257.                     continue;
  258.           
  259.                flDist = GetDistance(d->o);
  260.                
  261.                if (flDist > 250.0f) continue;
  262.                
  263.                EnemyVal = 1;
  264.           
  265.                if (flDist < flNearestDist)
  266.                {
  267.                     EnemyVal+=2;
  268.                     flNearestDist = flDist;
  269.                }
  270.                
  271.                if (d == m_pPrevEnemy)
  272.                     EnemyVal+=2;
  273.                     
  274.                if (EnemyVal < BestEnemyVal) continue;
  275.                
  276.                vec bestfromenemy = g_vecZero, bestfrombot = g_vecZero;
  277.                flNearestOldPosDistToEnemy = flNearestOldPosDistToBot = 9999.9f;
  278.                
  279.                // Check previous locations of enemy
  280.                for (int j=0;j<d->history.size();j++)
  281.                {
  282.                     const vec &v = d->history.getpos(j);
  283.                     if (v==m_vPrevHuntLocation) continue;
  284.                     
  285.                     flDist = GetDistance(d->o, v);
  286.                     
  287.                     if ((flDist < flNearestOldPosDistToEnemy) && IsReachable(v))
  288.                     {
  289.                          flNearestOldPosDistToEnemy = flDist;
  290.                          bestfromenemy = v;
  291.                     }
  292.                }
  293.                
  294.                // Check previous locations of bot hisself
  295.                for (int j=0;j<m_pMyEnt->history.size();j++)
  296.                {
  297.                     const vec &v = m_pMyEnt->history.getpos(j);
  298.                     if (v==m_vPrevHuntLocation) continue;
  299.                     
  300.                     flDist = GetDistance(v);
  301.                     
  302.                     if ((flDist < flNearestOldPosDistToBot) && ::IsVisible(d->o, v) &&
  303.                          IsReachable(v))
  304.                     {
  305.                          flNearestOldPosDistToBot = flDist;
  306.                          bestfrombot = v;
  307.                     }
  308.                }
  309.                                              
  310.                if (bestfromenemy!=g_vecZero)
  311.                {                    
  312.                     pNewEnemy = d;
  313.                     BestEnemyVal = EnemyVal;
  314.                     BestOldPos = bestfromenemy;                   
  315.                }
  316.                else if (bestfrombot!=g_vecZero)
  317.                {                    
  318.                     pNewEnemy = d;
  319.                     BestEnemyVal = EnemyVal;
  320.                     BestOldPos = bestfrombot;
  321.                }               
  322.  
  323.           // Then examine the local player
  324.           if (player1 && !isteam(player1->team, m_pMyEnt->team) &&
  325.               (player1->state == CS_ALIVE) && ((flDist = GetDistance(player1->o)) <= 250.0f))
  326.           {
  327.                d = player1;
  328.                EnemyVal = 1;
  329.           
  330.                if (flDist < flNearestDist)
  331.                {
  332.                     EnemyVal+=2;
  333.                     flNearestDist = flDist;
  334.                }
  335.                
  336.                if (d == m_pPrevEnemy)
  337.                     EnemyVal+=2;
  338.                     
  339.                if (EnemyVal >= BestEnemyVal)
  340.                {    
  341.                     BestEnemyVal = EnemyVal;           
  342.                     vec bestfromenemy = g_vecZero, bestfrombot = g_vecZero;
  343.                     flNearestOldPosDistToEnemy = flNearestOldPosDistToBot = 9999.9f;
  344.                
  345.                     // Check previous locations of enemy
  346.                     for (int j=0;j<d->history.size();j++)
  347.                     {
  348.                          const vec &v = d->history.getpos(j);
  349.                          if (v==m_vPrevHuntLocation) continue;
  350.                     
  351.                          flDist = GetDistance(d->o, v);
  352.                     
  353.                          if ((flDist < flNearestOldPosDistToEnemy) && IsReachable(v))
  354.                          {
  355.                               flNearestOldPosDistToEnemy = flDist;
  356.                               bestfromenemy = v;
  357.                          }
  358.                     }
  359.                
  360.                     // Check previous locations of bot hisself
  361.                     for (int j=0;j<m_pMyEnt->history.size();j++)
  362.                     {
  363.                          const vec &v = m_pMyEnt->history.getpos(j);
  364.                          if (v==m_vPrevHuntLocation) continue;
  365.                     
  366.                          flDist = GetDistance(v);
  367.                     
  368.                          if ((flDist < flNearestOldPosDistToBot) && ::IsVisible(d->o, v) &&
  369.                              IsReachable(v))
  370.                          {
  371.                               flNearestOldPosDistToBot = flDist;
  372.                               bestfrombot = v;
  373.                          }
  374.                     }
  375.                                              
  376.                     if (bestfromenemy!=g_vecZero)
  377.                     {                    
  378.                          pNewEnemy = d;
  379.                          BestEnemyVal = EnemyVal;
  380.                          BestOldPos = bestfromenemy;                   
  381.                     }
  382.                     else if (bestfrombot!=g_vecZero)
  383.                     {                    
  384.                          pNewEnemy = d;
  385.                          BestEnemyVal = EnemyVal;
  386.                          BestOldPos = bestfrombot;
  387.                     }               
  388.                }    
  389.           }
  390.      }
  391.      
  392.      if (pNewEnemy)
  393.      {
  394.           if (!m_pHuntTarget) // Add shoot delay if new enemy is found
  395.           {
  396.                float flMinShootDelay = m_pBotSkill->flMinAttackDelay;
  397.                float flMaxShootDelay = m_pBotSkill->flMaxAttackDelay;
  398.                
  399.                m_iShootDelay = lastmillis + int(RandomFloat(flMinShootDelay,
  400.                                                 flMaxShootDelay) * 1000.0f);
  401.           }
  402.      
  403.           if (m_vHuntLocation!=g_vecZero)
  404.                m_vPrevHuntLocation = m_vHuntLocation;
  405.           
  406.           m_pHuntTarget = pNewEnemy;
  407.           m_vHuntLocation = BestOldPos;
  408.           m_fPrevHuntDist = 0.0f;
  409.           return true;
  410.      }
  411.  
  412.      return false;
  413. }
  414.  
  415. bool CBot::HuntEnemy(void)
  416. {
  417.      if (m_iCombatNavTime > lastmillis)
  418.      {
  419.           SetMoveDir(m_iMoveDir, false);
  420.           return true;
  421.      }
  422.      
  423.      m_iCombatNavTime = m_iMoveDir = 0;
  424.      
  425.      bool bDone = false, bNew = false;
  426.      float flDist = GetDistance(m_vHuntLocation);
  427.      
  428.      if (flDist <= 3.0f)
  429.           bDone = true;
  430.           
  431.      if ((m_fPrevHuntDist > 0.0) && (flDist > m_fPrevHuntDist))
  432.           bDone = true;
  433.           
  434.      m_fPrevHuntDist = flDist;
  435.      
  436.      if ((m_iHuntPlayerUpdateTime < lastmillis) || bDone)
  437.      {
  438.           m_iHuntPlayerUpdateTime = lastmillis + 1250;
  439.                
  440.           short BestPosIndexFromEnemy = -1, BestPosIndexFromBot = -1,  j;
  441.           float NearestDistToEnemy = 9999.9f, NearestDistToBot = 9999.9f;
  442.  
  443.           // Check previous locations of enemy
  444.           for (j=0;j<m_pHuntTarget->history.size();j++)
  445.           {
  446.                const vec &OldPos = m_pHuntTarget->history.getpos(j);
  447.                          
  448.                if (bDone && m_vHuntLocation==OldPos)
  449.                     continue;
  450.                
  451.                if (GetDistance(OldPos) > 250.0f)
  452.                     continue;
  453.                     
  454.                flDist = GetDistance(m_pHuntTarget->o, OldPos);
  455.                     
  456.                if ((flDist < NearestDistToEnemy) && (IsReachable(OldPos)))
  457.                {
  458.                     NearestDistToEnemy = flDist;
  459.                     BestPosIndexFromEnemy = j;
  460.                     break;
  461.                }
  462.           }
  463.           
  464.           // Check previous locations of bot
  465.           for (j=0;j<m_pMyEnt->history.size();j++)
  466.           {
  467.                const vec &OldPos = m_pMyEnt->history.getpos(j);
  468.                          
  469.                if (bDone && m_vHuntLocation==OldPos)
  470.                     continue;
  471.                
  472.                if (GetDistance(OldPos) > 25.0f)
  473.                     continue;
  474.                     
  475.                flDist = GetDistance(OldPos);
  476.                     
  477.                if ((flDist < NearestDistToBot) && ::IsVisible(m_pHuntTarget->o, OldPos) &&
  478.                    (IsReachable(OldPos)))
  479.                {
  480.                     NearestDistToBot = flDist;
  481.                     BestPosIndexFromBot = j;
  482.                     break;
  483.                }
  484.           }
  485.           
  486.           if (BestPosIndexFromEnemy > -1)
  487.           {
  488.                m_vPrevHuntLocation = m_vHuntLocation;
  489.                m_vHuntLocation = m_pHuntTarget->history.getpos(BestPosIndexFromEnemy);
  490.                bNew = true;
  491.                m_fPrevHuntDist = 0.0f;
  492.           }
  493.           else if (BestPosIndexFromBot > -1)
  494.           {
  495.                m_vPrevHuntLocation = m_vHuntLocation;
  496.                m_vHuntLocation = m_pMyEnt->history.getpos(BestPosIndexFromEnemy);
  497.                bNew = true;
  498.                m_fPrevHuntDist = 0.0f;
  499.           }          
  500.      }
  501.      
  502.      if (!bNew) // Check if current location is still reachable
  503.      {
  504.           if (bDone || !IsReachable(m_vHuntLocation))
  505.           {
  506.                m_pHuntTarget = NULL;
  507.                m_vPrevHuntLocation = m_vHuntLocation;
  508.                m_vHuntLocation = g_vecZero;
  509.                m_fPrevHuntDist = 0.0f;
  510.                m_iHuntDelay = lastmillis + 3500;
  511.                return false;
  512.           }
  513.      }
  514.      else
  515.           condebug("New hunt pos");
  516.           
  517.      // Aim to position
  518.      //AimToVec(m_vHuntLocation);
  519.                     
  520.      int iMoveDir = GetDirection(GetViewAngles(), m_pMyEnt->o, m_vHuntLocation);
  521.                                         
  522.      if (iMoveDir != DIR_NONE)
  523.      {
  524.           m_iMoveDir = iMoveDir;
  525.           m_iCombatNavTime = lastmillis + 125;
  526.      }
  527.      
  528.      bool aimtopos = true;
  529.      
  530.      if ((lastmillis - m_iSawEnemyTime) > 1500)
  531.      {
  532.           if (m_iLookAroundDelay < lastmillis)
  533.           {
  534.                if (m_iLookAroundTime > lastmillis)
  535.                {
  536.                     if (m_iLookAroundUpdateTime < lastmillis)
  537.                     {
  538.                          float flAddAngle;
  539.                          if (m_bLookLeft) flAddAngle = RandomFloat(-110, -80);
  540.                          else flAddAngle = RandomFloat(80, 110);
  541.                          m_pMyEnt->targetyaw = WrapYZAngle(m_pMyEnt->targetyaw + flAddAngle);
  542.                          m_iLookAroundUpdateTime = lastmillis + RandomLong(400, 800);
  543.                     }
  544.                     aimtopos = false;
  545.                }
  546.                else if (m_iLookAroundTime > 0)
  547.                {
  548.                     m_iLookAroundTime = 0;
  549.                     m_iLookAroundDelay = lastmillis + RandomLong(750, 1000);
  550.                }
  551.                else
  552.                     m_iLookAroundTime = lastmillis + RandomLong(2200, 3200);
  553.           }
  554.      }
  555.      
  556.      if (aimtopos)
  557.           AimToVec(m_vHuntLocation);
  558.      
  559.      debugbeam(m_pMyEnt->o, m_vHuntLocation);
  560.      
  561.      if (m_fYawToTurn <= 25.0f)
  562.           m_iHuntLastTurnLessTime = lastmillis;
  563.  
  564.      // Bot had to turn much for a while?
  565.      if ((m_iHuntLastTurnLessTime > 0) &&  (m_iHuntLastTurnLessTime < (lastmillis - 1000)))
  566.      {
  567.           m_iHuntPauseTime = lastmillis + 200;
  568.      }
  569.  
  570.      if (m_iHuntPauseTime >= lastmillis)
  571.      {
  572.           m_pMyEnt->move = 0;
  573.           m_fPrevHuntDist = 0.0f;
  574.      }
  575.      else
  576.      {
  577.           // Check if bot has to jump over a wall...
  578.           if (CheckJump())
  579.                m_pMyEnt->jumpnext = true;
  580.           else // Check if bot has to jump to reach this location
  581.           {
  582.                float flHeightDiff = m_vHuntLocation.z - m_pMyEnt->o.z;
  583.                bool bToHigh = false;
  584.                if (Get2DDistance(m_vHuntLocation) <= 2.0f)
  585.                {
  586.                     if (flHeightDiff >= 1.5f)
  587.                     {
  588.                          if (flHeightDiff <= JUMP_HEIGHT)
  589.                          {
  590. #ifndef RELEASE_BUILD                    
  591.                               char sz[64];
  592.                               sprintf(sz, "OldPos z diff: %f", m_vHuntLocation.z-m_pMyEnt->o.z);
  593.                               condebug(sz);
  594. #endif                         
  595.                               // Jump if close to pos and the pos is high
  596.                               m_pMyEnt->jumpnext = true;
  597.                          }
  598.                          else
  599.                               bToHigh = true;
  600.                     }
  601.                }
  602.                     
  603.                if (bToHigh)
  604.                {
  605.                     m_pHuntTarget = NULL;
  606.                     m_vPrevHuntLocation = m_vHuntLocation;
  607.                     m_vHuntLocation = g_vecZero;
  608.                     m_fPrevHuntDist = 0.0f;
  609.                     m_iHuntDelay = lastmillis + 3500;
  610.                     return false;
  611.                }                                        
  612.           }
  613.      }
  614.      
  615.      return true;
  616. }             
  617.      
  618. void CBot::ShootEnemy()
  619. {
  620.      if(!m_pMyEnt->enemy) return;
  621.          
  622.      m_iSawEnemyTime = lastmillis;
  623.           
  624.      // Aim to enemy
  625.      vec enemypos = GetEnemyPos(m_pMyEnt->enemy);
  626.      AimToVec(enemypos);
  627.      
  628.      // Time to shoot?
  629.      if (m_iShootDelay < lastmillis)
  630.      //if ((lastmillis-m_pMyEnt->lastaction) >= m_pMyEnt->gunwait)
  631.      {
  632.           if (m_pMyEnt->mag[m_pMyEnt->gunselect])
  633.           {
  634.                // If the bot is using a sniper only shoot if crosshair is near the enemy
  635.                if (WeaponInfoTable[m_pMyEnt->gunselect].eWeaponType == TYPE_SNIPER)
  636.                {
  637.                     float yawtoturn = fabs(WrapYZAngle(m_pMyEnt->yaw - m_pMyEnt->targetyaw));
  638.                     float pitchtoturn = fabs(WrapYZAngle(m_pMyEnt->pitch - m_pMyEnt->targetpitch));
  639.                
  640.                     if ((yawtoturn > 5) || (pitchtoturn > 15)) // UNDONE: Should be skill based
  641.                          return;
  642.                }
  643.  
  644.                float flDist = GetDistance(enemypos);
  645.                
  646.                // Check if bot is in fire range
  647.                if ((flDist < WeaponInfoTable[m_pMyEnt->gunselect].flMinFireDistance) ||
  648.                    (flDist > WeaponInfoTable[m_pMyEnt->gunselect].flMaxFireDistance))
  649.                     return;
  650.                     
  651.                // Now shoot!
  652.                m_pMyEnt->attacking = true;
  653.                
  654.                // Get the position the bot is aiming at
  655.                vec forward, right, up, dest;
  656.                traceresult_s tr;
  657.                
  658.                AnglesToVectors(GetViewAngles(), forward, right, up);
  659.                
  660.                dest = m_pMyEnt->o;
  661.                forward.mul(1000);
  662.                dest.add(forward);
  663.                
  664.                TraceLine(m_pMyEnt->o, dest, m_pMyEnt, false, &tr);
  665.                debugbeam(m_pMyEnt->o, tr.end);
  666.                
  667.                // Shoot 
  668.                shoot(m_pMyEnt, tr.end);
  669.                
  670.                // Add shoot delay
  671.                m_iShootDelay = lastmillis + GetShootDelay();               
  672.           }
  673.           ChoosePreferredWeapon(); 
  674.      }
  675. #ifndef RELEASE_BUILD     
  676.      else
  677.      {
  678.           char sz[64];
  679.           sprintf(sz, "shootdelay: %d\n", (m_iShootDelay-lastmillis));
  680.           AddDebugText(sz);
  681.      }
  682. #endif     
  683. }
  684.  
  685. bool CBot::ChoosePreferredWeapon()
  686. {
  687.      TMultiChoice<int> WeaponChoices;
  688.      short sWeaponScore;
  689.      float flDist = GetDistance(m_pMyEnt->enemy->o);
  690.      
  691.      if ((m_iChangeWeaponDelay > lastmillis) && (m_pMyEnt->ammo[m_pMyEnt->gunselect]))
  692.      {
  693.           if ((WeaponInfoTable[m_pMyEnt->gunselect].eWeaponType != TYPE_MELEE) || (flDist <= 3.5f))
  694.                return true;
  695.      }
  696.             
  697.      // Choose a weapon
  698.      for(int i=0;i<MAX_WEAPONS;i++)
  699.      {
  700.           // If no ammo for this weapon, skip it
  701.           if (m_pMyEnt->ammo[i] == 0) continue;
  702.           
  703.           sWeaponScore = 5; // Minimal score for a weapon
  704.           
  705.           if ((flDist >= WeaponInfoTable[i].flMinDesiredDistance) &&
  706.               (flDist <= WeaponInfoTable[i].flMaxDesiredDistance))
  707.           {
  708.                // In desired range for this weapon
  709.                sWeaponScore += 5; // Increase score much
  710.           }
  711.           else if ((flDist < WeaponInfoTable[i].flMinFireDistance) ||
  712.                    (flDist > WeaponInfoTable[i].flMaxFireDistance))
  713.                continue; // Wrong distance for this weapon
  714.                
  715.           // The ideal distance would be between the Min and Max desired distance.
  716.           // Score on the difference of the avarage of the Min and Max desired distance.
  717.           float flAvarage = (WeaponInfoTable[i].flMinDesiredDistance +
  718.                              WeaponInfoTable[i].flMaxDesiredDistance) / 2.0f;
  719.           float flIdealDiff = fabs(flDist - flAvarage);
  720.           
  721.           if (flIdealDiff < 0.5f) // Close to ideal distance
  722.                sWeaponScore += 4;
  723.           else if (flIdealDiff <= 1.0f)
  724.                sWeaponScore += 2;
  725.                
  726.           // Now rate the weapon on available ammo...
  727.           if (WeaponInfoTable[i].sMinDesiredAmmo > 0)
  728.           {
  729.                // Calculate how much percent of the min desired ammo the bot has
  730.                float flDesiredPercent = (float(m_pMyEnt->ammo[i]) /
  731.                                          float(WeaponInfoTable[i].sMinDesiredAmmo)) *
  732.                                          100.0f;
  733.                                          
  734.                if (flDesiredPercent >= 400.0f)
  735.                     sWeaponScore += 4;
  736.                else if (flDesiredPercent >= 200.0f)
  737.                     sWeaponScore += 3;
  738.                else if (flDesiredPercent >= 100.0f)
  739.                     sWeaponScore += 1;
  740.           }
  741.           
  742.           WeaponChoices.Insert(i, sWeaponScore);
  743.      }   
  744.      
  745.      int WeaponSelect;
  746.      if (WeaponChoices.GetSelection(WeaponSelect))
  747.      {
  748.           m_iChangeWeaponDelay = lastmillis + RandomLong(2000, 8000);
  749.           m_bShootAtFeet = ((WeaponInfoTable[WeaponSelect].eWeaponType==TYPE_ROCKET) &&
  750.                             (RandomLong(1, 100) <=
  751.                              m_pBotSkill->sShootAtFeetWithRLPercent));
  752.           return SelectGun(WeaponSelect);
  753.      }
  754.      
  755.      return false;
  756. }
  757.      
  758. int CBot::GetShootDelay()
  759. {
  760.      // UNDONE
  761.      if ((WeaponInfoTable[m_pMyEnt->gunselect].eWeaponType == TYPE_MELEE) ||
  762.          (WeaponInfoTable[m_pMyEnt->gunselect].eWeaponType == TYPE_AUTO))
  763.           return m_pMyEnt->gunwait;
  764.      
  765.      float flMinShootDelay = m_pBotSkill->flMinAttackDelay;
  766.      float flMaxShootDelay = m_pBotSkill->flMaxAttackDelay;
  767.      return max(m_pMyEnt->gunwait, int(RandomFloat(flMinShootDelay, flMaxShootDelay) * 1000.0f));
  768. }
  769.  
  770. void CBot::CheckReload() // reload gun if no enemies are around
  771. {
  772.     if(m_pMyEnt->enemy) return;
  773.     SelectGun(m_pMyEnt->primary);
  774.     reload(m_pMyEnt);
  775.     return;
  776. }
  777.  
  778. void CBot::MainAI()
  779. {
  780.      // Default bots will run forward
  781.      m_pMyEnt->move = 1;
  782.         
  783.      // Default bots won't strafe
  784.      m_pMyEnt->strafe = 0;
  785.         
  786.      if (!BotManager.BotsShoot() && m_pMyEnt->enemy)
  787.           m_pMyEnt->enemy = NULL; // Clear enemy when bots may not shoot
  788.              
  789.      if (m_bGoToDebugGoal) // For debugging the waypoint navigation
  790.      {
  791.           if (!HeadToGoal())
  792.           {
  793.                ResetWaypointVars();
  794.                m_vGoal = g_vecZero;
  795.           }
  796.           else
  797.                AddDebugText("Heading to debug goal...");
  798.      }
  799.      if (BotManager.BotsShoot() && FindEnemy()) // Combat
  800.      {
  801.           AddDebugText("has enemy");
  802.                     
  803.           // Shoot at enemy
  804.           ShootEnemy();
  805.           
  806.           if (m_eCurrentBotState != STATE_ENEMY)
  807.           {
  808.                m_vGoal = g_vecZero;
  809.                ResetWaypointVars();
  810.           }
  811.                
  812.           m_eCurrentBotState = STATE_ENEMY;
  813.           if (!CheckJump())
  814.                DoCombatNav();
  815.      }
  816.      else if (CheckHunt() && HuntEnemy())
  817.      {
  818.           CheckReload();
  819.           AddDebugText("Hunting to %s", m_pHuntTarget->name);
  820.           m_eCurrentBotState = STATE_HUNT;
  821.      }
  822.      // Heading to an interesting entity(ammo, armour etc)
  823.      else if (CheckItems()) 
  824.      {
  825.           CheckReload();
  826.           AddDebugText("has ent");
  827.           m_eCurrentBotState = STATE_ENT;
  828.      }
  829.      else if (m_classicsp && DoSPStuff()) // Home to goal, find/follow friends etc.
  830.      {
  831.           
  832.           AddDebugText("SP stuff");
  833.           m_eCurrentBotState = STATE_SP;
  834.      }
  835.      else // Normal navigation
  836.      {
  837.           CheckReload();
  838.           if (m_eCurrentBotState != STATE_NORMAL)
  839.           {
  840.                m_vGoal = g_vecZero;
  841.                ResetWaypointVars();
  842.           }
  843.           
  844.           m_eCurrentBotState = STATE_NORMAL;
  845.           bool bDoNormalNav = true;
  846.                     
  847.           AddDebugText("normal nav");
  848.           
  849.           // Make sure the bot looks straight forward and not up or down
  850.           m_pMyEnt->pitch = 0;
  851.           
  852.           // if it is time to look for a waypoint AND if there are waypoints in this
  853.           // level...
  854.           if (WaypointClass.m_iWaypointCount >= 1)
  855.           {
  856.                // check if we need to find a waypoint...
  857.                if (CurrentWPIsValid() == false)
  858.                {
  859.                     if (m_iLookForWaypointTime <= lastmillis)
  860.                     {
  861.                          // find the nearest reachable waypoint
  862.                          waypoint_s *pWP = GetNearestWaypoint(10.0f);
  863.  
  864.                          if (pWP && (pWP != m_pCurrentWaypoint))
  865.                          {
  866.                               SetCurrentWaypoint(pWP);
  867.                               condebug("New nav wp");
  868.                               bDoNormalNav = !HeadToWaypoint();
  869.                               if (bDoNormalNav)
  870.                                    ResetWaypointVars();
  871.                          }
  872.                          else
  873.                               ResetWaypointVars();
  874.                          
  875.                          m_iLookForWaypointTime = lastmillis + 250;
  876.                     }
  877.                }
  878.                else
  879.                {
  880.                     bDoNormalNav = !HeadToWaypoint();
  881.                     if (bDoNormalNav)
  882.                          ResetWaypointVars();                    
  883.                     AddDebugText("Using wps for nav");
  884.                }
  885.           }
  886.           
  887.           // If nothing special, do regular (waypointless) navigation
  888.           if(bDoNormalNav)
  889.           {    
  890.                // Is the bot underwater?
  891.                if (UnderWater(m_pMyEnt->o) && WaterNav())
  892.                {
  893.                     // Bot is under water, navigation happens in WaterNav
  894.                }
  895.                // Time to check the environment?
  896.                else if (m_iCheckEnvDelay < lastmillis)
  897.                {
  898.                     if (m_vWaterGoal!=g_vecZero) m_vWaterGoal = g_vecZero;
  899.                     
  900.                     // Check for stuck and strafe
  901.                     if (UnderWater(m_pMyEnt->o) || !CheckStuck())
  902.                     {
  903.                          // Only do this when the bot is underwater or when the bot isn't stuck
  904.                          
  905.                          // Check field of view (FOV)
  906.                          CheckFOV();
  907.                     }
  908.                }
  909.                          
  910.                // Check if the bot has to strafe
  911.                CheckStrafe();
  912.                
  913.                m_pMyEnt->move = 1;
  914.           }         
  915.      }
  916. }
  917.  
  918. void CBot::DoCombatNav()
  919. {          
  920.      if (m_iCombatNavTime > lastmillis)
  921.      {
  922.           // If bot has a lower skill and has to turn much, wait
  923.           if ((m_sSkillNr > 2) && (m_fYawToTurn > 90.0f))
  924.           {
  925.                ResetMoveSpeed();
  926.           }
  927.           else
  928.           {
  929.                SetMoveDir(m_iMoveDir, false);
  930.           }
  931.           return;
  932.      }
  933.      
  934.      if (m_bCombatJump)
  935.      {
  936.           m_pMyEnt->jumpnext = true;
  937.           m_bCombatJump = false;
  938.           m_iCombatJumpDelay = lastmillis + RandomLong(1500, 2800);
  939.           return;
  940.      }
  941.      
  942.      m_iMoveDir = DIR_NONE;
  943.  
  944.      // Check if bot is on top of his enemy
  945.      float r = m_pMyEnt->radius+m_pMyEnt->enemy->radius;
  946.      if ((fabs(m_pMyEnt->enemy->o.x-m_pMyEnt->o.x)<r &&
  947.           fabs(m_pMyEnt->enemy->o.y-m_pMyEnt->o.y)<r) &&
  948.           ((m_pMyEnt->enemy->o.z+m_pMyEnt->enemy->aboveeye) < (m_pMyEnt->o.z + m_pMyEnt->aboveeye)))
  949.      {
  950.           // Try to get off him!
  951.           condebug("On enemy!");
  952.           TMultiChoice<int> AwayDirChoices;
  953.           
  954.           if (IsVisible(LEFT, 4.0f, false))
  955.                AwayDirChoices.Insert(LEFT);
  956.           if (IsVisible(RIGHT, 4.0f, false))
  957.                AwayDirChoices.Insert(RIGHT);
  958.           if (IsVisible(FORWARD, 4.0f, false))
  959.                AwayDirChoices.Insert(FORWARD);
  960.           if (IsVisible(BACKWARD, 4.0f, false))
  961.                AwayDirChoices.Insert(BACKWARD);
  962.           
  963.           int iDir;
  964.           if (AwayDirChoices.GetSelection(iDir))
  965.           {
  966.                m_iMoveDir = iDir;
  967.                m_iCombatNavTime = lastmillis + 500;
  968.           }
  969.      }
  970.      
  971.      float flDist = GetDistance(m_pMyEnt->enemy->o);
  972.  
  973.      // Check for nearby items?
  974.      if (((m_iCheckEntsDelay < lastmillis) || m_pTargetEnt) &&
  975.          m_pBotSkill->bCanSearchItemsInCombat)
  976.      {
  977.           m_iCheckEntsDelay = lastmillis + 125;
  978.           bool bSearchItems = false;
  979.           
  980.           if (m_pTargetEnt)
  981.           {
  982.                // Bot has already found an entity, still valid?
  983.                vec v(m_pTargetEnt->x, m_pTargetEnt->y,
  984.                          S(m_pTargetEnt->x, m_pTargetEnt->y)->floor+m_pMyEnt->eyeheight);
  985.                if ((GetDistance(v) > 25.0f) || !IsVisible(m_pTargetEnt))
  986.                     m_pTargetEnt = NULL;
  987.           }
  988.           
  989.           if (!m_pTargetEnt && (m_iCheckEntsDelay <= lastmillis))
  990.           {
  991.                if (WeaponInfoTable[m_pMyEnt->gunselect].eWeaponType == TYPE_MELEE)
  992.                     bSearchItems = (flDist >= 8.0f);
  993.                else
  994.                     bSearchItems = (m_pMyEnt->ammo[m_pMyEnt->gunselect] <=
  995.                                     WeaponInfoTable[m_pMyEnt->gunselect].sMinDesiredAmmo);
  996.                
  997.                if (bSearchItems)
  998.                     m_pTargetEnt = SearchForEnts(false, 25.0f, 1.0f);
  999.           }
  1000.                     
  1001.           if (m_pTargetEnt)
  1002.           {
  1003.                condebug("Combat ent");
  1004.                vec v(m_pTargetEnt->x, m_pTargetEnt->y,
  1005.                          S(m_pTargetEnt->x, m_pTargetEnt->y)->floor+m_pMyEnt->eyeheight);
  1006.                
  1007.                debugbeam(m_pMyEnt->o, v);
  1008.  
  1009.                float flHeightDiff = v.z - m_pMyEnt->o.z;
  1010.                bool bToHigh = false;
  1011.                
  1012.                // Check he height for this ent
  1013.                if (Get2DDistance(v) <= 2.0f)
  1014.                {
  1015.                     if (flHeightDiff >= 1.5f)
  1016.                     {
  1017.                          if (flHeightDiff <= JUMP_HEIGHT)
  1018.                          {
  1019. #ifndef RELEASE_BUILD                    
  1020.                               char sz[64];
  1021.                               sprintf(sz, "Ent z diff: %f", v.z-m_pMyEnt->o.z);
  1022.                               condebug(sz);
  1023. #endif                         
  1024.                               m_pMyEnt->jumpnext = true; // Jump if close to ent and the ent is high
  1025.                          }
  1026.                          else
  1027.                               bToHigh = true;
  1028.                     }
  1029.                }
  1030.                     
  1031.                if (!bToHigh)
  1032.                {              
  1033.                     int iMoveDir = GetDirection(GetViewAngles(), m_pMyEnt->o, v);
  1034.                                              
  1035.                     if (iMoveDir != DIR_NONE)
  1036.                     {
  1037.                          m_iMoveDir = iMoveDir;
  1038.                          m_iCombatNavTime = lastmillis + RandomLong(125, 250);
  1039.                     }
  1040.                               
  1041.                     // Check if bot needs to jump over something
  1042.                     vec from = m_pMyEnt->o;
  1043.                     from.z -= 1.0f;
  1044.                     if (!IsVisible(from, iMoveDir, 3.0f, false))
  1045.                          m_pMyEnt->jumpnext = true;
  1046.                          
  1047.                     return;
  1048.                }          
  1049.           }
  1050.      }
  1051.                
  1052.      // High skill and enemy is close?
  1053.      if ((m_sSkillNr <= 1) && (m_fYawToTurn < 80.0f) && (flDist <= 20.0f) &&
  1054.          (m_iCombatJumpDelay < lastmillis))
  1055.      {
  1056.           // Randomly jump a bit, to avoid some basic firepower ;)
  1057.           
  1058.           // How lower the distance to the enemy, how higher the chance for a jump
  1059.           short sJumpPercent = (100 - ((short)flDist * 8));
  1060.           if (RandomLong(1, 100) <= sJumpPercent)
  1061.           {
  1062.                // Choose a nice direction to jump to
  1063.                
  1064.                // Is the enemy close?
  1065.                if ((GetDistance(m_pMyEnt->enemy->o) <= 4.0f) ||
  1066.                    (WeaponInfoTable[m_pMyEnt->gunselect].eWeaponType == TYPE_MELEE))
  1067.                {
  1068.                     m_iMoveDir = FORWARD; // Jump forward
  1069.                     SetMoveDir(FORWARD, false);
  1070.                     m_bCombatJump = true;
  1071.                }
  1072.                else if (WeaponInfoTable[m_pMyEnt->gunselect].eWeaponType != TYPE_MELEE)// else jump to a random direction
  1073.                {
  1074.                     /* 
  1075.                          Directions to choose:
  1076.                          - Forward-right
  1077.                          - Right
  1078.                          - Backward-right
  1079.                          - Backward
  1080.                          - Backward-left
  1081.                          - Left
  1082.                          - Forward-left
  1083.                          
  1084.                     */
  1085.                     
  1086.                     TMultiChoice<int> JumpDirChoices;
  1087.                     short sForwardScore = ((flDist > 8.0f) || (flDist < 4.0f)) ? 20 : 10;
  1088.                     short sBackwardScore = (flDist <= 6.0f) ? 20 : 10;
  1089.                     short sStrafeScore = (flDist < 6.0f) ? 20 : 10;
  1090.                     
  1091.                     if (IsVisible((FORWARD | LEFT), 4.0f, false))
  1092.                          JumpDirChoices.Insert((FORWARD | LEFT), sForwardScore);
  1093.                     if (IsVisible((FORWARD | RIGHT), 4.0f, false))
  1094.                          JumpDirChoices.Insert((FORWARD | RIGHT), sForwardScore);
  1095.                     if (IsVisible(BACKWARD, 4.0f, false))
  1096.                          JumpDirChoices.Insert(BACKWARD, sBackwardScore);
  1097.                     if (IsVisible((BACKWARD | LEFT), 4.0f, false))
  1098.                          JumpDirChoices.Insert((BACKWARD | LEFT), sBackwardScore);
  1099.                     if (IsVisible((BACKWARD | RIGHT), 4.0f, false))
  1100.                          JumpDirChoices.Insert((BACKWARD | RIGHT), sBackwardScore);
  1101.                     if (IsVisible(LEFT, 4.0f, false))
  1102.                          JumpDirChoices.Insert(LEFT, sStrafeScore);
  1103.                     if (IsVisible(RIGHT, 4.0f, false))
  1104.                          JumpDirChoices.Insert(RIGHT, sStrafeScore);
  1105.                     
  1106.                     int JumpDir;
  1107.                     if (JumpDirChoices.GetSelection(JumpDir))
  1108.                     {
  1109.                          m_iMoveDir = JumpDir;
  1110.                          SetMoveDir(JumpDir, false);
  1111.                          m_bCombatJump = true;
  1112.                     }
  1113.                }
  1114.                
  1115.                if (m_bCombatJump)
  1116.                {
  1117.                     m_iCombatNavTime = lastmillis + RandomLong(125, 250);
  1118.                     return;
  1119.                }
  1120.           }
  1121.      }
  1122.      
  1123.      if (WeaponInfoTable[m_pMyEnt->gunselect].eWeaponType == TYPE_MELEE)
  1124.           return; // Simply walk towards enemy if using a melee type
  1125.      
  1126.      flDist = Get2DDistance(m_pMyEnt->enemy->o);
  1127.      
  1128.      // Out of desired range for current weapon?
  1129.      if ((flDist <= WeaponInfoTable[m_pMyEnt->gunselect].flMinDesiredDistance) ||
  1130.          (flDist >= WeaponInfoTable[m_pMyEnt->gunselect].flMaxDesiredDistance))
  1131.      {
  1132.           if (flDist >= WeaponInfoTable[m_pMyEnt->gunselect].flMaxDesiredDistance)
  1133.           {
  1134.                m_iMoveDir = FORWARD;
  1135.           }
  1136.           else
  1137.           {
  1138.                m_iMoveDir = BACKWARD;
  1139.           }
  1140.      
  1141.           vec src, forward, right, up, dest, MyAngles = GetViewAngles(), o = m_pMyEnt->o;
  1142.           traceresult_s tr;
  1143.      
  1144.           // Is it furthest or farthest? bleh
  1145.           float flFurthestDist = 0;
  1146.           int bestdir = -1, dir = 0;
  1147.           bool moveback = (m_pMyEnt->move == -1);
  1148.      
  1149.           for(int j=-45;j<=45;j+=45)
  1150.           {
  1151.                src = MyAngles;
  1152.                src.y = WrapYZAngle(src.y + j);
  1153.                src.x = 0.0f;
  1154.           
  1155.                // If we're moving backwards, trace backwards
  1156.                if (moveback)
  1157.                     src.y = WrapYZAngle(src.y + 180);
  1158.           
  1159.                AnglesToVectors(src, forward, right, up);
  1160.           
  1161.                dest = o;
  1162.                forward.mul(40);
  1163.                dest.add(forward);
  1164.           
  1165.                TraceLine(o, dest, m_pMyEnt, false, &tr);
  1166.           
  1167.                //debugbeam(origin, end);
  1168.                flDist = GetDistance(tr.end);
  1169.           
  1170.                if (flFurthestDist < flDist)
  1171.                {
  1172.                     flFurthestDist = flDist;
  1173.                     bestdir = dir;
  1174.                }
  1175.                dir++;
  1176.           }
  1177.      
  1178.           switch(bestdir)
  1179.           {
  1180.           case 0:
  1181.                if (moveback)
  1182.                     m_iMoveDir |= RIGHT; // Strafe right
  1183.                else
  1184.                     m_iMoveDir |= LEFT; // Strafe left
  1185.                break;
  1186.           case 2:
  1187.                if (moveback)
  1188.                     m_iMoveDir |= LEFT; // Strafe left
  1189.                else
  1190.                     m_iMoveDir |= RIGHT; // Strafe right
  1191.                break;
  1192.           }
  1193.           
  1194.           if (m_iMoveDir != DIR_NONE)
  1195.           {
  1196.                SetMoveDir(m_iMoveDir, false);
  1197.                m_iCombatNavTime = lastmillis + 500;
  1198.           }
  1199.      }
  1200.      else if (m_pBotSkill->bCircleStrafe) // Circle strafe when in desired range...
  1201.      {
  1202.           traceresult_s tr;
  1203.           vec angles, end, forward, right, up;
  1204.           TMultiChoice<int> StrafeDirChoices;
  1205.           
  1206.           // Check the left side...
  1207.           angles = GetViewAngles();
  1208.           angles.y = WrapYZAngle(angles.y - 75.0f); // Not 90 degrees because the bot
  1209.                                                     // doesn't strafe in a straight line
  1210.                                                     // (aims still to enemy).
  1211.           
  1212.           AnglesToVectors(angles, forward, right, up);
  1213.           end = m_pMyEnt->o;
  1214.           forward.mul(15.0f);
  1215.           end.add(forward);
  1216.           
  1217.           TraceLine(m_pMyEnt->o, end, m_pMyEnt, true, &tr);
  1218.           StrafeDirChoices.Insert(LEFT, (int)GetDistance(m_pMyEnt->o, tr.end));
  1219.           
  1220.           // Check the right side...
  1221.           angles = GetViewAngles();
  1222.           angles.y = WrapYZAngle(angles.y + 75.0f); // Not 90 degrees because the bot
  1223.                                                     // doesn't strafe in a straight line
  1224.                                                     // (aims still to enemy).
  1225.           
  1226.           AnglesToVectors(angles, forward, right, up);
  1227.           end = m_pMyEnt->o;
  1228.           forward.mul(15.0f);
  1229.           end.add(forward);
  1230.           
  1231.           TraceLine(m_pMyEnt->o, end, m_pMyEnt, true, &tr);
  1232.           StrafeDirChoices.Insert(RIGHT, (int)GetDistance(m_pMyEnt->o, tr.end));
  1233.      
  1234.           int StrafeDir;
  1235.           if (StrafeDirChoices.GetSelection(StrafeDir))
  1236.           {
  1237.                m_iMoveDir = StrafeDir;
  1238.                SetMoveDir(StrafeDir, false);
  1239.                m_iCombatNavTime = lastmillis + RandomLong(1500, 3000);               
  1240.           }
  1241.      }
  1242.      else // Bot can't circle strafe(low skill), just stand still
  1243.           ResetMoveSpeed();
  1244. }
  1245.  
  1246. bool CBot::CheckStuck()
  1247. {
  1248.      if (m_iStuckCheckDelay >= lastmillis)
  1249.           return false;
  1250.  
  1251.      if ((m_vGoal!=g_vecZero) && (GetDistance(m_vGoal) < 2.0f))
  1252.           return false;
  1253.                     
  1254.      bool IsStuck = false;
  1255.      
  1256.      vec CurPos = m_pMyEnt->o, PrevPos = m_vPrevOrigin;
  1257.      CurPos.z = PrevPos.z = 0;
  1258.      // Did the bot hardly move the last frame?
  1259.      if (GetDistance(CurPos, PrevPos) <= 0.1f)
  1260.      {
  1261.           if (m_bStuck)
  1262.           {
  1263.                if (m_iStuckTime < lastmillis)
  1264.                     IsStuck = true;
  1265.           }
  1266.           else
  1267.           {
  1268.                m_bStuck = true;
  1269.                m_iStuckTime = lastmillis + 1000;
  1270.           }
  1271.      }
  1272.      else
  1273.      {
  1274.           m_bStuck = false;
  1275.           m_iStuckTime = 0;
  1276.      }
  1277.      
  1278.      if (IsStuck)          
  1279.      {
  1280. #ifndef RELEASE_BUILD     
  1281.           char msg[64];
  1282.           sprintf(msg, "stuck (%f)", GetDistance(m_vPrevOrigin));
  1283.           condebug(msg);
  1284. #endif
  1285.           
  1286.           m_bStuck = false;
  1287.           m_iStuckTime = 0;
  1288.           
  1289.           // Crap bot is stuck, lets just try some random things
  1290.                         
  1291.           // Check if the bot can turn around
  1292.           vec src = GetViewAngles();
  1293.           src.x = 0;
  1294.           vec forward, right, up, dir, dest;
  1295.           traceresult_s tr;
  1296.           
  1297.           AnglesToVectors(src, forward, right, up);
  1298.           
  1299.           // Check the left side...
  1300.           dir = right;
  1301.           dest = m_pMyEnt->o;
  1302.           dir.mul(3);
  1303.           dest.sub(dir);
  1304.           
  1305.           TraceLine(m_pMyEnt->o, dest, m_pMyEnt, false, &tr);
  1306.           //debugbeam(m_pMyEnt->o, end);
  1307.           
  1308.           if (!tr.collided)
  1309.           {
  1310.                // Bot can turn left, do so
  1311.                m_pMyEnt->targetyaw = WrapYZAngle(m_pMyEnt->yaw - 90);
  1312.                m_iStuckCheckDelay = m_iCheckEnvDelay = lastmillis + 500;
  1313.                return true;
  1314.           }
  1315.           
  1316.           // Check the right side...
  1317.           dir = right;
  1318.           dest = m_pMyEnt->o;
  1319.           dir.mul(3);
  1320.           dest.add(dir);
  1321.           
  1322.           TraceLine(m_pMyEnt->o, dest, m_pMyEnt, true, &tr);
  1323.           //debugbeam(m_pMyEnt->o, end);
  1324.           
  1325.           if (!tr.collided)
  1326.           {
  1327.                // Bot can turn right, do so
  1328.                m_pMyEnt->targetyaw = WrapYZAngle(m_pMyEnt->yaw + 90);
  1329.                m_iStuckCheckDelay = m_iCheckEnvDelay = lastmillis + 500;
  1330.                return true;
  1331.           }
  1332.           
  1333.           // Check if bot can turn 180 degrees
  1334.           dir = forward;
  1335.           dest = m_pMyEnt->o;
  1336.           dir.mul(3);
  1337.           dest.add(dir);
  1338.           
  1339.           TraceLine(m_pMyEnt->o, dest, m_pMyEnt, true, &tr);
  1340.           //debugbeam(m_pMyEnt->o, end);
  1341.           
  1342.           if (!tr.collided)
  1343.           {
  1344.                // Bot can turn around, do so
  1345.                m_pMyEnt->targetyaw = WrapYZAngle(m_pMyEnt->yaw + 180);
  1346.                m_iStuckCheckDelay = m_iCheckEnvDelay = lastmillis + 500;
  1347.                return true;
  1348.           }
  1349.           
  1350.           // Bleh bot couldn't turn, lets just randomly jump :|
  1351.           
  1352.           condebug("Randomly avoiding stuck...");
  1353.           if (RandomLong(0, 2) == 0)
  1354.                m_pMyEnt->jumpnext = true;
  1355.           else
  1356.                m_pMyEnt->targetyaw = WrapYZAngle(m_pMyEnt->yaw + RandomLong(60, 160));
  1357.           return true;
  1358.      }
  1359.      
  1360.      return false;
  1361. }
  1362.  
  1363. // Check if a near wall is blocking and we can jump over it
  1364. bool CBot::CheckJump()
  1365. {
  1366.      bool bHasGoal = m_vGoal!=g_vecZero;
  1367.      float flGoalDist = (bHasGoal) ? GetDistance(m_vGoal) : 0.0f;
  1368.      
  1369. //     if ((bHasGoal) && (flGoalDist < 2.0f))
  1370.   //        return false; UNDONE?
  1371.           
  1372.      vec start = m_pMyEnt->o;
  1373.      float flTraceDist = 3.0f;
  1374.      
  1375.      if (bHasGoal && (flGoalDist < flTraceDist))
  1376.           flTraceDist = flGoalDist;
  1377.  
  1378.      // Something blocks at eye hight?
  1379.      if (!IsVisible(start, FORWARD, flTraceDist, false))
  1380.      {
  1381.           // Check if the bot can jump over it
  1382.           start.z += (JUMP_HEIGHT - 1.0f);
  1383.           if (IsVisible(start) && !IsVisible(start, FORWARD, flTraceDist, false))
  1384.           {
  1385.                // Jump
  1386.                debugnav("High wall");
  1387.                m_pMyEnt->jumpnext = true;
  1388.                return true;
  1389.           }
  1390.      }
  1391.      else
  1392.      {
  1393.           // Check if something is blocking at feet height, so the bot can jump over it
  1394.           start.z -= 1.7f;
  1395.           
  1396.           // Trace was blocked?
  1397.           if (!IsVisible(start, FORWARD, flTraceDist, false))
  1398.           {
  1399.                //debugbeam(start, end);
  1400.           
  1401.                // Jump
  1402.                debugnav("Low wall");
  1403.                m_pMyEnt->jumpnext = true;
  1404.                return true;
  1405.           }
  1406.      }
  1407.      
  1408.      return false; // Bot didn't had to jump(or couldn't)
  1409. }
  1410.      
  1411. bool CBot::CheckStrafe()
  1412. {
  1413.      if (m_iStrafeTime >= lastmillis)
  1414.      {
  1415.           SetMoveDir(m_iMoveDir, true);
  1416.           return true;
  1417.      }
  1418.      
  1419.      if (m_iStrafeCheckDelay >= lastmillis)
  1420.           return false;
  1421.           
  1422.      // Check for near walls
  1423.      traceresult_s tr;
  1424.      vec from = m_pMyEnt->o, to, forward, right, up, dir;
  1425.      float flLeftDist = -1.0f, flRightDist = -1.0f;
  1426.      bool bStrafe = false;
  1427.      int iStrafeDir = DIR_NONE;
  1428.      
  1429.      AnglesToVectors(GetViewAngles(), forward, right, up);
  1430.      
  1431.      // Check for a near left wall
  1432.      to = from;
  1433.      dir = right;
  1434.      dir.mul(3.0f);
  1435.      to.sub(dir);
  1436.      TraceLine(from, to, m_pMyEnt, false, &tr);
  1437.      if (tr.collided)
  1438.           flLeftDist = GetDistance(from, tr.end);
  1439.      //debugbeam(m_pMyEnt->o, to);
  1440.           
  1441.      // Check for a near right wall
  1442.      to = from;
  1443.      dir = right;
  1444.      dir.mul(3.0f);
  1445.      to.add(dir);
  1446.      TraceLine(from, to, m_pMyEnt, false, &tr);
  1447.      if (tr.collided)
  1448.           flRightDist = GetDistance(from, tr.end);
  1449.      //debugbeam(m_pMyEnt->o, to);
  1450.      
  1451.      if ((flLeftDist == -1.0f) && (flRightDist == -1.0f))
  1452.      {
  1453.           dir = right;
  1454.           dir.mul(m_pMyEnt->radius);
  1455.           
  1456.           // Check left
  1457.           from = m_pMyEnt->o;
  1458.           from.sub(dir);
  1459.           if (IsVisible(from, FORWARD, 3.0f, false, &flLeftDist))
  1460.                flLeftDist = -1.0f;
  1461.  
  1462.           // Check right
  1463.           from = m_pMyEnt->o;
  1464.           from.add(dir);
  1465.           if (IsVisible(from, FORWARD, 3.0f, false, &flRightDist))
  1466.                flRightDist = -1.0f;
  1467.      }
  1468.           
  1469.      if ((flLeftDist != -1.0f) && (flRightDist != -1.0f))
  1470.      {
  1471.           if (flLeftDist < flRightDist)
  1472.           {
  1473.                // Strafe right
  1474.                bStrafe = true;
  1475.                iStrafeDir = RIGHT;
  1476.           }
  1477.           else if (flRightDist < flLeftDist)
  1478.           {
  1479.                // Strafe left
  1480.                bStrafe = true;
  1481.                iStrafeDir = LEFT;
  1482.           }
  1483.           else
  1484.           {
  1485.                // Randomly choose a strafe direction
  1486.                bStrafe = true;
  1487.                if (RandomLong(0, 1))
  1488.                     iStrafeDir = LEFT;
  1489.                else
  1490.                     iStrafeDir = RIGHT;
  1491.           }
  1492.      }
  1493.      else if (flLeftDist != -1.0f)
  1494.      {
  1495.           // Strafe right
  1496.           bStrafe = true;
  1497.           iStrafeDir = RIGHT;
  1498.      }
  1499.      else if (flRightDist != -1.0f)
  1500.      {
  1501.           // Strafe left
  1502.           bStrafe = true;
  1503.           iStrafeDir = LEFT;
  1504.      }
  1505.      
  1506.      if (bStrafe)
  1507.      {
  1508.           SetMoveDir(iStrafeDir, true);
  1509.           m_iMoveDir = iStrafeDir;
  1510.           m_iStrafeTime = lastmillis + RandomLong(75, 150);
  1511.      }
  1512.  
  1513.      return bStrafe;
  1514. }
  1515.  
  1516. void CBot::CheckFOV()
  1517. {
  1518.      m_iCheckEnvDelay = lastmillis + RandomLong(125, 250);
  1519.      vec MyAngles = GetViewAngles();
  1520.      vec src, forward, right, up, dest, best(0, 0, 0);
  1521.      vec origin = m_pMyEnt->o;
  1522.      float flDist, flFurthestDist = 0;
  1523.      bool WallLeft = false;
  1524.      traceresult_s tr;
  1525.      
  1526.      //origin.z -= 1.5; // Slightly under eye level
  1527.              
  1528.      // Scan 90 degrees FOV
  1529.      for(int angle=-45;angle<=45;angle+=5)
  1530.      {
  1531.           src = MyAngles;
  1532.           src.y = WrapYZAngle(src.y + angle);
  1533.           
  1534.           AnglesToVectors(src, forward, right, up);
  1535.           
  1536.           dest = origin;
  1537.           forward.mul(40);
  1538.           dest.add(forward);
  1539.           
  1540.           TraceLine(origin, dest, m_pMyEnt, false, &tr);
  1541.           
  1542.           //debugbeam(origin, end);
  1543.           flDist = GetDistance(tr.end);
  1544.           
  1545.           if (flFurthestDist < flDist)
  1546.           {
  1547.                flFurthestDist = flDist;
  1548.                best = tr.end;
  1549.           }
  1550.      }
  1551.      
  1552.      if (best.x && best.y && best.z)
  1553.      {
  1554.           AimToVec(best);
  1555.           // Update MyAngles, since their (going to be) change(d)
  1556.           MyAngles.x = m_pMyEnt->targetpitch;
  1557.           MyAngles.y = m_pMyEnt->targetyaw;
  1558.      }
  1559.      
  1560.      float flNearestHitDist = GetDistance(best);
  1561.      
  1562.      if (!UnderWater(m_pMyEnt->o) && m_pMyEnt->onfloor)
  1563.      {
  1564.           // Check if a near wall is blocking and we can jump over it
  1565.           if (flNearestHitDist < 4)
  1566.           {
  1567.                // Check if the bot can jump over it
  1568.                src = MyAngles;
  1569.                src.x = 0;
  1570.      
  1571.                AnglesToVectors(src, forward, right, up);
  1572.      
  1573.                vec start = origin;
  1574.                start.z += 2.0f;
  1575.                dest = start;
  1576.                forward.mul(6);
  1577.                dest.add(forward);
  1578.           
  1579.                TraceLine(start, dest, m_pMyEnt, false, &tr);
  1580.                //debugbeam(start, end);
  1581.      
  1582.                if (!tr.collided)
  1583.                {
  1584.                     // Jump
  1585.                     debugnav("High wall");
  1586.                     m_pMyEnt->jumpnext = true;
  1587.                     m_iStrafeCheckDelay = lastmillis + RandomLong(250, 500);
  1588.                     return;
  1589.                }
  1590.           }
  1591.           else
  1592.           {
  1593.                // Check if something is blocking below us, so the bot can jump over it
  1594.                src = MyAngles;
  1595.                src.x = 0;
  1596.      
  1597.                AnglesToVectors(src, forward, right, up);
  1598.      
  1599.                vec start = origin;
  1600.                start.z -= 1.7f;
  1601.                dest = start;
  1602.                forward.mul(4);
  1603.                dest.add(forward);
  1604.           
  1605.                TraceLine(start, dest, m_pMyEnt, false, &tr);
  1606.      
  1607.                // Trace was blocked?
  1608.                if (tr.collided)
  1609.                {
  1610.                     //debugbeam(start, end);
  1611.           
  1612.                     // Jump
  1613.                     debugnav("Low wall");
  1614.                     m_pMyEnt->jumpnext = true;
  1615.                     m_iStrafeCheckDelay = lastmillis + RandomLong(250, 500);
  1616.                     return;
  1617.                }
  1618.           }
  1619.           
  1620.           // Check if the bot is going to fall...
  1621.           src = MyAngles;
  1622.           src.x = 0.0f;
  1623.           AnglesToVectors(src, forward, right, up);
  1624.      
  1625.           dest = origin;
  1626.           forward.mul(3.0f);
  1627.           dest.add(forward);
  1628.      
  1629.           TraceLine(origin, dest, m_pMyEnt, false, &tr);
  1630.      
  1631.           int cx = int(tr.end.x), cy = int(tr.end.y);
  1632.           short CubesInWater=0;
  1633.           for(int x=cx-1;x<=(cx+1);x++)
  1634.           {
  1635.                for(int y=cy-1;y<=(cy+1);y++)
  1636.                {
  1637.                     if (OUTBORD(x, y)) continue;
  1638.                     //sqr *s = S(fast_f2nat(x), fast_f2nat(y));
  1639.                     //if (!SOLID(s))
  1640.                     {
  1641.                          vec from(x, y, m_pMyEnt->o.z);
  1642.                          dest = from;
  1643.                          dest.z -= 6.0f;
  1644.                          TraceLine(from, dest, m_pMyEnt, false, &tr);
  1645.                          bool turn = false;
  1646.                          if (UnderWater(tr.end)) CubesInWater++;
  1647.                          if (CubesInWater > 2) turn = true; // Always avoid water
  1648.                          if (!tr.collided && RandomLong(0, 1))
  1649.                               turn = true; // Randomly avoid a fall
  1650.           
  1651.                          if (turn)
  1652.                          {
  1653.                               m_pMyEnt->targetyaw = WrapYZAngle(m_pMyEnt->yaw + 180);
  1654.                               m_iCheckEnvDelay = m_iStrafeCheckDelay = lastmillis + RandomLong(750, 1500);
  1655.                               debugnav("Water or a fall in front");
  1656.                               return;
  1657.                          }
  1658.                     }
  1659.                }
  1660.           }
  1661.      }
  1662.      
  1663.      // Is the bot about to head a corner?
  1664.      if (flNearestHitDist <= 4.0f)
  1665.      {
  1666.           src = MyAngles;
  1667.           src.y = WrapYZAngle(src.y - 45.0f);
  1668.           AnglesToVectors(src, forward, right, up);
  1669.      
  1670.           dest = origin;
  1671.           forward.mul(4.0f);
  1672.           dest.add(forward);
  1673.           
  1674.           TraceLine(origin, dest, m_pMyEnt, false, &tr);
  1675.      
  1676.           WallLeft = (tr.collided);
  1677.      
  1678.           src = MyAngles;
  1679.           src.y += WrapYZAngle(src.y + 45.0f);
  1680.           AnglesToVectors(src, forward, right, up);
  1681.      
  1682.           dest = origin;
  1683.           forward.mul(4.0f);
  1684.           dest.add(forward);
  1685.           
  1686.           TraceLine(origin, dest, m_pMyEnt, false, &tr);
  1687.      
  1688.           if (WallLeft && tr.collided)
  1689.           {
  1690.                // We're about to hit a corner, turn away
  1691.                debugnav("Corner");
  1692.                m_pMyEnt->targetyaw = WrapYZAngle(m_pMyEnt->yaw + RandomFloat(160.0f, 200.0f));
  1693.                m_iCheckEnvDelay = m_iStrafeCheckDelay = lastmillis + RandomLong(750, 1500);
  1694.                return;
  1695.           }
  1696.      }
  1697. }
  1698.  
  1699. // Called when bot is underwater
  1700. bool CBot::WaterNav()
  1701. {
  1702.      const int iSearchRange = 4;
  1703.      
  1704.      if (m_vWaterGoal==g_vecZero)
  1705.      {
  1706.           AddDebugText("WaterNav");
  1707.           // Find the nearest and reachable cube which isn't underwater
  1708.           
  1709.           int cx = int(m_pMyEnt->o.x);
  1710.           int cy = int(m_pMyEnt->o.y);
  1711.           float flNearestDist = 9999.0f, flDist;
  1712.           
  1713.           if (OUTBORD(cx, cy)) return false;
  1714.                    
  1715.           // Check all cubes in range...
  1716.           for (int x=(cx-iSearchRange);x<=(cx+iSearchRange);x++)
  1717.           {
  1718.                for (int y=(cy-iSearchRange);y<=(cy+iSearchRange);y++)
  1719.                {
  1720.                     sqr *s = S(x, y);
  1721.           
  1722.                     if (SOLID(s)) continue;
  1723.                     if ((x==cx) && (y==cy)) continue;
  1724.                
  1725.                     vec v(x, y, GetCubeFloor(x, y));
  1726.                     
  1727.                     if (UnderWater(v)) continue; // Skip, cube is underwater
  1728.                     
  1729.                     if (hdr.waterlevel < (v.z - 2.0f)) continue; // Cube is too high
  1730.                     
  1731.                     // Check if the bot 'can fit' on the cube(no near obstacles)
  1732.                     bool small = false;
  1733.                     for (int a=(x-2);a<=(x+2);a++)
  1734.                     {
  1735.                          if (small) break;
  1736.                          for (int b=(y-2);b<=(y+2);b++)
  1737.                          {
  1738.                               if ((x==a) && (y==b)) continue;
  1739.                               vec v2(a, b, GetCubeFloor(a, b));
  1740.                               if (v.z < (v2.z-1-JUMP_HEIGHT))
  1741.                               {
  1742.                                    small=true;
  1743.                                    break;
  1744.                               }
  1745.                          
  1746.                               if ((a >= (x-1)) && (a <= (x+1)) && (b >= (y-1)) && (b <= (y+1)))
  1747.                               {
  1748.                                    if ((v2.z) < (v.z-2.0f))
  1749.                                    {
  1750.                                         small = true;
  1751.                                         break;
  1752.                                    }
  1753.                               }
  1754.                          
  1755.                               traceresult_s tr;
  1756.                               TraceLine(v, v2, NULL, false, &tr);
  1757.                               if (tr.collided)
  1758.                               {
  1759.                                    small=true;
  1760.                                    break;
  1761.                               }
  1762.                          }
  1763.                          if (small) break;
  1764.                     }
  1765.                     if (small)
  1766.                     {
  1767.                          debugbeam(m_pMyEnt->o, v);
  1768.                          continue;
  1769.                     }
  1770.                     
  1771.                     // Okay, cube is valid.
  1772.                     flDist = GetDistance(v);
  1773.                     if (flDist < flNearestDist)
  1774.                     {
  1775.                          flNearestDist = flDist;
  1776.                          m_vWaterGoal = v;
  1777.                     }
  1778.                }
  1779.           }
  1780.      }
  1781.      
  1782.      if (m_vWaterGoal!=g_vecZero)
  1783.      {
  1784.           AddDebugText("WaterNav");
  1785.           //debugbeam(m_pMyEnt->o, m_vWaterGoal);
  1786.           vec aim = m_vWaterGoal;
  1787.           aim.z += 1.5f; // Aim a bit further up
  1788.           AimToVec(aim);
  1789.           if ((RandomLong(1, 100) <= 15) && (Get2DDistance(m_vWaterGoal) <= 7.0f))
  1790.                m_pMyEnt->jumpnext = true;
  1791.           return true;
  1792.      }
  1793.           
  1794.      return false;
  1795. }
  1796.           
  1797. bool CBot::CheckItems()
  1798. {          
  1799.      if (!m_pCurrentGoalWaypoint && !CheckJump() && CheckStuck())
  1800.      {
  1801.           // Don't check for ents a while when stuck                   
  1802.           m_iCheckEntsDelay = lastmillis + RandomLong(1000, 2000);
  1803.           return false;
  1804.      }
  1805.      
  1806.      if (m_vGoal==g_vecZero)
  1807.           m_pTargetEnt = NULL;
  1808.  
  1809.      if (!m_pTargetEnt)
  1810.      {
  1811.           if (m_iCheckEntsDelay > lastmillis)
  1812.                return false;
  1813.           else
  1814.           {
  1815.                m_pTargetEnt = SearchForEnts(!m_classicsp);
  1816.                m_iCheckEntsDelay = lastmillis + RandomLong(2500, 5000);
  1817.           }
  1818.      }
  1819.                   
  1820.      if (m_pTargetEnt)
  1821.      {
  1822.           if (HeadToTargetEnt())
  1823.                return true;
  1824.      }
  1825.      
  1826.      if (m_eCurrentBotState == STATE_ENT)
  1827.      {
  1828.           ResetWaypointVars();
  1829.           m_vGoal = g_vecZero;
  1830.           m_pTargetEnt = NULL;
  1831.      }
  1832.      
  1833.      return false;
  1834. }          
  1835.  
  1836. bool CBot::InUnreachableList(entity *e)
  1837. {
  1838.      TLinkedList<unreachable_ent_s *>::node_s *p = m_UnreachableEnts.GetFirst();
  1839.      while(p)
  1840.      {
  1841.           if (p->Entry->ent == e) return true;
  1842.           p = p->next;
  1843.      }
  1844.      return false;
  1845. }
  1846.  
  1847. bool CBot::IsReachable(vec to, float flMaxHeight)
  1848. {
  1849.      vec from = m_pMyEnt->o;
  1850.      traceresult_s tr;
  1851.      float curr_height, last_height;
  1852.  
  1853.      float distance = GetDistance(from, to);
  1854.  
  1855.      // is the destination close enough?
  1856.      //if (distance < REACHABLE_RANGE)
  1857.      {
  1858.           if (IsVisible(to))
  1859.           {
  1860.                // Look if bot can 'fit trough'
  1861.                vec src = from, forward, right, up;
  1862.                AnglesToVectors(GetViewAngles(), forward, right, up);
  1863.                
  1864.                // Trace from 1 cube to the left
  1865.                vec temp = right;
  1866.                temp.mul(1.0f);
  1867.                src.sub(temp);
  1868.                if (!::IsVisible(src, to)) return false;
  1869.  
  1870.                // Trace from 1 cube to the right
  1871.                src.add(temp);
  1872.                if (!::IsVisible(src, to)) return false;
  1873.                               
  1874.                if (UnderWater(from) && UnderWater(to))
  1875.                {
  1876.                     // No need to worry about heights in water
  1877.                     return true;
  1878.                }
  1879. /*
  1880.                if (to.z > (from.z + JUMP_HEIGHT))
  1881.                {
  1882.                     vec v_new_src = to;
  1883.                     vec v_new_dest = to;
  1884.  
  1885.                     v_new_dest.z = v_new_dest.z - (JUMP_HEIGHT + 1.0f);
  1886.  
  1887.                     // check if we didn't hit anything, if so then it's in mid-air
  1888.                     if (::IsVisible(v_new_src, v_new_dest, NULL))
  1889.                     {
  1890.                          condebug("to is in midair");
  1891.                          debugbeam(from, to);
  1892.                          return false;  // can't reach this one
  1893.                     }
  1894.                }
  1895. */
  1896.  
  1897.                // check if distance to ground increases more than jump height
  1898.                // at points between from and to...
  1899.  
  1900.                vec v_temp = to; 
  1901.                v_temp.sub(from);
  1902.                vec v_direction = Normalize(v_temp);  // 1 unit long
  1903.                vec v_check = from;
  1904.                vec v_down = from;
  1905.  
  1906.                v_down.z = v_down.z - 100.0f;  // straight down
  1907.  
  1908.                TraceLine(v_check, v_down, NULL, false, &tr);
  1909.  
  1910.                  // height from ground
  1911.                last_height = GetDistance(v_check, tr.end);
  1912.  
  1913.                distance = GetDistance(to, v_check);  // distance from goal
  1914.  
  1915.                while (distance > 2.0f)
  1916.                {
  1917.                     // move 2 units closer to the goal
  1918.                     v_temp = v_direction;
  1919.                     v_temp.mul(2.0f);
  1920.                     v_check.add(v_temp);
  1921.  
  1922.                     v_down = v_check;
  1923.                     v_down.z = v_down.z - 100.0f;
  1924.  
  1925.                     TraceLine(v_check, v_down, NULL, false, &tr);
  1926.  
  1927.                     curr_height = GetDistance(v_check, tr.end);
  1928.  
  1929.                     // is the difference in the last height and the current height
  1930.                     // higher that the jump height?
  1931.                     if ((last_height - curr_height) >= flMaxHeight)
  1932.                     {
  1933.                          // can't get there from here...
  1934.                          //condebug("traces failed to to");
  1935.                          debugbeam(from, to);
  1936.                          return false;
  1937.                     }
  1938.  
  1939.                     last_height = curr_height;
  1940.  
  1941.                     distance = GetDistance(to, v_check);  // distance from goal
  1942.                }
  1943.  
  1944.                return true;
  1945.           }
  1946.      }
  1947.  
  1948.      return false;
  1949. }
  1950.  
  1951. void CBot::HearSound(int n, vec *o)
  1952. {
  1953.      // Has the bot already an enemy?
  1954.      if (m_pMyEnt->enemy) return;
  1955.      
  1956.  
  1957.      //fixmebot
  1958.      // Is the sound not interesting?  
  1959.      if(n == S_DIE1 || n == S_DIE2) return;
  1960.  
  1961.      int soundvol = m_pBotSkill->iMaxHearVolume - 
  1962.                             (int)(GetDistance(*o)*3*m_pBotSkill->iMaxHearVolume/255);
  1963.           
  1964.      if (soundvol == 0) return;
  1965.           
  1966.      // Look who made the sound(check for the nearest enemy)
  1967.      float flDist, flNearestDist = 3.0f; // Range of 3 units
  1968.      playerent *pNearest = NULL;
  1969.      
  1970.           // Check all players first
  1971.           loopv(players)
  1972.           {
  1973.                playerent *d = players[i];
  1974.  
  1975.                if (d == m_pMyEnt || !d || (d->state != CS_ALIVE) ||
  1976.                    isteam(m_pMyEnt->team, d->team))
  1977.                     continue;
  1978.           
  1979.                flDist = GetDistance(*o, d->o);
  1980.                if ((flDist < flNearestDist) && IsVisible(d))
  1981.                {
  1982.                     pNearest = d;
  1983.                     flNearestDist = flDist;
  1984.                }
  1985.           }
  1986.     
  1987.           // Check local player
  1988.           if (player1 && (player1->state == CS_ALIVE) &&
  1989.               !isteam(m_pMyEnt->team, player1->team))
  1990.           {
  1991.                flDist = GetDistance(*o, player1->o);
  1992.                if ((flDist < flNearestDist) && IsVisible(player1))
  1993.                {
  1994.                     pNearest = player1;
  1995.                     flNearestDist = flDist;
  1996.                }    
  1997.           }
  1998.      
  1999.      if (pNearest)
  2000.      {
  2001.           if (m_pMyEnt->enemy != pNearest)
  2002.                m_iShootDelay = lastmillis + GetShootDelay(); // Add shoot delay when new enemy found
  2003.  
  2004.           m_pMyEnt->enemy = pNearest;               
  2005.      }
  2006. }
  2007.  
  2008. bool CBot::IsInFOV(const vec &o)
  2009. {              
  2010.      vec v2, forward, right, up;
  2011.      float flDot;
  2012.  
  2013.      AnglesToVectors(GetViewAngles(), forward, right, up);
  2014.  
  2015.      v2 = o;
  2016.      v2.sub(m_pMyEnt->o);
  2017.      v2.z = 0.0f; // Make 2D
  2018.      v2 = Normalize(v2);
  2019.      forward.z = 0; // Make 2D
  2020.           
  2021.      flDot = v2.dot(forward);
  2022.  
  2023.      return(flDot >= 0.5f); // sin2(0.5) == 60 degrees FOV
  2024. }
  2025.      
  2026. // Code of CBot - End
  2027.