// // C++ Implementation: bot_ai // // Description: The AI part comes here(navigation, shooting etc) // // // Author: // // Code of CBot - Start #include "bot.h" extern weaponinfo_s WeaponInfoTable[MAX_WEAPONS]; vec CBot::GetEnemyPos(playerent *d) { // Aim offset idea by botman vec o = d->o, offset; float flDist = GetDistance(d->o), flScale; if (WeaponInfoTable[m_pMyEnt->gunselect].eWeaponType == TYPE_ROCKET) { // Bot is using a rocket launcher, aim at enemy feet? if (m_bShootAtFeet && !OUTBORD(d->o.x, d->o.y)) { // Only do this when enemy is fairly close to the ground vec end = o; end.z -= 900.0f; traceresult_s tr; TraceLine(o, end, NULL, false, &tr); if ((o.z - tr.end.z) < 8.0f) { end = o; end.z = tr.end.z; if (IsVisible(end)) { // Target at ground o.z = tr.end.z; } } } if (m_pBotSkill->bCanPredict) { // How higher the skill, how further the bot predicts float flPredictTime = RandomFloat(1.25f, 1.7f) / (m_sSkillNr+1); o = PredictPos(o, d->vel, flPredictTime); } } else { if (m_pBotSkill->bCanPredict) { // How higher the skill, how 'more' the bot predicts float flPredictTime = RandomFloat(0.8f, 1.2f) / (m_sSkillNr+1); o = PredictPos(o, d->vel, flPredictTime); } } if (flDist > 60.0f) flScale = 1.0f; else if (flDist > 6.0f) flScale = flDist / 60.0f; else flScale = 0.1f; switch (m_sSkillNr) { case 0: // no offset offset.x = 0; offset.y = 0; offset.z = 0; break; case 1: // GOOD, offset a little for x, y, and z offset.x = RandomFloat(-3, 3) * flScale; offset.y = RandomFloat(-3, 3) * flScale; offset.z = RandomFloat(-6, 6) * flScale; break; case 2: // FAIR, offset somewhat for x, y, and z offset.x = RandomFloat(-8, 8) * flScale; offset.y = RandomFloat(-8, 8) * flScale; offset.z = RandomFloat(-12, 12) * flScale; break; case 3: // POOR, offset for x, y, and z offset.x = RandomFloat(-15, 15) * flScale; offset.y = RandomFloat(-15, 15) * flScale; offset.z = RandomFloat(-25, 25) * flScale; break; case 4: // BAD, offset lots for x, y, and z offset.x = RandomFloat(-20, 20) * flScale; offset.y = RandomFloat(-20, 20) * flScale; offset.z = RandomFloat(-35, 35) * flScale; break; } o.add(offset); return o; } bool CBot::FindEnemy(void) { // UNDONE: Enemies are now only scored on their distance if (m_pMyEnt->enemy) // Bot already has an enemy { // Check if the enemy is still in game bool found = IsInGame(m_pMyEnt->enemy); // Check if the enemy is still ingame, still alive, not joined my team and is visible if (found && !isteam(m_pMyEnt->team, m_pMyEnt->enemy->team)) { if ((m_pMyEnt->enemy->state == CS_ALIVE) && (IsVisible(m_pMyEnt->enemy))) return true; else m_pPrevEnemy = m_pMyEnt->enemy; } else m_pMyEnt->enemy = NULL; } if (m_iEnemySearchDelay > lastmillis) return (m_pMyEnt->enemy!=NULL); m_pMyEnt->enemy = NULL; // Add enemy searchy delay float MinDelay = m_pBotSkill->flMinEnemySearchDelay; float MaxDelay = m_pBotSkill->flMaxEnemySearchDelay; m_iEnemySearchDelay = lastmillis + int(RandomFloat(MinDelay, MaxDelay) * 1000.0f); playerent *pNewEnemy = NULL, *d = NULL; float flDist, flNearestDist = 99999.9f; short EnemyVal, BestEnemyVal = -100; // First loop through all players loopv(players) { d = players[i]; // Handy shortcut if (d == m_pMyEnt || !d || isteam(d->team, m_pMyEnt->team) || (d->state != CS_ALIVE)) continue; // Check if the enemy is visible if(!IsInFOV(d) || !IsVisible(d)) continue; flDist = GetDistance(d->o); EnemyVal = 1; if (flDist < flNearestDist) { EnemyVal+=2; flNearestDist = flDist; } if (EnemyVal > BestEnemyVal) { pNewEnemy = d; BestEnemyVal = EnemyVal; } } // Then examine the local player if (player1 && !isteam(player1->team, m_pMyEnt->team) && (player1->state == CS_ALIVE)) { // Check if the enemy is visible if(IsInFOV(player1) && IsVisible(player1)) { flDist = GetDistance(player1->o); EnemyVal = 1; if (flDist < flNearestDist) { EnemyVal+=2; flNearestDist = flDist; } if (EnemyVal > BestEnemyVal) { pNewEnemy = player1; BestEnemyVal = EnemyVal; } } } //} if (pNewEnemy) { if (!m_pMyEnt->enemy) // Add shoot delay if new enemy is found { float flMinShootDelay = m_pBotSkill->flMinAttackDelay; float flMaxShootDelay = m_pBotSkill->flMaxAttackDelay; m_iShootDelay = lastmillis + int(RandomFloat(flMinShootDelay, flMaxShootDelay) * 1000.0f); } if ((m_pMyEnt->enemy != pNewEnemy) && m_pMyEnt->enemy) m_pPrevEnemy = m_pMyEnt->enemy; m_pMyEnt->enemy = pNewEnemy; return true; } return false; } bool CBot::CheckHunt(void) { if (!BotManager.BotsShoot()) return false; if (m_pHuntTarget) // Bot already has an enemy to hunt { // Check if the enemy is still in game bool found = IsInGame(m_pHuntTarget); // Check if the enemy is still ingame, still alive, not joined my team and is visible if (found && !isteam(m_pMyEnt->team, m_pHuntTarget->team)) { if ((m_pHuntTarget->state == CS_ALIVE) && IsReachable(m_vHuntLocation)) return true; } else m_pHuntTarget = NULL; } if (m_iHuntDelay > lastmillis) return (m_pHuntTarget!=NULL); if (m_vHuntLocation!=g_vecZero) m_vPrevHuntLocation = m_vHuntLocation; m_pHuntTarget = NULL; m_vHuntLocation = g_vecZero; // Add enemy hunt search delay m_iHuntDelay = lastmillis + 1500; playerent *pNewEnemy = NULL, *d = NULL; float flDist, flNearestDist = 99999.9f, flNearestOldPosDistToEnemy = 99999.9f; float flNearestOldPosDistToBot = 99999.9f; short EnemyVal, BestEnemyVal = -100; vec BestOldPos; // First loop through all players loopv(players) { d = players[i]; // Handy shortcut if (d == m_pMyEnt || !d || isteam(d->team, m_pMyEnt->team) || (d->state != CS_ALIVE)) continue; flDist = GetDistance(d->o); if (flDist > 250.0f) continue; EnemyVal = 1; if (flDist < flNearestDist) { EnemyVal+=2; flNearestDist = flDist; } if (d == m_pPrevEnemy) EnemyVal+=2; if (EnemyVal < BestEnemyVal) continue; vec bestfromenemy = g_vecZero, bestfrombot = g_vecZero; flNearestOldPosDistToEnemy = flNearestOldPosDistToBot = 9999.9f; // Check previous locations of enemy for (int j=0;jhistory.size();j++) { const vec &v = d->history.getpos(j); if (v==m_vPrevHuntLocation) continue; flDist = GetDistance(d->o, v); if ((flDist < flNearestOldPosDistToEnemy) && IsReachable(v)) { flNearestOldPosDistToEnemy = flDist; bestfromenemy = v; } } // Check previous locations of bot hisself for (int j=0;jhistory.size();j++) { const vec &v = m_pMyEnt->history.getpos(j); if (v==m_vPrevHuntLocation) continue; flDist = GetDistance(v); if ((flDist < flNearestOldPosDistToBot) && ::IsVisible(d->o, v) && IsReachable(v)) { flNearestOldPosDistToBot = flDist; bestfrombot = v; } } if (bestfromenemy!=g_vecZero) { pNewEnemy = d; BestEnemyVal = EnemyVal; BestOldPos = bestfromenemy; } else if (bestfrombot!=g_vecZero) { pNewEnemy = d; BestEnemyVal = EnemyVal; BestOldPos = bestfrombot; } // Then examine the local player if (player1 && !isteam(player1->team, m_pMyEnt->team) && (player1->state == CS_ALIVE) && ((flDist = GetDistance(player1->o)) <= 250.0f)) { d = player1; EnemyVal = 1; if (flDist < flNearestDist) { EnemyVal+=2; flNearestDist = flDist; } if (d == m_pPrevEnemy) EnemyVal+=2; if (EnemyVal >= BestEnemyVal) { BestEnemyVal = EnemyVal; vec bestfromenemy = g_vecZero, bestfrombot = g_vecZero; flNearestOldPosDistToEnemy = flNearestOldPosDistToBot = 9999.9f; // Check previous locations of enemy for (int j=0;jhistory.size();j++) { const vec &v = d->history.getpos(j); if (v==m_vPrevHuntLocation) continue; flDist = GetDistance(d->o, v); if ((flDist < flNearestOldPosDistToEnemy) && IsReachable(v)) { flNearestOldPosDistToEnemy = flDist; bestfromenemy = v; } } // Check previous locations of bot hisself for (int j=0;jhistory.size();j++) { const vec &v = m_pMyEnt->history.getpos(j); if (v==m_vPrevHuntLocation) continue; flDist = GetDistance(v); if ((flDist < flNearestOldPosDistToBot) && ::IsVisible(d->o, v) && IsReachable(v)) { flNearestOldPosDistToBot = flDist; bestfrombot = v; } } if (bestfromenemy!=g_vecZero) { pNewEnemy = d; BestEnemyVal = EnemyVal; BestOldPos = bestfromenemy; } else if (bestfrombot!=g_vecZero) { pNewEnemy = d; BestEnemyVal = EnemyVal; BestOldPos = bestfrombot; } } } } if (pNewEnemy) { if (!m_pHuntTarget) // Add shoot delay if new enemy is found { float flMinShootDelay = m_pBotSkill->flMinAttackDelay; float flMaxShootDelay = m_pBotSkill->flMaxAttackDelay; m_iShootDelay = lastmillis + int(RandomFloat(flMinShootDelay, flMaxShootDelay) * 1000.0f); } if (m_vHuntLocation!=g_vecZero) m_vPrevHuntLocation = m_vHuntLocation; m_pHuntTarget = pNewEnemy; m_vHuntLocation = BestOldPos; m_fPrevHuntDist = 0.0f; return true; } return false; } bool CBot::HuntEnemy(void) { if (m_iCombatNavTime > lastmillis) { SetMoveDir(m_iMoveDir, false); return true; } m_iCombatNavTime = m_iMoveDir = 0; bool bDone = false, bNew = false; float flDist = GetDistance(m_vHuntLocation); if (flDist <= 3.0f) bDone = true; if ((m_fPrevHuntDist > 0.0) && (flDist > m_fPrevHuntDist)) bDone = true; m_fPrevHuntDist = flDist; if ((m_iHuntPlayerUpdateTime < lastmillis) || bDone) { m_iHuntPlayerUpdateTime = lastmillis + 1250; short BestPosIndexFromEnemy = -1, BestPosIndexFromBot = -1, j; float NearestDistToEnemy = 9999.9f, NearestDistToBot = 9999.9f; // Check previous locations of enemy for (j=0;jhistory.size();j++) { const vec &OldPos = m_pHuntTarget->history.getpos(j); if (bDone && m_vHuntLocation==OldPos) continue; if (GetDistance(OldPos) > 250.0f) continue; flDist = GetDistance(m_pHuntTarget->o, OldPos); if ((flDist < NearestDistToEnemy) && (IsReachable(OldPos))) { NearestDistToEnemy = flDist; BestPosIndexFromEnemy = j; break; } } // Check previous locations of bot for (j=0;jhistory.size();j++) { const vec &OldPos = m_pMyEnt->history.getpos(j); if (bDone && m_vHuntLocation==OldPos) continue; if (GetDistance(OldPos) > 25.0f) continue; flDist = GetDistance(OldPos); if ((flDist < NearestDistToBot) && ::IsVisible(m_pHuntTarget->o, OldPos) && (IsReachable(OldPos))) { NearestDistToBot = flDist; BestPosIndexFromBot = j; break; } } if (BestPosIndexFromEnemy > -1) { m_vPrevHuntLocation = m_vHuntLocation; m_vHuntLocation = m_pHuntTarget->history.getpos(BestPosIndexFromEnemy); bNew = true; m_fPrevHuntDist = 0.0f; } else if (BestPosIndexFromBot > -1) { m_vPrevHuntLocation = m_vHuntLocation; m_vHuntLocation = m_pMyEnt->history.getpos(BestPosIndexFromEnemy); bNew = true; m_fPrevHuntDist = 0.0f; } } if (!bNew) // Check if current location is still reachable { if (bDone || !IsReachable(m_vHuntLocation)) { m_pHuntTarget = NULL; m_vPrevHuntLocation = m_vHuntLocation; m_vHuntLocation = g_vecZero; m_fPrevHuntDist = 0.0f; m_iHuntDelay = lastmillis + 3500; return false; } } else condebug("New hunt pos"); // Aim to position //AimToVec(m_vHuntLocation); int iMoveDir = GetDirection(GetViewAngles(), m_pMyEnt->o, m_vHuntLocation); if (iMoveDir != DIR_NONE) { m_iMoveDir = iMoveDir; m_iCombatNavTime = lastmillis + 125; } bool aimtopos = true; if ((lastmillis - m_iSawEnemyTime) > 1500) { if (m_iLookAroundDelay < lastmillis) { if (m_iLookAroundTime > lastmillis) { if (m_iLookAroundUpdateTime < lastmillis) { float flAddAngle; if (m_bLookLeft) flAddAngle = RandomFloat(-110, -80); else flAddAngle = RandomFloat(80, 110); m_pMyEnt->targetyaw = WrapYZAngle(m_pMyEnt->targetyaw + flAddAngle); m_iLookAroundUpdateTime = lastmillis + RandomLong(400, 800); } aimtopos = false; } else if (m_iLookAroundTime > 0) { m_iLookAroundTime = 0; m_iLookAroundDelay = lastmillis + RandomLong(750, 1000); } else m_iLookAroundTime = lastmillis + RandomLong(2200, 3200); } } if (aimtopos) AimToVec(m_vHuntLocation); debugbeam(m_pMyEnt->o, m_vHuntLocation); if (m_fYawToTurn <= 25.0f) m_iHuntLastTurnLessTime = lastmillis; // Bot had to turn much for a while? if ((m_iHuntLastTurnLessTime > 0) && (m_iHuntLastTurnLessTime < (lastmillis - 1000))) { m_iHuntPauseTime = lastmillis + 200; } if (m_iHuntPauseTime >= lastmillis) { m_pMyEnt->move = 0; m_fPrevHuntDist = 0.0f; } else { // Check if bot has to jump over a wall... if (CheckJump()) m_pMyEnt->jumpnext = true; else // Check if bot has to jump to reach this location { float flHeightDiff = m_vHuntLocation.z - m_pMyEnt->o.z; bool bToHigh = false; if (Get2DDistance(m_vHuntLocation) <= 2.0f) { if (flHeightDiff >= 1.5f) { if (flHeightDiff <= JUMP_HEIGHT) { #ifndef RELEASE_BUILD char sz[64]; sprintf(sz, "OldPos z diff: %f", m_vHuntLocation.z-m_pMyEnt->o.z); condebug(sz); #endif // Jump if close to pos and the pos is high m_pMyEnt->jumpnext = true; } else bToHigh = true; } } if (bToHigh) { m_pHuntTarget = NULL; m_vPrevHuntLocation = m_vHuntLocation; m_vHuntLocation = g_vecZero; m_fPrevHuntDist = 0.0f; m_iHuntDelay = lastmillis + 3500; return false; } } } return true; } void CBot::ShootEnemy() { if(!m_pMyEnt->enemy) return; m_iSawEnemyTime = lastmillis; // Aim to enemy vec enemypos = GetEnemyPos(m_pMyEnt->enemy); AimToVec(enemypos); // Time to shoot? if (m_iShootDelay < lastmillis) //if ((lastmillis-m_pMyEnt->lastaction) >= m_pMyEnt->gunwait) { if (m_pMyEnt->mag[m_pMyEnt->gunselect]) { // If the bot is using a sniper only shoot if crosshair is near the enemy if (WeaponInfoTable[m_pMyEnt->gunselect].eWeaponType == TYPE_SNIPER) { float yawtoturn = fabs(WrapYZAngle(m_pMyEnt->yaw - m_pMyEnt->targetyaw)); float pitchtoturn = fabs(WrapYZAngle(m_pMyEnt->pitch - m_pMyEnt->targetpitch)); if ((yawtoturn > 5) || (pitchtoturn > 15)) // UNDONE: Should be skill based return; } float flDist = GetDistance(enemypos); // Check if bot is in fire range if ((flDist < WeaponInfoTable[m_pMyEnt->gunselect].flMinFireDistance) || (flDist > WeaponInfoTable[m_pMyEnt->gunselect].flMaxFireDistance)) return; // Now shoot! m_pMyEnt->attacking = true; // Get the position the bot is aiming at vec forward, right, up, dest; traceresult_s tr; AnglesToVectors(GetViewAngles(), forward, right, up); dest = m_pMyEnt->o; forward.mul(1000); dest.add(forward); TraceLine(m_pMyEnt->o, dest, m_pMyEnt, false, &tr); debugbeam(m_pMyEnt->o, tr.end); // Shoot shoot(m_pMyEnt, tr.end); // Add shoot delay m_iShootDelay = lastmillis + GetShootDelay(); } ChoosePreferredWeapon(); } #ifndef RELEASE_BUILD else { char sz[64]; sprintf(sz, "shootdelay: %d\n", (m_iShootDelay-lastmillis)); AddDebugText(sz); } #endif } bool CBot::ChoosePreferredWeapon() { TMultiChoice WeaponChoices; short sWeaponScore; float flDist = GetDistance(m_pMyEnt->enemy->o); if ((m_iChangeWeaponDelay > lastmillis) && (m_pMyEnt->ammo[m_pMyEnt->gunselect])) { if ((WeaponInfoTable[m_pMyEnt->gunselect].eWeaponType != TYPE_MELEE) || (flDist <= 3.5f)) return true; } // Choose a weapon for(int i=0;iammo[i] == 0) continue; sWeaponScore = 5; // Minimal score for a weapon if ((flDist >= WeaponInfoTable[i].flMinDesiredDistance) && (flDist <= WeaponInfoTable[i].flMaxDesiredDistance)) { // In desired range for this weapon sWeaponScore += 5; // Increase score much } else if ((flDist < WeaponInfoTable[i].flMinFireDistance) || (flDist > WeaponInfoTable[i].flMaxFireDistance)) continue; // Wrong distance for this weapon // The ideal distance would be between the Min and Max desired distance. // Score on the difference of the avarage of the Min and Max desired distance. float flAvarage = (WeaponInfoTable[i].flMinDesiredDistance + WeaponInfoTable[i].flMaxDesiredDistance) / 2.0f; float flIdealDiff = fabs(flDist - flAvarage); if (flIdealDiff < 0.5f) // Close to ideal distance sWeaponScore += 4; else if (flIdealDiff <= 1.0f) sWeaponScore += 2; // Now rate the weapon on available ammo... if (WeaponInfoTable[i].sMinDesiredAmmo > 0) { // Calculate how much percent of the min desired ammo the bot has float flDesiredPercent = (float(m_pMyEnt->ammo[i]) / float(WeaponInfoTable[i].sMinDesiredAmmo)) * 100.0f; if (flDesiredPercent >= 400.0f) sWeaponScore += 4; else if (flDesiredPercent >= 200.0f) sWeaponScore += 3; else if (flDesiredPercent >= 100.0f) sWeaponScore += 1; } WeaponChoices.Insert(i, sWeaponScore); } int WeaponSelect; if (WeaponChoices.GetSelection(WeaponSelect)) { m_iChangeWeaponDelay = lastmillis + RandomLong(2000, 8000); m_bShootAtFeet = ((WeaponInfoTable[WeaponSelect].eWeaponType==TYPE_ROCKET) && (RandomLong(1, 100) <= m_pBotSkill->sShootAtFeetWithRLPercent)); return SelectGun(WeaponSelect); } return false; } int CBot::GetShootDelay() { // UNDONE if ((WeaponInfoTable[m_pMyEnt->gunselect].eWeaponType == TYPE_MELEE) || (WeaponInfoTable[m_pMyEnt->gunselect].eWeaponType == TYPE_AUTO)) return m_pMyEnt->gunwait; float flMinShootDelay = m_pBotSkill->flMinAttackDelay; float flMaxShootDelay = m_pBotSkill->flMaxAttackDelay; return max(m_pMyEnt->gunwait, int(RandomFloat(flMinShootDelay, flMaxShootDelay) * 1000.0f)); } void CBot::CheckReload() // reload gun if no enemies are around { if(m_pMyEnt->enemy) return; SelectGun(m_pMyEnt->primary); reload(m_pMyEnt); return; } void CBot::MainAI() { // Default bots will run forward m_pMyEnt->move = 1; // Default bots won't strafe m_pMyEnt->strafe = 0; if (!BotManager.BotsShoot() && m_pMyEnt->enemy) m_pMyEnt->enemy = NULL; // Clear enemy when bots may not shoot if (m_bGoToDebugGoal) // For debugging the waypoint navigation { if (!HeadToGoal()) { ResetWaypointVars(); m_vGoal = g_vecZero; } else AddDebugText("Heading to debug goal..."); } if (BotManager.BotsShoot() && FindEnemy()) // Combat { AddDebugText("has enemy"); // Shoot at enemy ShootEnemy(); if (m_eCurrentBotState != STATE_ENEMY) { m_vGoal = g_vecZero; ResetWaypointVars(); } m_eCurrentBotState = STATE_ENEMY; if (!CheckJump()) DoCombatNav(); } else if (CheckHunt() && HuntEnemy()) { CheckReload(); AddDebugText("Hunting to %s", m_pHuntTarget->name); m_eCurrentBotState = STATE_HUNT; } // Heading to an interesting entity(ammo, armour etc) else if (CheckItems()) { CheckReload(); AddDebugText("has ent"); m_eCurrentBotState = STATE_ENT; } else if (m_classicsp && DoSPStuff()) // Home to goal, find/follow friends etc. { AddDebugText("SP stuff"); m_eCurrentBotState = STATE_SP; } else // Normal navigation { CheckReload(); if (m_eCurrentBotState != STATE_NORMAL) { m_vGoal = g_vecZero; ResetWaypointVars(); } m_eCurrentBotState = STATE_NORMAL; bool bDoNormalNav = true; AddDebugText("normal nav"); // Make sure the bot looks straight forward and not up or down m_pMyEnt->pitch = 0; // if it is time to look for a waypoint AND if there are waypoints in this // level... if (WaypointClass.m_iWaypointCount >= 1) { // check if we need to find a waypoint... if (CurrentWPIsValid() == false) { if (m_iLookForWaypointTime <= lastmillis) { // find the nearest reachable waypoint waypoint_s *pWP = GetNearestWaypoint(10.0f); if (pWP && (pWP != m_pCurrentWaypoint)) { SetCurrentWaypoint(pWP); condebug("New nav wp"); bDoNormalNav = !HeadToWaypoint(); if (bDoNormalNav) ResetWaypointVars(); } else ResetWaypointVars(); m_iLookForWaypointTime = lastmillis + 250; } } else { bDoNormalNav = !HeadToWaypoint(); if (bDoNormalNav) ResetWaypointVars(); AddDebugText("Using wps for nav"); } } // If nothing special, do regular (waypointless) navigation if(bDoNormalNav) { // Is the bot underwater? if (UnderWater(m_pMyEnt->o) && WaterNav()) { // Bot is under water, navigation happens in WaterNav } // Time to check the environment? else if (m_iCheckEnvDelay < lastmillis) { if (m_vWaterGoal!=g_vecZero) m_vWaterGoal = g_vecZero; // Check for stuck and strafe if (UnderWater(m_pMyEnt->o) || !CheckStuck()) { // Only do this when the bot is underwater or when the bot isn't stuck // Check field of view (FOV) CheckFOV(); } } // Check if the bot has to strafe CheckStrafe(); m_pMyEnt->move = 1; } } } void CBot::DoCombatNav() { if (m_iCombatNavTime > lastmillis) { // If bot has a lower skill and has to turn much, wait if ((m_sSkillNr > 2) && (m_fYawToTurn > 90.0f)) { ResetMoveSpeed(); } else { SetMoveDir(m_iMoveDir, false); } return; } if (m_bCombatJump) { m_pMyEnt->jumpnext = true; m_bCombatJump = false; m_iCombatJumpDelay = lastmillis + RandomLong(1500, 2800); return; } m_iMoveDir = DIR_NONE; // Check if bot is on top of his enemy float r = m_pMyEnt->radius+m_pMyEnt->enemy->radius; if ((fabs(m_pMyEnt->enemy->o.x-m_pMyEnt->o.x)enemy->o.y-m_pMyEnt->o.y)enemy->o.z+m_pMyEnt->enemy->aboveeye) < (m_pMyEnt->o.z + m_pMyEnt->aboveeye))) { // Try to get off him! condebug("On enemy!"); TMultiChoice AwayDirChoices; if (IsVisible(LEFT, 4.0f, false)) AwayDirChoices.Insert(LEFT); if (IsVisible(RIGHT, 4.0f, false)) AwayDirChoices.Insert(RIGHT); if (IsVisible(FORWARD, 4.0f, false)) AwayDirChoices.Insert(FORWARD); if (IsVisible(BACKWARD, 4.0f, false)) AwayDirChoices.Insert(BACKWARD); int iDir; if (AwayDirChoices.GetSelection(iDir)) { m_iMoveDir = iDir; m_iCombatNavTime = lastmillis + 500; } } float flDist = GetDistance(m_pMyEnt->enemy->o); // Check for nearby items? if (((m_iCheckEntsDelay < lastmillis) || m_pTargetEnt) && m_pBotSkill->bCanSearchItemsInCombat) { m_iCheckEntsDelay = lastmillis + 125; bool bSearchItems = false; if (m_pTargetEnt) { // Bot has already found an entity, still valid? vec v(m_pTargetEnt->x, m_pTargetEnt->y, S(m_pTargetEnt->x, m_pTargetEnt->y)->floor+m_pMyEnt->eyeheight); if ((GetDistance(v) > 25.0f) || !IsVisible(m_pTargetEnt)) m_pTargetEnt = NULL; } if (!m_pTargetEnt && (m_iCheckEntsDelay <= lastmillis)) { if (WeaponInfoTable[m_pMyEnt->gunselect].eWeaponType == TYPE_MELEE) bSearchItems = (flDist >= 8.0f); else bSearchItems = (m_pMyEnt->ammo[m_pMyEnt->gunselect] <= WeaponInfoTable[m_pMyEnt->gunselect].sMinDesiredAmmo); if (bSearchItems) m_pTargetEnt = SearchForEnts(false, 25.0f, 1.0f); } if (m_pTargetEnt) { condebug("Combat ent"); vec v(m_pTargetEnt->x, m_pTargetEnt->y, S(m_pTargetEnt->x, m_pTargetEnt->y)->floor+m_pMyEnt->eyeheight); debugbeam(m_pMyEnt->o, v); float flHeightDiff = v.z - m_pMyEnt->o.z; bool bToHigh = false; // Check he height for this ent if (Get2DDistance(v) <= 2.0f) { if (flHeightDiff >= 1.5f) { if (flHeightDiff <= JUMP_HEIGHT) { #ifndef RELEASE_BUILD char sz[64]; sprintf(sz, "Ent z diff: %f", v.z-m_pMyEnt->o.z); condebug(sz); #endif m_pMyEnt->jumpnext = true; // Jump if close to ent and the ent is high } else bToHigh = true; } } if (!bToHigh) { int iMoveDir = GetDirection(GetViewAngles(), m_pMyEnt->o, v); if (iMoveDir != DIR_NONE) { m_iMoveDir = iMoveDir; m_iCombatNavTime = lastmillis + RandomLong(125, 250); } // Check if bot needs to jump over something vec from = m_pMyEnt->o; from.z -= 1.0f; if (!IsVisible(from, iMoveDir, 3.0f, false)) m_pMyEnt->jumpnext = true; return; } } } // High skill and enemy is close? if ((m_sSkillNr <= 1) && (m_fYawToTurn < 80.0f) && (flDist <= 20.0f) && (m_iCombatJumpDelay < lastmillis)) { // Randomly jump a bit, to avoid some basic firepower ;) // How lower the distance to the enemy, how higher the chance for a jump short sJumpPercent = (100 - ((short)flDist * 8)); if (RandomLong(1, 100) <= sJumpPercent) { // Choose a nice direction to jump to // Is the enemy close? if ((GetDistance(m_pMyEnt->enemy->o) <= 4.0f) || (WeaponInfoTable[m_pMyEnt->gunselect].eWeaponType == TYPE_MELEE)) { m_iMoveDir = FORWARD; // Jump forward SetMoveDir(FORWARD, false); m_bCombatJump = true; } else if (WeaponInfoTable[m_pMyEnt->gunselect].eWeaponType != TYPE_MELEE)// else jump to a random direction { /* Directions to choose: - Forward-right - Right - Backward-right - Backward - Backward-left - Left - Forward-left */ TMultiChoice JumpDirChoices; short sForwardScore = ((flDist > 8.0f) || (flDist < 4.0f)) ? 20 : 10; short sBackwardScore = (flDist <= 6.0f) ? 20 : 10; short sStrafeScore = (flDist < 6.0f) ? 20 : 10; if (IsVisible((FORWARD | LEFT), 4.0f, false)) JumpDirChoices.Insert((FORWARD | LEFT), sForwardScore); if (IsVisible((FORWARD | RIGHT), 4.0f, false)) JumpDirChoices.Insert((FORWARD | RIGHT), sForwardScore); if (IsVisible(BACKWARD, 4.0f, false)) JumpDirChoices.Insert(BACKWARD, sBackwardScore); if (IsVisible((BACKWARD | LEFT), 4.0f, false)) JumpDirChoices.Insert((BACKWARD | LEFT), sBackwardScore); if (IsVisible((BACKWARD | RIGHT), 4.0f, false)) JumpDirChoices.Insert((BACKWARD | RIGHT), sBackwardScore); if (IsVisible(LEFT, 4.0f, false)) JumpDirChoices.Insert(LEFT, sStrafeScore); if (IsVisible(RIGHT, 4.0f, false)) JumpDirChoices.Insert(RIGHT, sStrafeScore); int JumpDir; if (JumpDirChoices.GetSelection(JumpDir)) { m_iMoveDir = JumpDir; SetMoveDir(JumpDir, false); m_bCombatJump = true; } } if (m_bCombatJump) { m_iCombatNavTime = lastmillis + RandomLong(125, 250); return; } } } if (WeaponInfoTable[m_pMyEnt->gunselect].eWeaponType == TYPE_MELEE) return; // Simply walk towards enemy if using a melee type flDist = Get2DDistance(m_pMyEnt->enemy->o); // Out of desired range for current weapon? if ((flDist <= WeaponInfoTable[m_pMyEnt->gunselect].flMinDesiredDistance) || (flDist >= WeaponInfoTable[m_pMyEnt->gunselect].flMaxDesiredDistance)) { if (flDist >= WeaponInfoTable[m_pMyEnt->gunselect].flMaxDesiredDistance) { m_iMoveDir = FORWARD; } else { m_iMoveDir = BACKWARD; } vec src, forward, right, up, dest, MyAngles = GetViewAngles(), o = m_pMyEnt->o; traceresult_s tr; // Is it furthest or farthest? bleh float flFurthestDist = 0; int bestdir = -1, dir = 0; bool moveback = (m_pMyEnt->move == -1); for(int j=-45;j<=45;j+=45) { src = MyAngles; src.y = WrapYZAngle(src.y + j); src.x = 0.0f; // If we're moving backwards, trace backwards if (moveback) src.y = WrapYZAngle(src.y + 180); AnglesToVectors(src, forward, right, up); dest = o; forward.mul(40); dest.add(forward); TraceLine(o, dest, m_pMyEnt, false, &tr); //debugbeam(origin, end); flDist = GetDistance(tr.end); if (flFurthestDist < flDist) { flFurthestDist = flDist; bestdir = dir; } dir++; } switch(bestdir) { case 0: if (moveback) m_iMoveDir |= RIGHT; // Strafe right else m_iMoveDir |= LEFT; // Strafe left break; case 2: if (moveback) m_iMoveDir |= LEFT; // Strafe left else m_iMoveDir |= RIGHT; // Strafe right break; } if (m_iMoveDir != DIR_NONE) { SetMoveDir(m_iMoveDir, false); m_iCombatNavTime = lastmillis + 500; } } else if (m_pBotSkill->bCircleStrafe) // Circle strafe when in desired range... { traceresult_s tr; vec angles, end, forward, right, up; TMultiChoice StrafeDirChoices; // Check the left side... angles = GetViewAngles(); angles.y = WrapYZAngle(angles.y - 75.0f); // Not 90 degrees because the bot // doesn't strafe in a straight line // (aims still to enemy). AnglesToVectors(angles, forward, right, up); end = m_pMyEnt->o; forward.mul(15.0f); end.add(forward); TraceLine(m_pMyEnt->o, end, m_pMyEnt, true, &tr); StrafeDirChoices.Insert(LEFT, (int)GetDistance(m_pMyEnt->o, tr.end)); // Check the right side... angles = GetViewAngles(); angles.y = WrapYZAngle(angles.y + 75.0f); // Not 90 degrees because the bot // doesn't strafe in a straight line // (aims still to enemy). AnglesToVectors(angles, forward, right, up); end = m_pMyEnt->o; forward.mul(15.0f); end.add(forward); TraceLine(m_pMyEnt->o, end, m_pMyEnt, true, &tr); StrafeDirChoices.Insert(RIGHT, (int)GetDistance(m_pMyEnt->o, tr.end)); int StrafeDir; if (StrafeDirChoices.GetSelection(StrafeDir)) { m_iMoveDir = StrafeDir; SetMoveDir(StrafeDir, false); m_iCombatNavTime = lastmillis + RandomLong(1500, 3000); } } else // Bot can't circle strafe(low skill), just stand still ResetMoveSpeed(); } bool CBot::CheckStuck() { if (m_iStuckCheckDelay >= lastmillis) return false; if ((m_vGoal!=g_vecZero) && (GetDistance(m_vGoal) < 2.0f)) return false; bool IsStuck = false; vec CurPos = m_pMyEnt->o, PrevPos = m_vPrevOrigin; CurPos.z = PrevPos.z = 0; // Did the bot hardly move the last frame? if (GetDistance(CurPos, PrevPos) <= 0.1f) { if (m_bStuck) { if (m_iStuckTime < lastmillis) IsStuck = true; } else { m_bStuck = true; m_iStuckTime = lastmillis + 1000; } } else { m_bStuck = false; m_iStuckTime = 0; } if (IsStuck) { #ifndef RELEASE_BUILD char msg[64]; sprintf(msg, "stuck (%f)", GetDistance(m_vPrevOrigin)); condebug(msg); #endif m_bStuck = false; m_iStuckTime = 0; // Crap bot is stuck, lets just try some random things // Check if the bot can turn around vec src = GetViewAngles(); src.x = 0; vec forward, right, up, dir, dest; traceresult_s tr; AnglesToVectors(src, forward, right, up); // Check the left side... dir = right; dest = m_pMyEnt->o; dir.mul(3); dest.sub(dir); TraceLine(m_pMyEnt->o, dest, m_pMyEnt, false, &tr); //debugbeam(m_pMyEnt->o, end); if (!tr.collided) { // Bot can turn left, do so m_pMyEnt->targetyaw = WrapYZAngle(m_pMyEnt->yaw - 90); m_iStuckCheckDelay = m_iCheckEnvDelay = lastmillis + 500; return true; } // Check the right side... dir = right; dest = m_pMyEnt->o; dir.mul(3); dest.add(dir); TraceLine(m_pMyEnt->o, dest, m_pMyEnt, true, &tr); //debugbeam(m_pMyEnt->o, end); if (!tr.collided) { // Bot can turn right, do so m_pMyEnt->targetyaw = WrapYZAngle(m_pMyEnt->yaw + 90); m_iStuckCheckDelay = m_iCheckEnvDelay = lastmillis + 500; return true; } // Check if bot can turn 180 degrees dir = forward; dest = m_pMyEnt->o; dir.mul(3); dest.add(dir); TraceLine(m_pMyEnt->o, dest, m_pMyEnt, true, &tr); //debugbeam(m_pMyEnt->o, end); if (!tr.collided) { // Bot can turn around, do so m_pMyEnt->targetyaw = WrapYZAngle(m_pMyEnt->yaw + 180); m_iStuckCheckDelay = m_iCheckEnvDelay = lastmillis + 500; return true; } // Bleh bot couldn't turn, lets just randomly jump :| condebug("Randomly avoiding stuck..."); if (RandomLong(0, 2) == 0) m_pMyEnt->jumpnext = true; else m_pMyEnt->targetyaw = WrapYZAngle(m_pMyEnt->yaw + RandomLong(60, 160)); return true; } return false; } // Check if a near wall is blocking and we can jump over it bool CBot::CheckJump() { bool bHasGoal = m_vGoal!=g_vecZero; float flGoalDist = (bHasGoal) ? GetDistance(m_vGoal) : 0.0f; // if ((bHasGoal) && (flGoalDist < 2.0f)) // return false; UNDONE? vec start = m_pMyEnt->o; float flTraceDist = 3.0f; if (bHasGoal && (flGoalDist < flTraceDist)) flTraceDist = flGoalDist; // Something blocks at eye hight? if (!IsVisible(start, FORWARD, flTraceDist, false)) { // Check if the bot can jump over it start.z += (JUMP_HEIGHT - 1.0f); if (IsVisible(start) && !IsVisible(start, FORWARD, flTraceDist, false)) { // Jump debugnav("High wall"); m_pMyEnt->jumpnext = true; return true; } } else { // Check if something is blocking at feet height, so the bot can jump over it start.z -= 1.7f; // Trace was blocked? if (!IsVisible(start, FORWARD, flTraceDist, false)) { //debugbeam(start, end); // Jump debugnav("Low wall"); m_pMyEnt->jumpnext = true; return true; } } return false; // Bot didn't had to jump(or couldn't) } bool CBot::CheckStrafe() { if (m_iStrafeTime >= lastmillis) { SetMoveDir(m_iMoveDir, true); return true; } if (m_iStrafeCheckDelay >= lastmillis) return false; // Check for near walls traceresult_s tr; vec from = m_pMyEnt->o, to, forward, right, up, dir; float flLeftDist = -1.0f, flRightDist = -1.0f; bool bStrafe = false; int iStrafeDir = DIR_NONE; AnglesToVectors(GetViewAngles(), forward, right, up); // Check for a near left wall to = from; dir = right; dir.mul(3.0f); to.sub(dir); TraceLine(from, to, m_pMyEnt, false, &tr); if (tr.collided) flLeftDist = GetDistance(from, tr.end); //debugbeam(m_pMyEnt->o, to); // Check for a near right wall to = from; dir = right; dir.mul(3.0f); to.add(dir); TraceLine(from, to, m_pMyEnt, false, &tr); if (tr.collided) flRightDist = GetDistance(from, tr.end); //debugbeam(m_pMyEnt->o, to); if ((flLeftDist == -1.0f) && (flRightDist == -1.0f)) { dir = right; dir.mul(m_pMyEnt->radius); // Check left from = m_pMyEnt->o; from.sub(dir); if (IsVisible(from, FORWARD, 3.0f, false, &flLeftDist)) flLeftDist = -1.0f; // Check right from = m_pMyEnt->o; from.add(dir); if (IsVisible(from, FORWARD, 3.0f, false, &flRightDist)) flRightDist = -1.0f; } if ((flLeftDist != -1.0f) && (flRightDist != -1.0f)) { if (flLeftDist < flRightDist) { // Strafe right bStrafe = true; iStrafeDir = RIGHT; } else if (flRightDist < flLeftDist) { // Strafe left bStrafe = true; iStrafeDir = LEFT; } else { // Randomly choose a strafe direction bStrafe = true; if (RandomLong(0, 1)) iStrafeDir = LEFT; else iStrafeDir = RIGHT; } } else if (flLeftDist != -1.0f) { // Strafe right bStrafe = true; iStrafeDir = RIGHT; } else if (flRightDist != -1.0f) { // Strafe left bStrafe = true; iStrafeDir = LEFT; } if (bStrafe) { SetMoveDir(iStrafeDir, true); m_iMoveDir = iStrafeDir; m_iStrafeTime = lastmillis + RandomLong(75, 150); } return bStrafe; } void CBot::CheckFOV() { m_iCheckEnvDelay = lastmillis + RandomLong(125, 250); vec MyAngles = GetViewAngles(); vec src, forward, right, up, dest, best(0, 0, 0); vec origin = m_pMyEnt->o; float flDist, flFurthestDist = 0; bool WallLeft = false; traceresult_s tr; //origin.z -= 1.5; // Slightly under eye level // Scan 90 degrees FOV for(int angle=-45;angle<=45;angle+=5) { src = MyAngles; src.y = WrapYZAngle(src.y + angle); AnglesToVectors(src, forward, right, up); dest = origin; forward.mul(40); dest.add(forward); TraceLine(origin, dest, m_pMyEnt, false, &tr); //debugbeam(origin, end); flDist = GetDistance(tr.end); if (flFurthestDist < flDist) { flFurthestDist = flDist; best = tr.end; } } if (best.x && best.y && best.z) { AimToVec(best); // Update MyAngles, since their (going to be) change(d) MyAngles.x = m_pMyEnt->targetpitch; MyAngles.y = m_pMyEnt->targetyaw; } float flNearestHitDist = GetDistance(best); if (!UnderWater(m_pMyEnt->o) && m_pMyEnt->onfloor) { // Check if a near wall is blocking and we can jump over it if (flNearestHitDist < 4) { // Check if the bot can jump over it src = MyAngles; src.x = 0; AnglesToVectors(src, forward, right, up); vec start = origin; start.z += 2.0f; dest = start; forward.mul(6); dest.add(forward); TraceLine(start, dest, m_pMyEnt, false, &tr); //debugbeam(start, end); if (!tr.collided) { // Jump debugnav("High wall"); m_pMyEnt->jumpnext = true; m_iStrafeCheckDelay = lastmillis + RandomLong(250, 500); return; } } else { // Check if something is blocking below us, so the bot can jump over it src = MyAngles; src.x = 0; AnglesToVectors(src, forward, right, up); vec start = origin; start.z -= 1.7f; dest = start; forward.mul(4); dest.add(forward); TraceLine(start, dest, m_pMyEnt, false, &tr); // Trace was blocked? if (tr.collided) { //debugbeam(start, end); // Jump debugnav("Low wall"); m_pMyEnt->jumpnext = true; m_iStrafeCheckDelay = lastmillis + RandomLong(250, 500); return; } } // Check if the bot is going to fall... src = MyAngles; src.x = 0.0f; AnglesToVectors(src, forward, right, up); dest = origin; forward.mul(3.0f); dest.add(forward); TraceLine(origin, dest, m_pMyEnt, false, &tr); int cx = int(tr.end.x), cy = int(tr.end.y); short CubesInWater=0; for(int x=cx-1;x<=(cx+1);x++) { for(int y=cy-1;y<=(cy+1);y++) { if (OUTBORD(x, y)) continue; //sqr *s = S(fast_f2nat(x), fast_f2nat(y)); //if (!SOLID(s)) { vec from(x, y, m_pMyEnt->o.z); dest = from; dest.z -= 6.0f; TraceLine(from, dest, m_pMyEnt, false, &tr); bool turn = false; if (UnderWater(tr.end)) CubesInWater++; if (CubesInWater > 2) turn = true; // Always avoid water if (!tr.collided && RandomLong(0, 1)) turn = true; // Randomly avoid a fall if (turn) { m_pMyEnt->targetyaw = WrapYZAngle(m_pMyEnt->yaw + 180); m_iCheckEnvDelay = m_iStrafeCheckDelay = lastmillis + RandomLong(750, 1500); debugnav("Water or a fall in front"); return; } } } } } // Is the bot about to head a corner? if (flNearestHitDist <= 4.0f) { src = MyAngles; src.y = WrapYZAngle(src.y - 45.0f); AnglesToVectors(src, forward, right, up); dest = origin; forward.mul(4.0f); dest.add(forward); TraceLine(origin, dest, m_pMyEnt, false, &tr); WallLeft = (tr.collided); src = MyAngles; src.y += WrapYZAngle(src.y + 45.0f); AnglesToVectors(src, forward, right, up); dest = origin; forward.mul(4.0f); dest.add(forward); TraceLine(origin, dest, m_pMyEnt, false, &tr); if (WallLeft && tr.collided) { // We're about to hit a corner, turn away debugnav("Corner"); m_pMyEnt->targetyaw = WrapYZAngle(m_pMyEnt->yaw + RandomFloat(160.0f, 200.0f)); m_iCheckEnvDelay = m_iStrafeCheckDelay = lastmillis + RandomLong(750, 1500); return; } } } // Called when bot is underwater bool CBot::WaterNav() { const int iSearchRange = 4; if (m_vWaterGoal==g_vecZero) { AddDebugText("WaterNav"); // Find the nearest and reachable cube which isn't underwater int cx = int(m_pMyEnt->o.x); int cy = int(m_pMyEnt->o.y); float flNearestDist = 9999.0f, flDist; if (OUTBORD(cx, cy)) return false; // Check all cubes in range... for (int x=(cx-iSearchRange);x<=(cx+iSearchRange);x++) { for (int y=(cy-iSearchRange);y<=(cy+iSearchRange);y++) { sqr *s = S(x, y); if (SOLID(s)) continue; if ((x==cx) && (y==cy)) continue; vec v(x, y, GetCubeFloor(x, y)); if (UnderWater(v)) continue; // Skip, cube is underwater if (hdr.waterlevel < (v.z - 2.0f)) continue; // Cube is too high // Check if the bot 'can fit' on the cube(no near obstacles) bool small = false; for (int a=(x-2);a<=(x+2);a++) { if (small) break; for (int b=(y-2);b<=(y+2);b++) { if ((x==a) && (y==b)) continue; vec v2(a, b, GetCubeFloor(a, b)); if (v.z < (v2.z-1-JUMP_HEIGHT)) { small=true; break; } if ((a >= (x-1)) && (a <= (x+1)) && (b >= (y-1)) && (b <= (y+1))) { if ((v2.z) < (v.z-2.0f)) { small = true; break; } } traceresult_s tr; TraceLine(v, v2, NULL, false, &tr); if (tr.collided) { small=true; break; } } if (small) break; } if (small) { debugbeam(m_pMyEnt->o, v); continue; } // Okay, cube is valid. flDist = GetDistance(v); if (flDist < flNearestDist) { flNearestDist = flDist; m_vWaterGoal = v; } } } } if (m_vWaterGoal!=g_vecZero) { AddDebugText("WaterNav"); //debugbeam(m_pMyEnt->o, m_vWaterGoal); vec aim = m_vWaterGoal; aim.z += 1.5f; // Aim a bit further up AimToVec(aim); if ((RandomLong(1, 100) <= 15) && (Get2DDistance(m_vWaterGoal) <= 7.0f)) m_pMyEnt->jumpnext = true; return true; } return false; } bool CBot::CheckItems() { if (!m_pCurrentGoalWaypoint && !CheckJump() && CheckStuck()) { // Don't check for ents a while when stuck m_iCheckEntsDelay = lastmillis + RandomLong(1000, 2000); return false; } if (m_vGoal==g_vecZero) m_pTargetEnt = NULL; if (!m_pTargetEnt) { if (m_iCheckEntsDelay > lastmillis) return false; else { m_pTargetEnt = SearchForEnts(!m_classicsp); m_iCheckEntsDelay = lastmillis + RandomLong(2500, 5000); } } if (m_pTargetEnt) { if (HeadToTargetEnt()) return true; } if (m_eCurrentBotState == STATE_ENT) { ResetWaypointVars(); m_vGoal = g_vecZero; m_pTargetEnt = NULL; } return false; } bool CBot::InUnreachableList(entity *e) { TLinkedList::node_s *p = m_UnreachableEnts.GetFirst(); while(p) { if (p->Entry->ent == e) return true; p = p->next; } return false; } bool CBot::IsReachable(vec to, float flMaxHeight) { vec from = m_pMyEnt->o; traceresult_s tr; float curr_height, last_height; float distance = GetDistance(from, to); // is the destination close enough? //if (distance < REACHABLE_RANGE) { if (IsVisible(to)) { // Look if bot can 'fit trough' vec src = from, forward, right, up; AnglesToVectors(GetViewAngles(), forward, right, up); // Trace from 1 cube to the left vec temp = right; temp.mul(1.0f); src.sub(temp); if (!::IsVisible(src, to)) return false; // Trace from 1 cube to the right src.add(temp); if (!::IsVisible(src, to)) return false; if (UnderWater(from) && UnderWater(to)) { // No need to worry about heights in water return true; } /* if (to.z > (from.z + JUMP_HEIGHT)) { vec v_new_src = to; vec v_new_dest = to; v_new_dest.z = v_new_dest.z - (JUMP_HEIGHT + 1.0f); // check if we didn't hit anything, if so then it's in mid-air if (::IsVisible(v_new_src, v_new_dest, NULL)) { condebug("to is in midair"); debugbeam(from, to); return false; // can't reach this one } } */ // check if distance to ground increases more than jump height // at points between from and to... vec v_temp = to; v_temp.sub(from); vec v_direction = Normalize(v_temp); // 1 unit long vec v_check = from; vec v_down = from; v_down.z = v_down.z - 100.0f; // straight down TraceLine(v_check, v_down, NULL, false, &tr); // height from ground last_height = GetDistance(v_check, tr.end); distance = GetDistance(to, v_check); // distance from goal while (distance > 2.0f) { // move 2 units closer to the goal v_temp = v_direction; v_temp.mul(2.0f); v_check.add(v_temp); v_down = v_check; v_down.z = v_down.z - 100.0f; TraceLine(v_check, v_down, NULL, false, &tr); curr_height = GetDistance(v_check, tr.end); // is the difference in the last height and the current height // higher that the jump height? if ((last_height - curr_height) >= flMaxHeight) { // can't get there from here... //condebug("traces failed to to"); debugbeam(from, to); return false; } last_height = curr_height; distance = GetDistance(to, v_check); // distance from goal } return true; } } return false; } void CBot::HearSound(int n, vec *o) { // Has the bot already an enemy? if (m_pMyEnt->enemy) return; //fixmebot // Is the sound not interesting? if(n == S_DIE1 || n == S_DIE2) return; int soundvol = m_pBotSkill->iMaxHearVolume - (int)(GetDistance(*o)*3*m_pBotSkill->iMaxHearVolume/255); if (soundvol == 0) return; // Look who made the sound(check for the nearest enemy) float flDist, flNearestDist = 3.0f; // Range of 3 units playerent *pNearest = NULL; // Check all players first loopv(players) { playerent *d = players[i]; if (d == m_pMyEnt || !d || (d->state != CS_ALIVE) || isteam(m_pMyEnt->team, d->team)) continue; flDist = GetDistance(*o, d->o); if ((flDist < flNearestDist) && IsVisible(d)) { pNearest = d; flNearestDist = flDist; } } // Check local player if (player1 && (player1->state == CS_ALIVE) && !isteam(m_pMyEnt->team, player1->team)) { flDist = GetDistance(*o, player1->o); if ((flDist < flNearestDist) && IsVisible(player1)) { pNearest = player1; flNearestDist = flDist; } } if (pNearest) { if (m_pMyEnt->enemy != pNearest) m_iShootDelay = lastmillis + GetShootDelay(); // Add shoot delay when new enemy found m_pMyEnt->enemy = pNearest; } } bool CBot::IsInFOV(const vec &o) { vec v2, forward, right, up; float flDot; AnglesToVectors(GetViewAngles(), forward, right, up); v2 = o; v2.sub(m_pMyEnt->o); v2.z = 0.0f; // Make 2D v2 = Normalize(v2); forward.z = 0; // Make 2D flDot = v2.dot(forward); return(flDot >= 0.5f); // sin2(0.5) == 60 degrees FOV } // Code of CBot - End