// // C++ Implementation: bot // // Description: Code for botmanager // // Main bot file // // Author: Rick // // // #include "bot.h" #ifndef VANILLA_CUBE // UNDONE bool dedserv = false; #define CS_DEDHOST 0xFF #endif extern ENetHost *clienthost; extern void respawnself(); CBotManager BotManager; // Bot manager class begin CBotManager::~CBotManager(void) { EndMap(); while(m_StoredBots.Empty() == false) delete m_StoredBots.Pop(); } void CBotManager::Init() { m_pBotToView = NULL; m_bBotsShoot = true; m_bIdleBots = false; m_iFrameTime = 0; m_iPrevTime = lastmillis; m_sBotSkill = 1; // Default all bots have the skill 'Good' CreateSkillData(); LoadBotNamesFile(); LoadBotTeamsFile(); //WaypointClass.Init(); lsrand(time(NULL)); } void CBotManager::Think() { if (m_bInit) { Init(); m_bInit = false; } if (m_pBotToView) ViewBot(); AddDebugText("m_sMaxAStarBots: %d", m_sMaxAStarBots); AddDebugText("m_sCurrentTriggerNr: %d", m_sCurrentTriggerNr); short x, y; WaypointClass.GetNodeIndexes(player1->o, &x, &y); AddDebugText("x: %d y: %d", x, y); m_iFrameTime = lastmillis - m_iPrevTime; if (m_iFrameTime > 250) m_iFrameTime = 250; m_iPrevTime = lastmillis; // Is it time to re-add bots? if ((m_fReAddBotDelay < lastmillis) && (m_fReAddBotDelay != -1.0f)) { while(m_StoredBots.Empty() == false) { CStoredBot *pStoredBot = m_StoredBots.Pop(); ReAddBot(pStoredBot); delete pStoredBot; } m_fReAddBotDelay = -1.0f; } // If this is a ded server check if there are any players, if not bots should be idle if (dedserv) { bool botsbeidle = true; loopv(players) { if (players[i] && (players[i]->state != CS_DEDHOST)) { botsbeidle = false; break; } } if (botsbeidle) return; } // Let all bots 'think' loopv(bots) { if (!bots[i]) continue; if (bots[i]->pBot) bots[i]->pBot->Think(); else condebug("Error: pBot == NULL in bot ent\n"); } } void CBotManager::LoadBotNamesFile() { // Init bot names array first for (int i=0;i<100;i++) strcpy(m_szBotNames[i], "Bot"); m_sBotNameCount = 0; // Load bot file char szNameFileName[256]; MakeBotFileName("bot_names.txt", NULL, NULL, szNameFileName); FILE *fp = fopen(szNameFileName, "r"); char szNameBuffer[256]; int iIndex, iStrIndex; if (!fp) { conoutf("Warning: Couldn't load bot names file"); return; } while (fgets(szNameBuffer, 80, fp) != NULL) { if (m_sBotNameCount >= 150) { conoutf("Warning: Max bot names reached(150), ignoring the rest of the" "names"); break; } short length = (short)strlen(szNameBuffer); if (szNameBuffer[length-1] == '\n') { szNameBuffer[length-1] = 0; // remove '\n' length--; } iStrIndex = 0; while (iStrIndex < length) { if ((szNameBuffer[iStrIndex] < ' ') || (szNameBuffer[iStrIndex] > '~') || (szNameBuffer[iStrIndex] == '"')) { for (iIndex=iStrIndex; iIndex < length; iIndex++) szNameBuffer[iIndex] = szNameBuffer[iIndex+1]; } iStrIndex++; } if (szNameBuffer[0] != 0) { if (strlen(szNameBuffer) >= 16) { conoutf("Warning: bot name \"%s\" has to many characters(16 is max)", szNameBuffer); } s_strncpy(m_szBotNames[m_sBotNameCount], szNameBuffer, 16); m_sBotNameCount++; } } fclose(fp); } char *CBotManager::GetBotName() { char *szOutput = NULL; TMultiChoice BotNameChoices; short ChoiceVal; for(int j=0;jstate != CS_DEDHOST) && !strcasecmp(players[i]->name, m_szBotNames[j])) ChoiceVal -= 10; } loopv(bots) { if (bots[i] && (!strcasecmp(bots[i]->name, m_szBotNames[j]))) ChoiceVal -= 10; } if ((player1->state != CS_DEDHOST) && !strcasecmp(player1->name, m_szBotNames[j])) ChoiceVal -= 10; if (ChoiceVal <= 0) ChoiceVal = 1; BotNameChoices.Insert(m_szBotNames[j], ChoiceVal); } // Couldn't find a selection? if (!BotNameChoices.GetSelection(szOutput)) szOutput = "Bot"; return szOutput; } void CBotManager::LoadBotTeamsFile() { // Init bot teams array first for (int i=0;i<20;i++) strcpy(m_szBotTeams[i], "b0ts"); m_sBotTeamCount = 0; // Load bot file char szNameFileName[256]; MakeBotFileName("bot_teams.txt", NULL, NULL, szNameFileName); FILE *fp = fopen(szNameFileName, "r"); char szNameBuffer[256]; int iIndex, iStrIndex; if (!fp) { conoutf("Warning: Couldn't load bot teams file"); return; } while ((m_sBotTeamCount < 20) && (fgets(szNameBuffer, 80, fp) != NULL)) { short length = (short)strlen(szNameBuffer); if (szNameBuffer[length-1] == '\n') { szNameBuffer[length-1] = 0; // remove '\n' length--; } iStrIndex = 0; while (iStrIndex < length) { if ((szNameBuffer[iStrIndex] < ' ') || (szNameBuffer[iStrIndex] > '~') || (szNameBuffer[iStrIndex] == '"')) { for (iIndex=iStrIndex; iIndex < length; iIndex++) szNameBuffer[iIndex] = szNameBuffer[iIndex+1]; } iStrIndex++; } if (szNameBuffer[0] != 0) { s_strncpy(m_szBotTeams[m_sBotTeamCount], szNameBuffer, 5); m_sBotTeamCount++; } } fclose(fp); } char *CBotManager::GetBotTeam() { char *szOutput = NULL; TMultiChoice BotTeamChoices; short ChoiceVal; for(int j=0;jname, m_szBotNames[j]))) ChoiceVal -= 10; } loopv(bots) { if (bots[i] && (!strcasecmp(bots[i]->name, m_szBotNames[j]))) ChoiceVal -= 10; } if (!strcasecmp(player1->name, m_szBotNames[j])) ChoiceVal -= 10; if (ChoiceVal <= 0) ChoiceVmonsterclearal = 1;*/ BotTeamChoices.Insert(m_szBotTeams[j], ChoiceVal); } // Couldn't find a selection? if (!BotTeamChoices.GetSelection(szOutput)) szOutput = "b0t"; return szOutput; } void CBotManager::RenderBots() { //static bool drawblue; loopv(bots) { if (bots[i] && (bots[i] != m_pBotToView)) { /*drawblue = (m_sp || isteam(player1->team, bots[i]->team)); renderclient(bots[i], drawblue, "playermodels/counterterrorist", 1.6f);*/ renderclient(bots[i]); } } } void CBotManager::EndMap() { // Remove all bots loopv(bots) { if(!bots[i] || !bots[i]->pBot) continue; // Store bots so they can be re-added after map change if (bots[i]->pBot && bots[i]->name[0] && bots[i]->team[0]) { CStoredBot *pStoredBot = new CStoredBot(bots[i]->name, bots[i]->team, bots[i]->pBot->m_sSkillNr); m_StoredBots.AddNode(pStoredBot); } delete bots[i]->pBot; bots[i]->pBot = NULL; freebotent(bots[i]); } bots.setsize(0); condebug("Cleared all bots"); m_fReAddBotDelay = lastmillis + 7500; //if(ishost()) WaypointClass.SaveWPExpFile(); //UNDONE } void CBotManager::BeginMap(char *szMapName) { EndMap(); // End previous map WaypointClass.Init(); WaypointClass.SetMapName(szMapName); if (!WaypointClass.LoadWaypoints()) WaypointClass.StartFlood(); //WaypointClass.LoadWPExpFile(); // UNDONE CalculateMaxAStarCount(); m_sUsingAStarBotsCount = 0; PickNextTrigger(); } int CBotManager::GetBotIndex(botent *m) { loopv(bots) { if (!bots[i]) continue; if (bots[i] == m) return i; } return -1; } void CBotManager::LetBotsUpdateStats() { loopv(bots) if (bots[i] && bots[i]->pBot) bots[i]->pBot->m_bSendC2SInit = false; } void CBotManager::LetBotsHear(int n, vec *loc) { if (bots.length() == 0 || !loc) return; loopv(bots) { if (!bots[i] || !bots[i]->pBot || (bots[i]->state == CS_DEAD)) continue; bots[i]->pBot->HearSound(n, loc); } } // Notify all bots of a new waypoint void CBotManager::AddWaypoint(node_s *pNode) { if (bots.length()) { short x, y; waypoint_s *pWP; loopv(bots) { if (!bots[i] || !bots[i]->pBot) continue; pWP = new waypoint_s; pWP->pNode = pNode; WaypointClass.GetNodeIndexes(pNode->v_origin, &x, &y); bots[i]->pBot->m_WaypointList[x][y].AddNode(pWP); #ifndef RELEASE_BUILD if (!bots[i]->pBot->GetWPFromNode(pNode)) condebug("Error adding bot wp!"); #endif } } CalculateMaxAStarCount(); } // Notify all bots of a deleted waypoint void CBotManager::DelWaypoint(node_s *pNode) { if (bots.length()) { short x, y; TLinkedList::node_s *p; loopv(bots) { if (!bots[i] || !bots[i]->pBot) continue; WaypointClass.GetNodeIndexes(pNode->v_origin, &x, &y); p = bots[i]->pBot->m_WaypointList[x][y].GetFirst(); while(p) { if (p->Entry->pNode == pNode) { delete p->Entry; bots[i]->pBot->m_WaypointList[x][y].DeleteNode(p); break; } p = p->next; } } } CalculateMaxAStarCount(); } void CBotManager::MakeBotFileName(const char *szFileName, const char *szDir1, const char *szDir2, char *szOutput) { char *DirSeperator; #ifdef WIN32 DirSeperator = "\\"; strcpy(szOutput, "bot\\"); #else DirSeperator = "/"; strcpy(szOutput, "bot/"); #endif if (szDir1) { strcat(szOutput, szDir1); strcat(szOutput, DirSeperator); } if (szDir2) { strcat(szOutput, szDir2); strcat(szOutput, DirSeperator); } strcat(szOutput, szFileName); } void CBotManager::CreateSkillData() { // First give the bot skill structure some default data InitSkillData(); // Now see if we can load the skill.cfg file char SkillFileName[256] = ""; FILE *pSkillFile = NULL; int SkillNr = -1; float value = 0; MakeBotFileName("bot_skill.cfg", NULL, NULL, SkillFileName); pSkillFile = fopen(SkillFileName, "r"); conoutf("Reading bot_skill.cfg file... "); if (pSkillFile == NULL) // file doesn't exist { conoutf("skill file not found, default settings will be used\n"); return; } int ch; char cmd_line[256]; int cmd_index; char *cmd, *arg1; while (pSkillFile) { cmd_index = 0; cmd_line[cmd_index] = 0; ch = fgetc(pSkillFile); // skip any leading blanks while (ch == ' ') ch = fgetc(pSkillFile); while ((ch != EOF) && (ch != '\r') && (ch != '\n')) { if (ch == '\t') // convert tabs to spaces ch = ' '; cmd_line[cmd_index] = ch; ch = fgetc(pSkillFile); // skip multiple spaces in input file while ((cmd_line[cmd_index] == ' ') && (ch == ' ')) ch = fgetc(pSkillFile); cmd_index++; } if (ch == '\r') // is it a carriage return? { ch = fgetc(pSkillFile); // skip the linefeed } // if reached end of file, then close it if (ch == EOF) { fclose(pSkillFile); pSkillFile = NULL; } cmd_line[cmd_index] = 0; // terminate the command line cmd_index = 0; cmd = cmd_line; arg1 = NULL; // skip to blank or end of string... while ((cmd_line[cmd_index] != ' ') && (cmd_line[cmd_index] != 0)) cmd_index++; if (cmd_line[cmd_index] == ' ') { cmd_line[cmd_index++] = 0; arg1 = &cmd_line[cmd_index]; } if ((cmd_line[0] == '#') || (cmd_line[0] == 0)) continue; // skip if comment or blank line if (strcasecmp(cmd, "[SKILL1]") == 0) SkillNr = 0; else if (strcasecmp(cmd, "[SKILL2]") == 0) SkillNr = 1; else if (strcasecmp(cmd, "[SKILL3]") == 0) SkillNr = 2; else if (strcasecmp(cmd, "[SKILL4]") == 0) SkillNr = 3; else if (strcasecmp(cmd, "[SKILL5]") == 0) SkillNr = 4; if ((arg1 == NULL) || (*arg1 == 0)) continue; if (SkillNr == -1) // Not in a skill block yet? continue; value = atof(arg1); if (strcasecmp(cmd, "min_x_aim_speed") == 0) { m_BotSkills[SkillNr].flMinAimXSpeed = value; } else if (strcasecmp(cmd, "max_x_aim_speed") == 0) { m_BotSkills[SkillNr].flMaxAimXSpeed = value; } else if (strcasecmp(cmd, "min_y_aim_speed") == 0) { m_BotSkills[SkillNr].flMinAimYSpeed = value; } else if (strcasecmp(cmd, "max_y_aim_speed") == 0) { m_BotSkills[SkillNr].flMaxAimYSpeed = value; } else if (strcasecmp(cmd, "min_x_aim_offset") == 0) { m_BotSkills[SkillNr].flMinAimXOffset = value; } else if (strcasecmp(cmd, "max_x_aim_offset") == 0) { m_BotSkills[SkillNr].flMaxAimXOffset = value; } else if (strcasecmp(cmd, "min_y_aim_offset") == 0) { m_BotSkills[SkillNr].flMinAimYOffset = value; } else if (strcasecmp(cmd, "max_y_aim_offset") == 0) { m_BotSkills[SkillNr].flMaxAimYOffset = value; } else if (strcasecmp(cmd, "min_attack_delay") == 0) { m_BotSkills[SkillNr].flMinAttackDelay = value; } else if (strcasecmp(cmd, "max_attack_delay") == 0) { m_BotSkills[SkillNr].flMaxAttackDelay = value; } else if (strcasecmp(cmd, "min_enemy_search_delay") == 0) { m_BotSkills[SkillNr].flMinEnemySearchDelay = value; } else if (strcasecmp(cmd, "max_enemy_search_delay") == 0) { m_BotSkills[SkillNr].flMaxEnemySearchDelay = value; } else if (strcasecmp(cmd, "shoot_at_feet_percent") == 0) { if (value < 0) value = 0; else if (value > 100) value = 100; m_BotSkills[SkillNr].sShootAtFeetWithRLPercent = (short)value; } else if (strcasecmp(cmd, "can_predict_position") == 0) { m_BotSkills[SkillNr].bCanPredict = value!=0; } else if (strcasecmp(cmd, "max_hear_volume") == 0) { if (value < 0) value = 0; else if (value > 255) value = 100; m_BotSkills[SkillNr].iMaxHearVolume = (int)value; } else if (strcasecmp(cmd, "can_circle_strafe") == 0) { m_BotSkills[SkillNr].bCircleStrafe = value!=0; } else if (strcasecmp(cmd, "can_search_items_in_combat") == 0) { m_BotSkills[SkillNr].bCanSearchItemsInCombat = value!=0; } } conoutf("done\n"); } void CBotManager::InitSkillData() { // Best skill m_BotSkills[0].flMinReactionDelay = 0.015f; m_BotSkills[0].flMaxReactionDelay = 0.035f; m_BotSkills[0].flMinAimXOffset = 15.0f; m_BotSkills[0].flMaxAimXOffset = 20.0f; m_BotSkills[0].flMinAimYOffset = 10.0f; m_BotSkills[0].flMaxAimYOffset = 15.0f; m_BotSkills[0].flMinAimXSpeed = 330.0f; m_BotSkills[0].flMaxAimXSpeed = 355.0f; m_BotSkills[0].flMinAimYSpeed = 400.0f; m_BotSkills[0].flMaxAimYSpeed = 450.0f; m_BotSkills[0].flMinAttackDelay = 0.1f; m_BotSkills[0].flMaxAttackDelay = 0.4f; m_BotSkills[0].flMinEnemySearchDelay = 0.09f; m_BotSkills[0].flMaxEnemySearchDelay = 0.12f; m_BotSkills[0].sShootAtFeetWithRLPercent = 85; m_BotSkills[0].bCanPredict = true; m_BotSkills[0].iMaxHearVolume = 75; m_BotSkills[0].bCircleStrafe = true; m_BotSkills[0].bCanSearchItemsInCombat = true; // Good skill m_BotSkills[1].flMinReactionDelay = 0.035f; m_BotSkills[1].flMaxReactionDelay = 0.045f; m_BotSkills[1].flMinAimXOffset = 20.0f; m_BotSkills[1].flMaxAimXOffset = 25.0f; m_BotSkills[1].flMinAimYOffset = 15.0f; m_BotSkills[1].flMaxAimYOffset = 20.0f; m_BotSkills[1].flMinAimXSpeed = 250.0f; m_BotSkills[1].flMaxAimXSpeed = 265.0f; m_BotSkills[1].flMinAimYSpeed = 260.0f; m_BotSkills[1].flMaxAimYSpeed = 285.0f; m_BotSkills[1].flMinAttackDelay = 0.3f; m_BotSkills[1].flMaxAttackDelay = 0.6f; m_BotSkills[1].flMinEnemySearchDelay = 0.12f; m_BotSkills[1].flMaxEnemySearchDelay = 0.17f; m_BotSkills[1].sShootAtFeetWithRLPercent = 60; m_BotSkills[1].bCanPredict = true; m_BotSkills[1].iMaxHearVolume = 60; m_BotSkills[1].bCircleStrafe = true; m_BotSkills[1].bCanSearchItemsInCombat = true; // Medium skill m_BotSkills[2].flMinReactionDelay = 0.075f; m_BotSkills[2].flMaxReactionDelay = 0.010f; m_BotSkills[2].flMinAimXOffset = 25.0f; m_BotSkills[2].flMaxAimXOffset = 30.0f; m_BotSkills[2].flMinAimYOffset = 20.0f; m_BotSkills[2].flMaxAimYOffset = 25.0f; m_BotSkills[2].flMinAimXSpeed = 190.0f; m_BotSkills[2].flMaxAimXSpeed = 125.0f; m_BotSkills[2].flMinAimYSpeed = 210.0f; m_BotSkills[2].flMaxAimYSpeed = 240.0f; m_BotSkills[2].flMinAttackDelay = 0.75f; m_BotSkills[2].flMaxAttackDelay = 1.0f; m_BotSkills[2].flMinEnemySearchDelay = 0.18f; m_BotSkills[2].flMaxEnemySearchDelay = 0.22f; m_BotSkills[2].sShootAtFeetWithRLPercent = 25; m_BotSkills[2].bCanPredict = false; m_BotSkills[2].iMaxHearVolume = 45; m_BotSkills[2].bCircleStrafe = true; m_BotSkills[2].bCanSearchItemsInCombat = false; // Worse skill m_BotSkills[3].flMinReactionDelay = 0.15f; m_BotSkills[3].flMaxReactionDelay = 0.20f; m_BotSkills[3].flMinAimXOffset = 30.0f; m_BotSkills[3].flMaxAimXOffset = 35.0f; m_BotSkills[3].flMinAimYOffset = 25.0f; m_BotSkills[3].flMaxAimYOffset = 30.0f; m_BotSkills[3].flMinAimXSpeed = 155.0f; m_BotSkills[3].flMaxAimXSpeed = 170.0f; m_BotSkills[3].flMinAimYSpeed = 160.0f; m_BotSkills[3].flMaxAimYSpeed = 210.0f; m_BotSkills[3].flMinAttackDelay = 1.2f; m_BotSkills[3].flMaxAttackDelay = 1.6f; m_BotSkills[3].flMinEnemySearchDelay = 0.25f; m_BotSkills[3].flMaxEnemySearchDelay = 0.30f; m_BotSkills[3].sShootAtFeetWithRLPercent = 10; m_BotSkills[3].bCanPredict = false; m_BotSkills[3].iMaxHearVolume = 30; m_BotSkills[3].bCircleStrafe = false; m_BotSkills[3].bCanSearchItemsInCombat = false; // Bad skill m_BotSkills[4].flMinReactionDelay = 0.30f; m_BotSkills[4].flMaxReactionDelay = 0.50f; m_BotSkills[4].flMinAimXOffset = 35.0f; m_BotSkills[4].flMaxAimXOffset = 40.0f; m_BotSkills[4].flMinAimYOffset = 30.0f; m_BotSkills[4].flMaxAimYOffset = 35.0f; m_BotSkills[4].flMinAimXSpeed = 45.0f; m_BotSkills[4].flMaxAimXSpeed = 60.0f; m_BotSkills[4].flMinAimYSpeed = 125.0f; m_BotSkills[4].flMaxAimYSpeed = 180.0f; m_BotSkills[4].flMinAttackDelay = 1.5f; m_BotSkills[4].flMaxAttackDelay = 2.0f; m_BotSkills[4].flMinEnemySearchDelay = 0.30f; m_BotSkills[4].flMaxEnemySearchDelay = 0.36f; m_BotSkills[4].sShootAtFeetWithRLPercent = 0; m_BotSkills[4].bCanPredict = false; m_BotSkills[4].iMaxHearVolume = 15; m_BotSkills[4].bCircleStrafe = false; m_BotSkills[4].bCanSearchItemsInCombat = false; } void CBotManager::ChangeBotSkill(short Skill, botent *bot) { static char *SkillNames[5] = { "best", "good", "medium", "worse", "bad" }; if (bot && bot->pBot) { // Only change skill of a single bot bot->pBot->m_pBotSkill = &m_BotSkills[Skill]; bot->pBot->m_sSkillNr = Skill; conoutf("Skill of %s is now %s", bot->name, SkillNames[Skill]); return; } // Change skill of all bots loopv(bots) { if (!bots[i] || !bots[i]->pBot) continue; bots[i]->pBot->m_pBotSkill = &m_BotSkills[Skill]; bots[i]->pBot->m_sSkillNr = Skill; } // Change default bot skill m_sBotSkill = Skill; conoutf("Skill of all bots is now %s", SkillNames[Skill]); } void CBotManager::ViewBot() { // Check if this bot is still in game bool bFound = false; loopv(bots) { if (bots[i] == m_pBotToView) { bFound = true; break; } } if (!bFound) { DisableBotView(); return; } player1->state = CS_DEAD; // Fake dead player1->o = m_pBotToView->o; player1->o.z += 1.0f; player1->yaw = m_pBotToView->yaw; player1->pitch = m_pBotToView->pitch; player1->roll = m_pBotToView->roll; player1->radius = 0; // Don't collide } void CBotManager::DisableBotView() { m_pBotToView = NULL; respawnself(); player1->radius = 1.1f; } void CBotManager::CalculateMaxAStarCount() { if (WaypointClass.m_iWaypointCount > 0) // Are there any waypoints? { m_sMaxAStarBots = 8 - short(ceil((float)WaypointClass.m_iWaypointCount / 1000.0f)); if (m_sMaxAStarBots < 1) m_sMaxAStarBots = 1; } else m_sMaxAStarBots = 1; } void CBotManager::PickNextTrigger() { short lowest = -1; bool found0 = false; // True if found a trigger with nr 0 loopv(ents) { entity &e = ents[i]; #if defined AC_CUBE /* if ((e.type != TRIGGER) || !e.spawned) continue;*/ #elif defined VANILLA_CUBE if ((e.type != CARROT) || !e.spawned) continue; #endif if (OUTBORD(e.x, e.y)) continue; vec o(e.x, e.y, S(e.x, e.y)->floor+player1->eyeheight); node_s *pWptNearEnt = NULL; pWptNearEnt = WaypointClass.GetNearestTriggerWaypoint(o, 2.0f); if (pWptNearEnt) { if ((pWptNearEnt->sTriggerNr > 0) && ((pWptNearEnt->sTriggerNr < lowest) || (lowest == -1))) lowest = pWptNearEnt->sTriggerNr; if (pWptNearEnt->sTriggerNr == 0) found0 = true; } #ifdef WP_FLOOD pWptNearEnt = WaypointClass.GetNearestTriggerFloodWP(o, 2.0f); if (pWptNearEnt) { if ((pWptNearEnt->sTriggerNr > 0) && ((pWptNearEnt->sTriggerNr < lowest) || (lowest == -1))) lowest = pWptNearEnt->sTriggerNr; if (pWptNearEnt->sTriggerNr == 0) found0 = true; } #endif } if ((lowest == -1) && found0) lowest = 0; if (lowest != -1) m_sCurrentTriggerNr = lowest; } botent *CBotManager::CreateBot(const char *team, const char *skill, const char *name) { botent *m = newbotent(); if (!m) return NULL; loopi(NUMGUNS) m->ammo[i] = m->mag[i] = 0; setskin(m, rnd(6)); // Create new bot class, dependand on the current mod #if defined VANILLA_CUBE m->pBot = new CCubeBot; #elif defined AC_CUBE m->pBot = new CACBot; #else #error "Unsupported mod!" #endif m->type = ENT_BOT; m->pBot->m_pMyEnt = m; m->pBot->m_iLastBotUpdate = 0; m->pBot->m_bSendC2SInit = false; if (name && *name) s_strncpy(m->name, name, 16); else s_strncpy(m->name, BotManager.GetBotName(), 16); if (team && *team && strcmp(team, "random")) s_strncpy(m->team, team, 5); else s_strncpy(m->team, BotManager.GetBotTeam(), 5); if (skill && *skill && strcmp(skill, "random")) { if (!strcasecmp(skill, "best")) m->pBot->m_sSkillNr = 0; else if (!strcasecmp(skill, "good")) m->pBot->m_sSkillNr = 1; else if (!strcasecmp(skill, "medium")) m->pBot->m_sSkillNr = 2; else if (!strcasecmp(skill, "worse")) m->pBot->m_sSkillNr = 3; else if (!strcasecmp(skill, "bad")) m->pBot->m_sSkillNr = 4; else { conoutf("Wrong skill specified. Should be best, good, medium, " "worse or bad"); conoutf("Using default skill instead..."); m->pBot->m_sSkillNr = BotManager.m_sBotSkill; } } else // No skill specified, use default m->pBot->m_sSkillNr = BotManager.m_sBotSkill; m->pBot->m_pBotSkill = &BotManager.m_BotSkills[m->pBot->m_sSkillNr]; // Sync waypoints m->pBot->SyncWaypoints(); m->pBot->Spawn(); bots.add(m); return m; } bool botmode() { if(m_botmode) return true; conoutf("the current game mode does not support bots"); return false; } // Bot manager class end void addbot(char *arg1, char *arg2, char *arg3) { if(!botmode()) return; conoutf("Creating bot...\n"); botent *b = BotManager.CreateBot(arg1, arg2, arg3); if (b) conoutf("connected: %s", b->name); else { conoutf("Error: Couldn't create bot!"); return; } } COMMAND(addbot, ARG_3STR); void addnbot(char *arg1, char *arg2, char *arg3) { if(!botmode()) return; if (!arg1 || !arg1[0]) return; int i = atoi(arg1); while(i > 0) { addbot(arg2, arg3, NULL); i--; } } COMMAND(addnbot, ARG_3STR); void botsshoot(int Shoot) { if (Shoot) { BotManager.SetBotsShoot(true); conoutf("Bots will shoot"); } else { BotManager.SetBotsShoot(false); conoutf("Bots won't shoot"); } } COMMAND(botsshoot, ARG_1INT); void idlebots(int Idle) { if (Idle) { BotManager.SetIdleBots(true); conoutf("Bots are idle"); } else { BotManager.SetIdleBots(false); conoutf("Bots aren't idle"); } } COMMAND(idlebots, ARG_1INT); void drawbeamtobots() { if(!botmode()) return; loopv(bots) { if (bots[i]) particle_trail(1, 500, player1->o, bots[i]->o); } } COMMAND(drawbeamtobots, ARG_NONE); void kickbot(const char *szName) { if(!botmode()) return; if (!szName || !(*szName)) return; int iBotInd = -1; loopv(bots) { if (!bots[i]) continue; if (!strcmp(bots[i]->name, szName)) { iBotInd = i; break; } } if (iBotInd != -1) { botent *d = bots[iBotInd]; if(d->name[0]) conoutf("bot %s disconnected", d->name); delete d->pBot; bots.remove(iBotInd); freebotent(d); } } COMMAND(kickbot, ARG_1STR); void kickallbots(void) { BotManager.ClearStoredBots(); loopv(bots) { if (bots[i]) { if(bots[i]->name[0]) conoutf("bot %s disconnected", bots[i]->name); delete bots[i]->pBot; freebotent(bots[i]); } }; bots.setsize(0); } COMMAND(kickallbots, ARG_NONE); void togglegrap() { if(!botmode()) return; if (SDL_WM_GrabInput(SDL_GRAB_QUERY) == SDL_GrabMode(0)) SDL_WM_GrabInput(SDL_GRAB_ON); else SDL_WM_GrabInput(SDL_GrabMode(0)); } COMMAND(togglegrap, ARG_NONE); void togglebotview(char *bot) { if(!botmode()) return; if (BotManager.m_pBotToView) BotManager.DisableBotView(); else if (bot && *bot) { loopv(bots) { if (!bots[i]) continue; if (!strcmp(bots[i]->name, bot)) { BotManager.EnableBotView(bots[i]); break; } } } } COMMAND(togglebotview, ARG_1STR); void botskill(char *bot, char *skill) { if (!skill || !(*skill)) return; short SkillNr; if (!strcasecmp(skill, "best")) SkillNr = 0; else if (!strcasecmp(skill, "good")) SkillNr = 1; else if (!strcasecmp(skill, "medium")) SkillNr = 2; else if (!strcasecmp(skill, "worse")) SkillNr = 3; else if (!strcasecmp(skill, "bad")) SkillNr = 4; else { conoutf("Wrong skill specified. Should be best, good, medium, worse or bad"); return; } if (bot) { loopv(bots) { if (bots[i] && !strcmp(bots[i]->name, bot)) { BotManager.ChangeBotSkill(SkillNr, bots[i]); break; } } } else BotManager.ChangeBotSkill(SkillNr); } COMMAND(botskill, ARG_2STR); void botskillall(char *skill) { botskill(NULL, skill); } COMMAND(botskillall, ARG_1STR); #ifndef RELEASE_BUILD #ifdef VANILLA_CUBE void drawbeamtocarrots() { loopv(ents) { entity &e = ents[i]; vec o = { e.x, e.y, S(e.x, e.y)->floor+player1->eyeheight }; if ((e.type != CARROT) || !e.spawned) continue; particle_trail(1, 500, player1->o, o); } } COMMAND(drawbeamtocarrots, ARG_NONE); void drawbeamtoteleporters() { loopv(ents) { entity &e = ents[i]; vec o = { e.x, e.y, S(e.x, e.y)->floor+player1->eyeheight }; if (e.type != TELEPORT) continue; particle_trail(1, 500, player1->o, o); } } COMMAND(drawbeamtoteleporters, ARG_NONE); #endif void telebot(void) { vec dest = player1->o, forward, right, up; vec angles(player1->pitch, player1->yaw, player1->roll); traceresult_s tr; AnglesToVectors(angles, forward, right, up); forward.mul(4.0f); dest.add(forward); TraceLine(player1->o, dest, player1, true, &tr); if (!tr.collided) { // Get the first bot loopv(bots) { if (!bots[i] || !bots[i]->pBot) continue; bots[i]->o = tr.end; break; } } } COMMAND(telebot, ARG_NONE); void testvisible(int iDir) { vec angles, end, forward, right, up; traceresult_s tr; int Dir; switch(iDir) { case 0: default: Dir = FORWARD; break; case 1: Dir = BACKWARD; break; case 2: Dir = LEFT; break; case 3: Dir = RIGHT; break; case 4: Dir = UP; break; case 5: Dir = DOWN; break; } vec from = player1->o; from.z -= (player1->eyeheight - 1.25f); end = from; makevec(&angles, player1->pitch, player1->yaw, player1->roll); angles.x=0; if (Dir & UP) angles.x = WrapXAngle(angles.x + 45.0f); else if (Dir & DOWN) angles.x = WrapXAngle(angles.x - 45.0f); if ((Dir & FORWARD) || (Dir & BACKWARD)) { if (Dir & BACKWARD) angles.y = WrapYZAngle(angles.y + 180.0f); if (Dir & LEFT) { if (Dir & FORWARD) angles.y = WrapYZAngle(angles.y - 45.0f); else angles.y = WrapYZAngle(angles.y + 45.0f); } else if (Dir & RIGHT) { if (Dir & FORWARD) angles.y = WrapYZAngle(angles.y + 45.0f); else angles.y = WrapYZAngle(angles.y - 45.0f); } } else if (Dir & LEFT) angles.y = WrapYZAngle(angles.y - 90.0f); else if (Dir & RIGHT) angles.y = WrapYZAngle(angles.y + 90.0f); else if (Dir & UP) angles.x = WrapXAngle(angles.x + 90.0f); else if (Dir & DOWN) angles.x = WrapXAngle(angles.x - 90.0f); AnglesToVectors(angles, forward, right, up); forward.mul(20.0f); end.add(forward); TraceLine(from, end, player1, false, &tr); //debugbeam(from, tr.end); char sz[250]; sprintf(sz, "dist: %f; hit: %d", GetDistance(from, tr.end), tr.collided); condebug(sz); } COMMAND(testvisible, ARG_1INT); void mapsize(void) { conoutf("ssize: %d", ssize); } COMMAND(mapsize, ARG_NONE); #endif