home *** CD-ROM | disk | FTP | other *** search
- //
- // C++ Implementation: bot_ai
- //
- // Description: The AI part comes here(navigation, shooting etc)
- //
- //
- // Author: <rickhelmus@gmail.com>
- //
-
-
- // 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;j<d->history.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;j<m_pMyEnt->history.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;j<d->history.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;j<m_pMyEnt->history.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;j<m_pHuntTarget->history.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;j<m_pMyEnt->history.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<int> 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;i<MAX_WEAPONS;i++)
- {
- // If no ammo for this weapon, skip it
- if (m_pMyEnt->ammo[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)<r &&
- fabs(m_pMyEnt->enemy->o.y-m_pMyEnt->o.y)<r) &&
- ((m_pMyEnt->enemy->o.z+m_pMyEnt->enemy->aboveeye) < (m_pMyEnt->o.z + m_pMyEnt->aboveeye)))
- {
- // Try to get off him!
- condebug("On enemy!");
- TMultiChoice<int> 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<int> 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<int> 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<unreachable_ent_s *>::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
-