1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017 |
- //
- // C++ Implementation: bot_ai
- //
- // Description: The AI part comes here(navigation, shooting etc)
- //
- //
- // Author: <rickhelmus@gmail.com>
- //
- // Code of CBot - Start
- #include "cube.h"
- #include "bot.h"
- extern weaponinfo_s WeaponInfoTable[MAX_WEAPONS];
- vec CBot::GetEnemyPos(playerent *d)
- {
- // Aim offset idea by botman
- vec o = m_pMyEnt->weaponsel->type == GUN_SNIPER && d->head.x >= 0 ? d->head : d->o, offset;
- float flDist = GetDistance(o), flScale;
- 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;
- }
- // WIP
- bool CBot::BotsAgainstHumans()
- {
- return false;
- }
- bool CBot::DetectEnemy(playerent *p)
- {
- return (IsInFOV(p) || (m_pBotSkill->flAlwaysDetectDistance > m_pMyEnt->o.dist(p->o)))
- && IsVisible(p);
- }
- bool CBot::FindEnemy(void)
- {
- // UNDONE: Enemies are now only scored on their distance
- if(BotsAgainstHumans())
- {
- m_pMyEnt->enemy = NULL;
- if(player1->state == CS_ALIVE)
- {
- m_pMyEnt->enemy = player1;
- }
- return m_pMyEnt->enemy != NULL;
- }
- 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 bots
- loopv(bots)
- {
- d = bots[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(!DetectEnemy(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(DetectEnemy(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 bots
- loopv(bots)
- {
- d = bots[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::CheckWeaponSwitch()
- {
- if(m_pMyEnt->nextweaponsel == NULL) m_pMyEnt->weaponchanging = 0;
- if(!m_pMyEnt->weaponchanging) return;
- int timeprogress = lastmillis-m_pMyEnt->weaponchanging;
- if(timeprogress>weapon::weaponchangetime)
- {
- m_pMyEnt->prevweaponsel = m_pMyEnt->weaponsel;
- m_pMyEnt->weaponsel = m_pMyEnt->nextweaponsel;
- m_pMyEnt->weaponchanging = 0;
- m_iChangeWeaponDelay = 0;
- if(!m_pMyEnt->weaponsel->mag)
- {
- tryreload(m_pMyEnt);
- }
- }
- }
- void CBot::ShootEnemy()
- {
- if(!m_pMyEnt->enemy) return;
- if(!IsVisible(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(m_pMyEnt, tr.end);
- // Add shoot delay
- m_iShootDelay = lastmillis + GetShootDelay();
- }
- }
- #ifndef RELEASE_BUILD
- else
- {
- char sz[64];
- sprintf(sz, "shootdelay: %d\n", (m_iShootDelay-lastmillis));
- AddDebugText(sz);
- }
- #endif
- }
- bool CBot::ChoosePreferredWeapon()
- {
- return true;
- }
- int CBot::GetShootDelay()
- {
- // UNDONE
- return m_pMyEnt->gunwait[m_pMyEnt->gunselect];
- if ((WeaponInfoTable[m_pMyEnt->gunselect].eWeaponType == TYPE_MELEE) ||
- (WeaponInfoTable[m_pMyEnt->gunselect].eWeaponType == TYPE_AUTO))
- return m_pMyEnt->gunwait[m_pMyEnt->gunselect];
- float flMinShootDelay = m_pBotSkill->flMinAttackDelay;
- float flMaxShootDelay = m_pBotSkill->flMaxAttackDelay;
- return max(m_pMyEnt->gunwait[m_pMyEnt->gunselect], int(RandomFloat(flMinShootDelay, flMaxShootDelay) * 1000.0f));
- }
- void CBot::CheckReload() // reload gun if no enemies are around
- {
- if(m_pMyEnt->mag[m_pMyEnt->weaponsel->type] >= WeaponInfoTable[m_pMyEnt->weaponsel->type].sMinDesiredAmmo) return; // do not reload if mindesiredammo is satisfied
- if(m_pMyEnt->enemy && m_pMyEnt->mag[m_pMyEnt->weaponsel->type])
- {
- return; // ignore the enemy, if no ammo in mag.
- }
- tryreload(m_pMyEnt);
- return;
- }
- void CBot::CheckScope()
- {
- #define MINSCOPEDIST 15
- #define MINSCOPETIME 1000
- if(m_pMyEnt->weaponsel->type != GUN_SNIPER) return;
- sniperrifle *sniper = (sniperrifle *)m_pMyEnt->weaponsel;
- if(m_pMyEnt->enemy && m_pMyEnt->o.dist(m_pMyEnt->enemy->o) > MINSCOPEDIST)
- {
- sniper->setscope(true);
- }
- else if(m_pMyEnt->scoping && lastmillis - sniper->scoped_since < MINSCOPETIME)
- {
- sniper->setscope(false);
- }
- }
- void CBot::MainAI()
- {
- // Default bots will run forward
- m_pMyEnt->move = 1;
- // Default bots won't strafe
- m_pMyEnt->strafe = 0;
- // Whatever the bot is doing, check for needed crouch
- if(CheckCrouch()) m_pMyEnt->trycrouch = true;
- else m_pMyEnt->trycrouch = false;
- 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
- {
- CheckReload();
- CheckScope();
- AddDebugText("has enemy");
- // Use best weapon
- ChoosePreferredWeapon();
- // 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();
- CheckScope();
- 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 + (CheckCrouch() ? 2000 : 0) >= 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::CheckCrouch()
- {
- bool bHasGoal = m_vGoal!=g_vecZero;
- float flGoalDist = (bHasGoal) ? GetDistance(m_vGoal) : 0.0f;
- vec start = m_pMyEnt->o;
- vec crouch = vec(0, 0, 2.0f);
- float flTraceDist = 3.0f;
- if (bHasGoal && (flGoalDist < flTraceDist))
- flTraceDist = flGoalDist;
- if (!IsVisible(vec(start).add(crouch), FORWARD, flTraceDist, false) && IsVisible(vec(start).sub(crouch), FORWARD, flTraceDist, false)) return true;
- return false;
- }
- 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 (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 bots first
- loopv(bots)
- {
- playerent *d = bots[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 target, dir, forward, right, up;
- float flDot, flAngle;
- AnglesToVectors(GetViewAngles(), forward, right, up);
- // direction the bot is aiming at
- dir = forward;
- dir.z = 0; // Make 2D
- // ideal direction
- target = o;
- target.sub(m_pMyEnt->o);
- target.z = 0.0f; // Make 2D
- // angle between these two directions
- flDot = target.dot(dir);
- flAngle = acos(flDot/(target.magnitude() * dir.magnitude()));
- return m_pBotSkill->iFov/2.0f >= flAngle/RAD;
- }
- // Code of CBot - End
|