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 / botmanager.cpp < prev   
Encoding:
C/C++ Source or Header  |  2007-04-04  |  35.6 KB  |  1,310 lines

  1. //
  2. // C++ Implementation: bot
  3. //
  4. // Description: Code for botmanager
  5. //
  6. // Main bot file
  7. //
  8. // Author:  Rick <rickhelmus@gmail.com>
  9. //
  10. //
  11. //
  12.  
  13. #include "bot.h"
  14.  
  15. #ifndef VANILLA_CUBE // UNDONE
  16. bool dedserv = false;
  17. #define CS_DEDHOST 0xFF
  18. #endif
  19.  
  20. extern ENetHost *clienthost;
  21.  
  22. extern void respawnself();
  23.  
  24. CBotManager BotManager;
  25.  
  26. // Bot manager class begin
  27.  
  28. CBotManager::~CBotManager(void)
  29. {
  30.      EndMap();
  31.      
  32.      while(m_StoredBots.Empty() == false)
  33.           delete m_StoredBots.Pop();
  34. }
  35.  
  36. void CBotManager::Init()
  37. {
  38.      m_pBotToView = NULL;
  39.      
  40.      m_bBotsShoot = true;
  41.      m_bIdleBots = false;
  42.      m_iFrameTime = 0;
  43.      m_iPrevTime = lastmillis;
  44.      m_sBotSkill = 1; // Default all bots have the skill 'Good'
  45.      
  46.      CreateSkillData();
  47.      LoadBotNamesFile();
  48.      LoadBotTeamsFile();
  49.      //WaypointClass.Init();
  50.      lsrand(time(NULL));
  51. }
  52.      
  53. void CBotManager::Think()
  54. {    
  55.      if (m_bInit)
  56.      {
  57.           Init();
  58.           m_bInit = false;
  59.      }
  60.  
  61.      if (m_pBotToView)
  62.           ViewBot();
  63.  
  64.      AddDebugText("m_sMaxAStarBots: %d", m_sMaxAStarBots);
  65.      AddDebugText("m_sCurrentTriggerNr: %d", m_sCurrentTriggerNr);
  66.      short x, y;
  67.      WaypointClass.GetNodeIndexes(player1->o, &x, &y);
  68.      AddDebugText("x: %d y: %d", x, y);
  69.      
  70.      m_iFrameTime = lastmillis - m_iPrevTime;
  71.      if (m_iFrameTime > 250) m_iFrameTime = 250;
  72.      m_iPrevTime = lastmillis;
  73.  
  74.      // Is it time to re-add bots?
  75.      if ((m_fReAddBotDelay < lastmillis) && (m_fReAddBotDelay != -1.0f))
  76.      {
  77.           while(m_StoredBots.Empty() == false)
  78.           {
  79.                CStoredBot *pStoredBot = m_StoredBots.Pop();
  80.                ReAddBot(pStoredBot);
  81.                delete pStoredBot;
  82.           }
  83.           
  84.           m_fReAddBotDelay = -1.0f;
  85.      }
  86.      
  87.      // If this is a ded server check if there are any players, if not bots should be idle
  88.      if (dedserv)
  89.      {
  90.           bool botsbeidle = true;
  91.           loopv(players)
  92.           {
  93.                if (players[i] && (players[i]->state != CS_DEDHOST)) { botsbeidle = false; break; }
  94.           }
  95.      
  96.           if (botsbeidle) return;
  97.      }
  98.      
  99.      // Let all bots 'think'
  100.      loopv(bots)
  101.      {
  102.           if (!bots[i])
  103.                continue;
  104.  
  105.           if (bots[i]->pBot)
  106.                bots[i]->pBot->Think();
  107.           else
  108.                condebug("Error: pBot == NULL in bot ent\n");
  109.      }
  110. }
  111.  
  112. void CBotManager::LoadBotNamesFile()
  113. {
  114.      // Init bot names array first
  115.      for (int i=0;i<100;i++)
  116.           strcpy(m_szBotNames[i], "Bot");
  117.      
  118.      m_sBotNameCount = 0;
  119.      
  120.      // Load bot file
  121.      char szNameFileName[256];
  122.      MakeBotFileName("bot_names.txt", NULL, NULL, szNameFileName);
  123.      FILE *fp = fopen(szNameFileName, "r");
  124.      char szNameBuffer[256];
  125.      int iIndex, iStrIndex;
  126.      
  127.      if (!fp)
  128.      {
  129.           conoutf("Warning: Couldn't load bot names file");
  130.           return;
  131.      }
  132.      
  133.      while (fgets(szNameBuffer, 80, fp) != NULL)
  134.      {
  135.           if (m_sBotNameCount >= 150)
  136.           {
  137.                conoutf("Warning: Max bot names reached(150), ignoring the rest of the"
  138.                        "names");
  139.                break;
  140.           }
  141.           
  142.           short length = (short)strlen(szNameBuffer);
  143.  
  144.           if (szNameBuffer[length-1] == '\n')
  145.           {
  146.                szNameBuffer[length-1] = 0;  // remove '\n'
  147.                length--;
  148.           }
  149.  
  150.           iStrIndex = 0;
  151.           while (iStrIndex < length)
  152.           {
  153.                if ((szNameBuffer[iStrIndex] < ' ') || (szNameBuffer[iStrIndex] > '~') ||
  154.                    (szNameBuffer[iStrIndex] == '"'))
  155.                {
  156.                     for (iIndex=iStrIndex; iIndex < length; iIndex++)
  157.                          szNameBuffer[iIndex] = szNameBuffer[iIndex+1];
  158.                }
  159.  
  160.                iStrIndex++;
  161.           }
  162.  
  163.           if (szNameBuffer[0] != 0)
  164.           {
  165.                if (strlen(szNameBuffer) >= 16)
  166.                {     conoutf("Warning: bot name \"%s\" has to many characters(16 is max)",
  167.                             szNameBuffer);
  168.                }
  169.                s_strncpy(m_szBotNames[m_sBotNameCount], szNameBuffer, 16);
  170.                m_sBotNameCount++;
  171.           }
  172.      }
  173.      fclose(fp);
  174. }
  175.      
  176. char *CBotManager::GetBotName()
  177. {
  178.      char *szOutput = NULL;
  179.      TMultiChoice<char *> BotNameChoices;
  180.      short ChoiceVal;
  181.      
  182.      for(int j=0;j<m_sBotNameCount;j++)
  183.      {
  184.           ChoiceVal = 50;
  185.           
  186.           loopv(players)
  187.           {
  188.                if (players[i] && (players[i]->state != CS_DEDHOST) && 
  189.                    !strcasecmp(players[i]->name, m_szBotNames[j]))
  190.                     ChoiceVal -= 10;
  191.           }
  192.           
  193.           loopv(bots)
  194.           {
  195.                if (bots[i] && (!strcasecmp(bots[i]->name, m_szBotNames[j])))
  196.                     ChoiceVal -= 10;
  197.           }
  198.           
  199.           if ((player1->state != CS_DEDHOST) && !strcasecmp(player1->name, m_szBotNames[j]))
  200.                ChoiceVal -= 10;
  201.                
  202.           if (ChoiceVal <= 0)
  203.                ChoiceVal = 1;
  204.           
  205.           BotNameChoices.Insert(m_szBotNames[j], ChoiceVal);
  206.      }
  207.      
  208.      // Couldn't find a selection?
  209.      if (!BotNameChoices.GetSelection(szOutput))
  210.           szOutput = "Bot";
  211.      
  212.      return szOutput;
  213. }
  214.      
  215. void CBotManager::LoadBotTeamsFile()
  216. {
  217.      // Init bot teams array first
  218.      for (int i=0;i<20;i++)
  219.           strcpy(m_szBotTeams[i], "b0ts");
  220.      
  221.      m_sBotTeamCount = 0;
  222.      
  223.      // Load bot file
  224.      char szNameFileName[256];
  225.      MakeBotFileName("bot_teams.txt", NULL, NULL, szNameFileName);
  226.      FILE *fp = fopen(szNameFileName, "r");
  227.      char szNameBuffer[256];
  228.      int iIndex, iStrIndex;
  229.      
  230.      if (!fp)
  231.      {
  232.           conoutf("Warning: Couldn't load bot teams file");
  233.           return;
  234.      }
  235.      
  236.      while ((m_sBotTeamCount < 20) && (fgets(szNameBuffer, 80, fp) != NULL))
  237.      {
  238.           short length = (short)strlen(szNameBuffer);
  239.  
  240.           if (szNameBuffer[length-1] == '\n')
  241.           {
  242.                szNameBuffer[length-1] = 0;  // remove '\n'
  243.                length--;
  244.           }
  245.  
  246.           iStrIndex = 0;
  247.           while (iStrIndex < length)
  248.           {
  249.                if ((szNameBuffer[iStrIndex] < ' ') || (szNameBuffer[iStrIndex] > '~') ||
  250.                    (szNameBuffer[iStrIndex] == '"'))
  251.                {
  252.                     for (iIndex=iStrIndex; iIndex < length; iIndex++)
  253.                          szNameBuffer[iIndex] = szNameBuffer[iIndex+1];
  254.                }
  255.  
  256.                iStrIndex++;
  257.           }
  258.  
  259.           if (szNameBuffer[0] != 0)
  260.           {
  261.                s_strncpy(m_szBotTeams[m_sBotTeamCount], szNameBuffer, 5);
  262.                m_sBotTeamCount++;
  263.           }
  264.      }
  265.      fclose(fp);
  266. }
  267.  
  268. char *CBotManager::GetBotTeam()
  269. {
  270.      char *szOutput = NULL;
  271.      TMultiChoice<char *> BotTeamChoices;
  272.      short ChoiceVal;
  273.      
  274.      for(int j=0;j<m_sBotTeamCount;j++)
  275.      {
  276.           ChoiceVal = 50;
  277.           /* UNDONE?
  278.           loopv(players)
  279.           {
  280.                if (players[i] && (!strcasecmp(players[i]->name, m_szBotNames[j])))
  281.                     ChoiceVal -= 10;
  282.           }
  283.           
  284.           loopv(bots)
  285.           {
  286.                if (bots[i] && (!strcasecmp(bots[i]->name, m_szBotNames[j])))
  287.                     ChoiceVal -= 10;
  288.           }
  289.           
  290.           if (!strcasecmp(player1->name, m_szBotNames[j]))
  291.                ChoiceVal -= 10;
  292.                
  293.           if (ChoiceVal <= 0)
  294.                ChoiceVmonsterclearal = 1;*/
  295.           
  296.           BotTeamChoices.Insert(m_szBotTeams[j], ChoiceVal);
  297.      }
  298.      
  299.      // Couldn't find a selection?
  300.      if (!BotTeamChoices.GetSelection(szOutput))
  301.           szOutput = "b0t";
  302.      
  303.      return szOutput;
  304. }
  305.  
  306. void CBotManager::RenderBots()
  307. {
  308.      //static bool drawblue;
  309.      
  310.      loopv(bots)
  311.      {
  312.           if (bots[i] && (bots[i] != m_pBotToView))
  313.           {
  314.                /*drawblue = (m_sp || isteam(player1->team, bots[i]->team));
  315.                renderclient(bots[i], drawblue, "playermodels/counterterrorist", 1.6f);*/
  316.               renderclient(bots[i]);
  317.           }
  318.      }
  319. }
  320.  
  321. void CBotManager::EndMap()
  322. {
  323.      // Remove all bots
  324.      loopv(bots)
  325.      {                
  326.           if(!bots[i] || !bots[i]->pBot)
  327.                continue;
  328.  
  329.           // Store bots so they can be re-added after map change
  330.           if (bots[i]->pBot && bots[i]->name[0] && bots[i]->team[0])
  331.           {
  332.                CStoredBot *pStoredBot = new CStoredBot(bots[i]->name, bots[i]->team,
  333.                                                        bots[i]->pBot->m_sSkillNr);
  334.                m_StoredBots.AddNode(pStoredBot);
  335.           }
  336.           delete bots[i]->pBot;
  337.           
  338.           bots[i]->pBot = NULL;
  339.           freebotent(bots[i]);
  340.      }
  341.      bots.setsize(0);     
  342.      condebug("Cleared all bots");
  343.      m_fReAddBotDelay = lastmillis + 7500;
  344.      //if(ishost()) WaypointClass.SaveWPExpFile(); //UNDONE
  345. }
  346.  
  347. void CBotManager::BeginMap(char *szMapName)
  348. {
  349.      EndMap(); // End previous map
  350.      
  351.      WaypointClass.Init();
  352.      WaypointClass.SetMapName(szMapName);
  353.      if (!WaypointClass.LoadWaypoints())
  354.           WaypointClass.StartFlood();
  355.      //WaypointClass.LoadWPExpFile(); // UNDONE
  356.  
  357.      CalculateMaxAStarCount();
  358.      m_sUsingAStarBotsCount = 0;
  359.      PickNextTrigger();
  360. }
  361.      
  362. int CBotManager::GetBotIndex(botent *m)
  363. {
  364.      loopv(bots)
  365.      {
  366.           if (!bots[i])
  367.                continue;
  368.      
  369.           if (bots[i] == m)
  370.                return i;
  371.      }
  372.      
  373.      return -1;
  374. }
  375.  
  376. void CBotManager::LetBotsUpdateStats()
  377. {
  378.      loopv(bots) if (bots[i] && bots[i]->pBot) bots[i]->pBot->m_bSendC2SInit = false;
  379. }
  380.  
  381. void CBotManager::LetBotsHear(int n, vec *loc)
  382. {
  383.      if (bots.length() == 0 || !loc) return;
  384.           
  385.      loopv(bots)
  386.      {
  387.           if (!bots[i] || !bots[i]->pBot || (bots[i]->state == CS_DEAD)) continue;
  388.           bots[i]->pBot->HearSound(n, loc);
  389.      }
  390. }
  391.  
  392. // Notify all bots of a new waypoint
  393. void CBotManager::AddWaypoint(node_s *pNode)
  394. {
  395.      if (bots.length())
  396.      {
  397.           short x, y;
  398.           waypoint_s *pWP;
  399.      
  400.           loopv(bots)
  401.           {
  402.                if (!bots[i] || !bots[i]->pBot) continue;
  403.           
  404.                pWP = new waypoint_s;
  405.                pWP->pNode = pNode;
  406.                WaypointClass.GetNodeIndexes(pNode->v_origin, &x, &y);
  407.                bots[i]->pBot->m_WaypointList[x][y].AddNode(pWP);
  408.                
  409. #ifndef RELEASE_BUILD
  410.                if (!bots[i]->pBot->GetWPFromNode(pNode)) condebug("Error adding bot wp!");
  411. #endif               
  412.           }
  413.      }
  414.      
  415.      CalculateMaxAStarCount();
  416. }
  417.  
  418. // Notify all bots of a deleted waypoint
  419. void CBotManager::DelWaypoint(node_s *pNode)
  420. {
  421.      if (bots.length())
  422.      {
  423.           short x, y;
  424.           TLinkedList<waypoint_s *>::node_s *p;
  425.      
  426.           loopv(bots)
  427.           {
  428.                if (!bots[i] || !bots[i]->pBot) continue;
  429.           
  430.                WaypointClass.GetNodeIndexes(pNode->v_origin, &x, &y);
  431.                p = bots[i]->pBot->m_WaypointList[x][y].GetFirst();
  432.           
  433.                while(p)
  434.                {
  435.                     if (p->Entry->pNode == pNode)
  436.                     {
  437.                          delete p->Entry;
  438.                          bots[i]->pBot->m_WaypointList[x][y].DeleteNode(p);
  439.                          break;
  440.                     }
  441.                     p = p->next;
  442.                }          
  443.           }
  444.      }
  445.      
  446.      CalculateMaxAStarCount();
  447. }
  448.  
  449. void CBotManager::MakeBotFileName(const char *szFileName, const char *szDir1, const char *szDir2, char *szOutput)
  450. {
  451.      char *DirSeperator;
  452.  
  453. #ifdef WIN32
  454.      DirSeperator = "\\";
  455.      strcpy(szOutput, "bot\\");
  456. #else
  457.      DirSeperator = "/";
  458.      strcpy(szOutput, "bot/");
  459. #endif
  460.      
  461.      if (szDir1)
  462.      {
  463.           strcat(szOutput, szDir1);
  464.           strcat(szOutput, DirSeperator);
  465.      }
  466.      
  467.      if (szDir2)
  468.      {
  469.           strcat(szOutput, szDir2);
  470.           strcat(szOutput, DirSeperator);
  471.      }
  472.      
  473.      strcat(szOutput, szFileName);
  474. }
  475.  
  476. void CBotManager::CreateSkillData()
  477. {
  478.      // First give the bot skill structure some default data
  479.      InitSkillData();
  480.      
  481.      // Now see if we can load the skill.cfg file
  482.      char SkillFileName[256] = "";
  483.      FILE *pSkillFile = NULL;
  484.      int SkillNr = -1;
  485.      float value = 0;
  486.  
  487.      MakeBotFileName("bot_skill.cfg", NULL, NULL, SkillFileName);
  488.  
  489.      pSkillFile = fopen(SkillFileName, "r");
  490.  
  491.      conoutf("Reading bot_skill.cfg file... ");
  492.  
  493.      if (pSkillFile == NULL) // file doesn't exist
  494.      {
  495.           conoutf("skill file not found, default settings will be used\n");
  496.           return;
  497.      }
  498.  
  499.      int ch;
  500.      char cmd_line[256];
  501.      int cmd_index;
  502.      char *cmd, *arg1;
  503.  
  504.      while (pSkillFile)
  505.      {    
  506.           cmd_index = 0;
  507.           cmd_line[cmd_index] = 0;
  508.  
  509.           ch = fgetc(pSkillFile);
  510.  
  511.           // skip any leading blanks
  512.           while (ch == ' ')
  513.                ch = fgetc(pSkillFile);
  514.  
  515.           while ((ch != EOF) && (ch != '\r') && (ch != '\n'))
  516.           {
  517.                if (ch == '\t')  // convert tabs to spaces
  518.                     ch = ' ';
  519.  
  520.                cmd_line[cmd_index] = ch;
  521.  
  522.                ch = fgetc(pSkillFile);
  523.  
  524.                // skip multiple spaces in input file
  525.                while ((cmd_line[cmd_index] == ' ') && (ch == ' '))      
  526.                     ch = fgetc(pSkillFile);
  527.  
  528.                cmd_index++;
  529.           }
  530.  
  531.           if (ch == '\r')  // is it a carriage return?
  532.           {
  533.                ch = fgetc(pSkillFile);  // skip the linefeed
  534.           }
  535.  
  536.           // if reached end of file, then close it
  537.           if (ch == EOF)
  538.           {
  539.                fclose(pSkillFile);
  540.                pSkillFile = NULL;
  541.           }
  542.  
  543.           cmd_line[cmd_index] = 0;  // terminate the command line
  544.  
  545.           cmd_index = 0;
  546.           cmd = cmd_line;
  547.           arg1 = NULL;
  548.  
  549.           // skip to blank or end of string...
  550.           while ((cmd_line[cmd_index] != ' ') && (cmd_line[cmd_index] != 0))
  551.                 cmd_index++;
  552.  
  553.           if (cmd_line[cmd_index] == ' ')
  554.           {
  555.                 cmd_line[cmd_index++] = 0;
  556.                 arg1 = &cmd_line[cmd_index];
  557.           }
  558.  
  559.           if ((cmd_line[0] == '#') || (cmd_line[0] == 0))
  560.                continue;  // skip if comment or blank line
  561.  
  562.  
  563.           if (strcasecmp(cmd, "[SKILL1]") == 0)
  564.                SkillNr = 0;
  565.           else if (strcasecmp(cmd, "[SKILL2]") == 0)
  566.                SkillNr = 1;
  567.           else if (strcasecmp(cmd, "[SKILL3]") == 0)
  568.                SkillNr = 2;
  569.           else if (strcasecmp(cmd, "[SKILL4]") == 0)
  570.                SkillNr = 3;
  571.           else if (strcasecmp(cmd, "[SKILL5]") == 0)
  572.                SkillNr = 4;
  573.  
  574.           if ((arg1 == NULL) || (*arg1 == 0))
  575.               continue;
  576.  
  577.           if (SkillNr == -1) // Not in a skill block yet?
  578.                continue;
  579.  
  580.           value = atof(arg1);
  581.  
  582.           if (strcasecmp(cmd, "min_x_aim_speed") == 0)
  583.           {
  584.                m_BotSkills[SkillNr].flMinAimXSpeed = value;
  585.           }
  586.           else if (strcasecmp(cmd, "max_x_aim_speed") == 0)
  587.           {
  588.                m_BotSkills[SkillNr].flMaxAimXSpeed = value;
  589.           }
  590.           else if (strcasecmp(cmd, "min_y_aim_speed") == 0)
  591.           {
  592.                m_BotSkills[SkillNr].flMinAimYSpeed = value;
  593.           }
  594.           else if (strcasecmp(cmd, "max_y_aim_speed") == 0)
  595.           {
  596.                m_BotSkills[SkillNr].flMaxAimYSpeed = value;
  597.           }
  598.           else if (strcasecmp(cmd, "min_x_aim_offset") == 0)
  599.           {
  600.                m_BotSkills[SkillNr].flMinAimXOffset = value;
  601.           }
  602.           else if (strcasecmp(cmd, "max_x_aim_offset") == 0)
  603.           {
  604.                m_BotSkills[SkillNr].flMaxAimXOffset = value;
  605.           }
  606.           else if (strcasecmp(cmd, "min_y_aim_offset") == 0)
  607.           {
  608.                m_BotSkills[SkillNr].flMinAimYOffset = value;
  609.           }
  610.           else if (strcasecmp(cmd, "max_y_aim_offset") == 0)
  611.           {
  612.                m_BotSkills[SkillNr].flMaxAimYOffset = value;
  613.           }
  614.           else if (strcasecmp(cmd, "min_attack_delay") == 0)
  615.           {
  616.                m_BotSkills[SkillNr].flMinAttackDelay = value;
  617.           }
  618.           else if (strcasecmp(cmd, "max_attack_delay") == 0)
  619.           {
  620.                m_BotSkills[SkillNr].flMaxAttackDelay = value;
  621.           }
  622.           else if (strcasecmp(cmd, "min_enemy_search_delay") == 0)
  623.           {
  624.                m_BotSkills[SkillNr].flMinEnemySearchDelay = value;
  625.           }
  626.           else if (strcasecmp(cmd, "max_enemy_search_delay") == 0)
  627.           {
  628.                m_BotSkills[SkillNr].flMaxEnemySearchDelay = value;
  629.           }
  630.           else if (strcasecmp(cmd, "shoot_at_feet_percent") == 0)
  631.           {
  632.                if (value < 0) value = 0;
  633.                else if (value > 100) value = 100;
  634.                m_BotSkills[SkillNr].sShootAtFeetWithRLPercent = (short)value;
  635.           }
  636.           else if (strcasecmp(cmd, "can_predict_position") == 0)
  637.           {
  638.                m_BotSkills[SkillNr].bCanPredict = value!=0;
  639.           }
  640.           else if (strcasecmp(cmd, "max_hear_volume") == 0)
  641.           {
  642.                if (value < 0) value = 0;
  643.                else if (value > 255) value = 100;
  644.                m_BotSkills[SkillNr].iMaxHearVolume = (int)value;
  645.           }
  646.           else if (strcasecmp(cmd, "can_circle_strafe") == 0)
  647.           {
  648.                m_BotSkills[SkillNr].bCircleStrafe = value!=0;
  649.           }     
  650.           else if (strcasecmp(cmd, "can_search_items_in_combat") == 0)
  651.           {
  652.                m_BotSkills[SkillNr].bCanSearchItemsInCombat = value!=0;
  653.           }          
  654.      }
  655.  
  656.      conoutf("done\n");
  657. }
  658.  
  659. void CBotManager::InitSkillData()
  660. {
  661.      // Best skill
  662.      m_BotSkills[0].flMinReactionDelay = 0.015f;
  663.      m_BotSkills[0].flMaxReactionDelay = 0.035f;
  664.      m_BotSkills[0].flMinAimXOffset = 15.0f;
  665.      m_BotSkills[0].flMaxAimXOffset = 20.0f;
  666.      m_BotSkills[0].flMinAimYOffset = 10.0f;
  667.      m_BotSkills[0].flMaxAimYOffset = 15.0f;
  668.      m_BotSkills[0].flMinAimXSpeed = 330.0f;
  669.      m_BotSkills[0].flMaxAimXSpeed = 355.0f;
  670.      m_BotSkills[0].flMinAimYSpeed = 400.0f;
  671.      m_BotSkills[0].flMaxAimYSpeed = 450.0f;
  672.      m_BotSkills[0].flMinAttackDelay = 0.1f;
  673.      m_BotSkills[0].flMaxAttackDelay = 0.4f;
  674.      m_BotSkills[0].flMinEnemySearchDelay = 0.09f;
  675.      m_BotSkills[0].flMaxEnemySearchDelay = 0.12f;
  676.      m_BotSkills[0].sShootAtFeetWithRLPercent = 85;
  677.      m_BotSkills[0].bCanPredict = true;
  678.      m_BotSkills[0].iMaxHearVolume = 75;
  679.      m_BotSkills[0].bCircleStrafe = true;
  680.      m_BotSkills[0].bCanSearchItemsInCombat = true;
  681.  
  682.      // Good skill
  683.      m_BotSkills[1].flMinReactionDelay = 0.035f;
  684.      m_BotSkills[1].flMaxReactionDelay = 0.045f;
  685.      m_BotSkills[1].flMinAimXOffset = 20.0f;
  686.      m_BotSkills[1].flMaxAimXOffset = 25.0f;
  687.      m_BotSkills[1].flMinAimYOffset = 15.0f;
  688.      m_BotSkills[1].flMaxAimYOffset = 20.0f;
  689.      m_BotSkills[1].flMinAimXSpeed = 250.0f;
  690.      m_BotSkills[1].flMaxAimXSpeed = 265.0f;
  691.      m_BotSkills[1].flMinAimYSpeed = 260.0f;
  692.      m_BotSkills[1].flMaxAimYSpeed = 285.0f;
  693.      m_BotSkills[1].flMinAttackDelay = 0.3f;
  694.      m_BotSkills[1].flMaxAttackDelay = 0.6f;
  695.      m_BotSkills[1].flMinEnemySearchDelay = 0.12f;
  696.      m_BotSkills[1].flMaxEnemySearchDelay = 0.17f;
  697.      m_BotSkills[1].sShootAtFeetWithRLPercent = 60;
  698.      m_BotSkills[1].bCanPredict = true;
  699.      m_BotSkills[1].iMaxHearVolume = 60;
  700.      m_BotSkills[1].bCircleStrafe = true;
  701.      m_BotSkills[1].bCanSearchItemsInCombat = true;
  702.  
  703.      // Medium skill
  704.      m_BotSkills[2].flMinReactionDelay = 0.075f;
  705.      m_BotSkills[2].flMaxReactionDelay = 0.010f;
  706.      m_BotSkills[2].flMinAimXOffset = 25.0f;
  707.      m_BotSkills[2].flMaxAimXOffset = 30.0f;
  708.      m_BotSkills[2].flMinAimYOffset = 20.0f;
  709.      m_BotSkills[2].flMaxAimYOffset = 25.0f;
  710.      m_BotSkills[2].flMinAimXSpeed = 190.0f;
  711.      m_BotSkills[2].flMaxAimXSpeed = 125.0f;
  712.      m_BotSkills[2].flMinAimYSpeed = 210.0f;
  713.      m_BotSkills[2].flMaxAimYSpeed = 240.0f;
  714.      m_BotSkills[2].flMinAttackDelay = 0.75f;
  715.      m_BotSkills[2].flMaxAttackDelay = 1.0f;
  716.      m_BotSkills[2].flMinEnemySearchDelay = 0.18f;
  717.      m_BotSkills[2].flMaxEnemySearchDelay = 0.22f;
  718.      m_BotSkills[2].sShootAtFeetWithRLPercent = 25;
  719.      m_BotSkills[2].bCanPredict = false;
  720.      m_BotSkills[2].iMaxHearVolume = 45;
  721.      m_BotSkills[2].bCircleStrafe = true;
  722.      m_BotSkills[2].bCanSearchItemsInCombat = false;
  723.  
  724.      // Worse skill
  725.      m_BotSkills[3].flMinReactionDelay = 0.15f;
  726.      m_BotSkills[3].flMaxReactionDelay = 0.20f;
  727.      m_BotSkills[3].flMinAimXOffset = 30.0f;
  728.      m_BotSkills[3].flMaxAimXOffset = 35.0f;
  729.      m_BotSkills[3].flMinAimYOffset = 25.0f;
  730.      m_BotSkills[3].flMaxAimYOffset = 30.0f;
  731.      m_BotSkills[3].flMinAimXSpeed = 155.0f;
  732.      m_BotSkills[3].flMaxAimXSpeed = 170.0f;
  733.      m_BotSkills[3].flMinAimYSpeed = 160.0f;
  734.      m_BotSkills[3].flMaxAimYSpeed = 210.0f;
  735.      m_BotSkills[3].flMinAttackDelay = 1.2f;
  736.      m_BotSkills[3].flMaxAttackDelay = 1.6f;
  737.      m_BotSkills[3].flMinEnemySearchDelay = 0.25f;
  738.      m_BotSkills[3].flMaxEnemySearchDelay = 0.30f;
  739.      m_BotSkills[3].sShootAtFeetWithRLPercent = 10;
  740.      m_BotSkills[3].bCanPredict = false;
  741.      m_BotSkills[3].iMaxHearVolume = 30;
  742.      m_BotSkills[3].bCircleStrafe = false;
  743.      m_BotSkills[3].bCanSearchItemsInCombat = false;
  744.  
  745.      // Bad skill
  746.      m_BotSkills[4].flMinReactionDelay = 0.30f;
  747.      m_BotSkills[4].flMaxReactionDelay = 0.50f;
  748.      m_BotSkills[4].flMinAimXOffset = 35.0f;
  749.      m_BotSkills[4].flMaxAimXOffset = 40.0f;
  750.      m_BotSkills[4].flMinAimYOffset = 30.0f;
  751.      m_BotSkills[4].flMaxAimYOffset = 35.0f;
  752.      m_BotSkills[4].flMinAimXSpeed = 45.0f;
  753.      m_BotSkills[4].flMaxAimXSpeed = 60.0f;
  754.      m_BotSkills[4].flMinAimYSpeed = 125.0f;
  755.      m_BotSkills[4].flMaxAimYSpeed = 180.0f;
  756.      m_BotSkills[4].flMinAttackDelay = 1.5f;
  757.      m_BotSkills[4].flMaxAttackDelay = 2.0f;
  758.      m_BotSkills[4].flMinEnemySearchDelay = 0.30f;
  759.      m_BotSkills[4].flMaxEnemySearchDelay = 0.36f;
  760.      m_BotSkills[4].sShootAtFeetWithRLPercent = 0;
  761.      m_BotSkills[4].bCanPredict = false;
  762.      m_BotSkills[4].iMaxHearVolume = 15;
  763.      m_BotSkills[4].bCircleStrafe = false;
  764.      m_BotSkills[4].bCanSearchItemsInCombat = false;
  765. }
  766.     
  767. void CBotManager::ChangeBotSkill(short Skill, botent *bot)
  768. {
  769.      static char *SkillNames[5] = { "best", "good", "medium", "worse", "bad" };
  770.      
  771.      if (bot && bot->pBot)
  772.      {
  773.           // Only change skill of a single bot
  774.           bot->pBot->m_pBotSkill = &m_BotSkills[Skill];
  775.           bot->pBot->m_sSkillNr = Skill;
  776.           conoutf("Skill of %s is now %s", bot->name, SkillNames[Skill]);
  777.           return;
  778.      }
  779.      
  780.      // Change skill of all bots
  781.      loopv(bots)
  782.      {
  783.           if (!bots[i] || !bots[i]->pBot) continue;
  784.           
  785.           bots[i]->pBot->m_pBotSkill = &m_BotSkills[Skill];
  786.           bots[i]->pBot->m_sSkillNr = Skill;
  787.      }
  788.      
  789.      // Change default bot skill
  790.      m_sBotSkill = Skill;
  791.      
  792.      conoutf("Skill of all bots is now %s", SkillNames[Skill]);
  793. }
  794.  
  795. void CBotManager::ViewBot()
  796. {   
  797.      // Check if this bot is still in game
  798.      bool bFound = false;
  799.      loopv(bots)
  800.      {
  801.           if (bots[i] == m_pBotToView)
  802.           {
  803.                bFound = true;
  804.                break;
  805.           }
  806.      }
  807.      
  808.      if (!bFound)
  809.      {
  810.           DisableBotView();
  811.           return;
  812.      }
  813.      
  814.      player1->state = CS_DEAD; // Fake dead
  815.      
  816.      player1->o = m_pBotToView->o;
  817.      player1->o.z += 1.0f;
  818.      player1->yaw = m_pBotToView->yaw;
  819.      player1->pitch = m_pBotToView->pitch;
  820.      player1->roll = m_pBotToView->roll;
  821.      player1->radius = 0; // Don't collide
  822. }
  823.  
  824. void CBotManager::DisableBotView()
  825. {
  826.      m_pBotToView = NULL;
  827.      respawnself();
  828.      player1->radius = 1.1f;
  829. }
  830.  
  831. void CBotManager::CalculateMaxAStarCount()
  832. {  
  833.      if (WaypointClass.m_iWaypointCount > 0) // Are there any waypoints?
  834.      {
  835.           m_sMaxAStarBots = 8 - short(ceil((float)WaypointClass.m_iWaypointCount /
  836.                                       1000.0f));
  837.           if (m_sMaxAStarBots < 1)
  838.                m_sMaxAStarBots = 1;
  839.      }
  840.      else
  841.           m_sMaxAStarBots = 1;
  842. }
  843.  
  844. void CBotManager::PickNextTrigger()
  845. {
  846.      short lowest = -1;
  847.      bool found0 = false; // True if found a trigger with nr 0
  848.      
  849.      loopv(ents)
  850.      {
  851.           entity &e = ents[i];
  852.           
  853. #if defined AC_CUBE          
  854. /*          if ((e.type != TRIGGER) || !e.spawned)
  855.                continue;*/
  856. #elif defined VANILLA_CUBE
  857.           if ((e.type != CARROT) || !e.spawned)
  858.                continue;
  859. #endif          
  860.           if (OUTBORD(e.x, e.y)) continue;
  861.           
  862.           vec o(e.x, e.y, S(e.x, e.y)->floor+player1->eyeheight);
  863.  
  864.           node_s *pWptNearEnt = NULL;
  865.           
  866.           pWptNearEnt = WaypointClass.GetNearestTriggerWaypoint(o, 2.0f);
  867.           
  868.           if (pWptNearEnt)
  869.           {
  870.                if ((pWptNearEnt->sTriggerNr > 0) &&
  871.                    ((pWptNearEnt->sTriggerNr < lowest) || (lowest == -1)))
  872.                     lowest = pWptNearEnt->sTriggerNr;
  873.                if (pWptNearEnt->sTriggerNr == 0) found0 = true;
  874.           }
  875.           
  876. #ifdef WP_FLOOD
  877.           pWptNearEnt = WaypointClass.GetNearestTriggerFloodWP(o, 2.0f);
  878.           
  879.           if (pWptNearEnt)
  880.           {
  881.                if ((pWptNearEnt->sTriggerNr > 0) && 
  882.                    ((pWptNearEnt->sTriggerNr < lowest) || (lowest == -1)))
  883.                     lowest = pWptNearEnt->sTriggerNr;
  884.                if (pWptNearEnt->sTriggerNr == 0) found0 = true;
  885.           }
  886.                     
  887. #endif               
  888.      }
  889.      
  890.      if ((lowest == -1) && found0) lowest = 0;
  891.      
  892.      if (lowest != -1)
  893.           m_sCurrentTriggerNr = lowest;
  894. }
  895.  
  896. botent *CBotManager::CreateBot(const char *team, const char *skill, const char *name)
  897. {
  898.      botent *m = newbotent();
  899.      if (!m) return NULL;
  900.      loopi(NUMGUNS) m->ammo[i] = m->mag[i] = 0;
  901.      setskin(m, rnd(6));
  902.      // Create new bot class, dependand on the current mod
  903. #if defined VANILLA_CUBE     
  904.      m->pBot = new CCubeBot;
  905. #elif defined AC_CUBE
  906.      m->pBot = new CACBot;
  907. #else
  908.      #error "Unsupported mod!"
  909. #endif
  910.      m->type = ENT_BOT;
  911.      m->pBot->m_pMyEnt = m;
  912.      m->pBot->m_iLastBotUpdate = 0;
  913.      m->pBot->m_bSendC2SInit = false;
  914.  
  915.      if (name && *name)
  916.           s_strncpy(m->name, name, 16);
  917.      else
  918.           s_strncpy(m->name, BotManager.GetBotName(), 16);
  919.      
  920.      if (team && *team && strcmp(team, "random"))
  921.           s_strncpy(m->team, team, 5);
  922.      else
  923.           s_strncpy(m->team, BotManager.GetBotTeam(), 5);
  924.      
  925.      if (skill && *skill && strcmp(skill, "random"))
  926.      {
  927.           if (!strcasecmp(skill, "best"))
  928.                m->pBot->m_sSkillNr = 0;
  929.           else if (!strcasecmp(skill, "good"))
  930.                m->pBot->m_sSkillNr = 1;
  931.           else if (!strcasecmp(skill, "medium"))
  932.                m->pBot->m_sSkillNr = 2;
  933.           else if (!strcasecmp(skill, "worse"))
  934.                m->pBot->m_sSkillNr = 3;
  935.           else if (!strcasecmp(skill, "bad"))
  936.                m->pBot->m_sSkillNr = 4;
  937.           else
  938.           {
  939.                conoutf("Wrong skill specified. Should be best, good, medium, "
  940.                        "worse or bad");
  941.                conoutf("Using default skill instead...");
  942.                m->pBot->m_sSkillNr = BotManager.m_sBotSkill;
  943.           }
  944.      }
  945.      else // No skill specified, use default
  946.           m->pBot->m_sSkillNr = BotManager.m_sBotSkill;
  947.           
  948.      m->pBot->m_pBotSkill = &BotManager.m_BotSkills[m->pBot->m_sSkillNr];
  949.  
  950.      // Sync waypoints
  951.      m->pBot->SyncWaypoints();
  952.               
  953.      m->pBot->Spawn();
  954.      
  955.      bots.add(m);
  956.      
  957.      return m;     
  958. }
  959.  
  960. bool botmode()
  961. {
  962.     if(m_botmode) return true;
  963.     conoutf("the current game mode does not support bots");
  964.     return false;
  965. }
  966.  
  967. // Bot manager class end
  968.  
  969. void addbot(char *arg1, char *arg2, char *arg3)
  970. {
  971.     if(!botmode()) return;
  972.     conoutf("Creating bot...\n");
  973.     botent *b = BotManager.CreateBot(arg1, arg2, arg3);
  974.     if (b)
  975.         conoutf("connected: %s", b->name);
  976.     else
  977.     {
  978.         conoutf("Error: Couldn't create bot!");
  979.         return;
  980.     }
  981. }
  982.  
  983. COMMAND(addbot, ARG_3STR);
  984.  
  985. void addnbot(char *arg1, char *arg2, char *arg3)
  986. {
  987.     if(!botmode()) return;
  988.      if (!arg1 || !arg1[0]) return;
  989.      
  990.      int i = atoi(arg1);
  991.      
  992.      while(i > 0)
  993.      {
  994.          addbot(arg2, arg3, NULL);
  995.          i--;
  996.      }
  997. }
  998.  
  999. COMMAND(addnbot, ARG_3STR);
  1000.  
  1001. void botsshoot(int Shoot)
  1002. {
  1003.      if (Shoot)
  1004.      {
  1005.           BotManager.SetBotsShoot(true);
  1006.           conoutf("Bots will shoot");
  1007.      }
  1008.      else
  1009.      {
  1010.           BotManager.SetBotsShoot(false);
  1011.           conoutf("Bots won't shoot");
  1012.      }
  1013. }
  1014.  
  1015. COMMAND(botsshoot, ARG_1INT);
  1016.           
  1017. void idlebots(int Idle)
  1018. {
  1019.      if (Idle)
  1020.      {
  1021.           BotManager.SetIdleBots(true);
  1022.           conoutf("Bots are idle");
  1023.      }
  1024.      else
  1025.      {
  1026.           BotManager.SetIdleBots(false);
  1027.           conoutf("Bots aren't idle");
  1028.      }
  1029. }
  1030.  
  1031. COMMAND(idlebots, ARG_1INT);
  1032.  
  1033. void drawbeamtobots()
  1034. {
  1035.     if(!botmode()) return;
  1036.      loopv(bots)
  1037.      {
  1038.           if (bots[i])
  1039.                particle_trail(1, 500, player1->o, bots[i]->o);
  1040.      }
  1041. }
  1042.  
  1043. COMMAND(drawbeamtobots, ARG_NONE);
  1044.  
  1045. void kickbot(const char *szName)
  1046. {
  1047.     if(!botmode()) return;
  1048.      if (!szName || !(*szName))
  1049.           return;
  1050.           
  1051.      int iBotInd = -1;
  1052.      loopv(bots)
  1053.      {
  1054.           if (!bots[i]) continue;
  1055.           
  1056.           if (!strcmp(bots[i]->name, szName))
  1057.           {
  1058.                iBotInd = i;
  1059.                break;
  1060.           }
  1061.      }
  1062.      
  1063.      if (iBotInd != -1)
  1064.      {
  1065.           botent *d = bots[iBotInd];
  1066.           if(d->name[0]) conoutf("bot %s disconnected", d->name);
  1067.           delete d->pBot;
  1068.           bots.remove(iBotInd);
  1069.           freebotent(d);
  1070.      }
  1071. }
  1072.  
  1073. COMMAND(kickbot, ARG_1STR);
  1074.  
  1075. void kickallbots(void)
  1076. {
  1077.      BotManager.ClearStoredBots();
  1078.  
  1079.      loopv(bots)
  1080.      {
  1081.           if (bots[i])
  1082.           {
  1083.                if(bots[i]->name[0]) conoutf("bot %s disconnected", bots[i]->name);
  1084.                delete bots[i]->pBot;
  1085.                freebotent(bots[i]);
  1086.           }
  1087.      };
  1088.      
  1089.      bots.setsize(0);
  1090. }
  1091.  
  1092. COMMAND(kickallbots, ARG_NONE);
  1093.  
  1094. void togglegrap()
  1095. {
  1096.     if(!botmode()) return;
  1097.      if (SDL_WM_GrabInput(SDL_GRAB_QUERY) == SDL_GrabMode(0))
  1098.           SDL_WM_GrabInput(SDL_GRAB_ON);
  1099.      else
  1100.           SDL_WM_GrabInput(SDL_GrabMode(0));
  1101. }
  1102.  
  1103. COMMAND(togglegrap, ARG_NONE);
  1104.  
  1105. void togglebotview(char *bot)
  1106. {
  1107.     if(!botmode()) return;
  1108.      if (BotManager.m_pBotToView)
  1109.           BotManager.DisableBotView();
  1110.      else if (bot && *bot)
  1111.      {
  1112.           loopv(bots)
  1113.           {
  1114.                if (!bots[i]) continue;
  1115.                
  1116.                if (!strcmp(bots[i]->name, bot))
  1117.                {
  1118.                     BotManager.EnableBotView(bots[i]);
  1119.                     break;
  1120.                }
  1121.           }
  1122.      }
  1123. }
  1124.  
  1125. COMMAND(togglebotview, ARG_1STR);
  1126.  
  1127. void botskill(char *bot, char *skill)
  1128. {
  1129.      if (!skill || !(*skill))
  1130.           return;
  1131.      
  1132.      short SkillNr;
  1133.      
  1134.      if (!strcasecmp(skill, "best"))
  1135.           SkillNr = 0;
  1136.      else if (!strcasecmp(skill, "good"))
  1137.           SkillNr = 1;
  1138.      else if (!strcasecmp(skill, "medium"))
  1139.           SkillNr = 2;
  1140.      else if (!strcasecmp(skill, "worse"))
  1141.           SkillNr = 3;
  1142.      else if (!strcasecmp(skill, "bad"))
  1143.           SkillNr = 4;
  1144.      else
  1145.      {
  1146.           conoutf("Wrong skill specified. Should be best, good, medium, worse or bad");
  1147.           return;
  1148.      }
  1149.      
  1150.      if (bot)
  1151.       {
  1152.            loopv(bots)
  1153.            {
  1154.                 if (bots[i] && !strcmp(bots[i]->name, bot))
  1155.                 {
  1156.                      BotManager.ChangeBotSkill(SkillNr, bots[i]);
  1157.                      break;
  1158.                 }
  1159.            }
  1160.       }
  1161.       else
  1162.            BotManager.ChangeBotSkill(SkillNr);
  1163. }
  1164.  
  1165. COMMAND(botskill, ARG_2STR);
  1166.  
  1167. void botskillall(char *skill)
  1168. {
  1169.      botskill(NULL, skill);
  1170. }
  1171.  
  1172. COMMAND(botskillall, ARG_1STR);
  1173.  
  1174. #ifndef RELEASE_BUILD
  1175.  
  1176. #ifdef VANILLA_CUBE
  1177. void drawbeamtocarrots()
  1178. {
  1179.      loopv(ents)
  1180.      {
  1181.           entity &e = ents[i];
  1182.           vec o = { e.x, e.y, S(e.x, e.y)->floor+player1->eyeheight };
  1183.           if ((e.type != CARROT) || !e.spawned) continue;
  1184.           particle_trail(1, 500, player1->o, o);
  1185.      }
  1186. }
  1187.  
  1188. COMMAND(drawbeamtocarrots, ARG_NONE);
  1189.  
  1190. void drawbeamtoteleporters()
  1191. {
  1192.      loopv(ents)
  1193.      {
  1194.           entity &e = ents[i];
  1195.           vec o = { e.x, e.y, S(e.x, e.y)->floor+player1->eyeheight };
  1196.           if (e.type != TELEPORT) continue;
  1197.           particle_trail(1, 500, player1->o, o);
  1198.      }
  1199. }
  1200.  
  1201. COMMAND(drawbeamtoteleporters, ARG_NONE);
  1202. #endif
  1203.  
  1204. void telebot(void)
  1205. {
  1206.      vec dest = player1->o, forward, right, up;
  1207.      vec angles(player1->pitch, player1->yaw, player1->roll);
  1208.      traceresult_s tr;
  1209.      
  1210.      AnglesToVectors(angles, forward, right, up);
  1211.      forward.mul(4.0f);
  1212.      dest.add(forward);
  1213.      
  1214.      TraceLine(player1->o, dest, player1, true, &tr);
  1215.      
  1216.      if (!tr.collided)
  1217.      {
  1218.           // Get the first bot
  1219.           loopv(bots)
  1220.           {
  1221.                if (!bots[i] || !bots[i]->pBot) continue;
  1222.                bots[i]->o = tr.end;
  1223.                break;
  1224.           }
  1225.      }
  1226. }
  1227.  
  1228. COMMAND(telebot, ARG_NONE);
  1229.  
  1230. void testvisible(int iDir)
  1231. {
  1232.     
  1233.      vec angles, end, forward, right, up;
  1234.      traceresult_s tr;
  1235.      int Dir;
  1236.      
  1237.      switch(iDir)
  1238.      {
  1239.           case 0: default: Dir = FORWARD; break;
  1240.           case 1: Dir = BACKWARD; break;
  1241.           case 2: Dir = LEFT; break;
  1242.           case 3: Dir = RIGHT; break;
  1243.           case 4: Dir = UP; break;
  1244.           case 5: Dir = DOWN; break;
  1245.      }
  1246.      
  1247.      vec from = player1->o;
  1248.      from.z -= (player1->eyeheight - 1.25f);
  1249.      end = from;
  1250.      makevec(&angles, player1->pitch, player1->yaw, player1->roll);
  1251.      angles.x=0;
  1252.      
  1253.      if (Dir & UP)
  1254.           angles.x = WrapXAngle(angles.x + 45.0f);
  1255.      else if (Dir & DOWN)
  1256.           angles.x = WrapXAngle(angles.x - 45.0f);
  1257.           
  1258.      if ((Dir & FORWARD) || (Dir & BACKWARD))
  1259.      {
  1260.           if (Dir & BACKWARD)
  1261.                angles.y = WrapYZAngle(angles.y + 180.0f);
  1262.           
  1263.           if (Dir & LEFT)
  1264.           {
  1265.                if (Dir & FORWARD)
  1266.                     angles.y = WrapYZAngle(angles.y - 45.0f);
  1267.                else
  1268.                     angles.y = WrapYZAngle(angles.y + 45.0f);
  1269.           }
  1270.           else if (Dir & RIGHT)
  1271.           {
  1272.                if (Dir & FORWARD)
  1273.                     angles.y = WrapYZAngle(angles.y + 45.0f);
  1274.                else
  1275.                     angles.y = WrapYZAngle(angles.y - 45.0f);
  1276.           }
  1277.      }
  1278.      else if (Dir & LEFT)
  1279.           angles.y = WrapYZAngle(angles.y - 90.0f);
  1280.      else if (Dir & RIGHT)
  1281.           angles.y = WrapYZAngle(angles.y + 90.0f);
  1282.      else if (Dir & UP)
  1283.           angles.x = WrapXAngle(angles.x + 90.0f);
  1284.      else if (Dir & DOWN)
  1285.           angles.x = WrapXAngle(angles.x - 90.0f);
  1286.           
  1287.      AnglesToVectors(angles, forward, right, up);
  1288.      
  1289.      forward.mul(20.0f);
  1290.      end.add(forward);
  1291.          
  1292.      TraceLine(from, end, player1, false, &tr);
  1293.      
  1294.      //debugbeam(from, tr.end);
  1295.      char sz[250];
  1296.      sprintf(sz, "dist: %f; hit: %d", GetDistance(from, tr.end), tr.collided);
  1297.      condebug(sz);
  1298. }
  1299.  
  1300. COMMAND(testvisible, ARG_1INT);
  1301.  
  1302. void mapsize(void)
  1303. {
  1304.      conoutf("ssize: %d", ssize);
  1305. }
  1306.  
  1307. COMMAND(mapsize, ARG_NONE);
  1308.                
  1309. #endif
  1310.