bot_ai.cpp 57 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017
  1. //
  2. // C++ Implementation: bot_ai
  3. //
  4. // Description: The AI part comes here(navigation, shooting etc)
  5. //
  6. //
  7. // Author: <rickhelmus@gmail.com>
  8. //
  9. // Code of CBot - Start
  10. #include "cube.h"
  11. #include "bot.h"
  12. extern weaponinfo_s WeaponInfoTable[MAX_WEAPONS];
  13. vec CBot::GetEnemyPos(playerent *d)
  14. {
  15. // Aim offset idea by botman
  16. vec o = m_pMyEnt->weaponsel->type == GUN_SNIPER && d->head.x >= 0 ? d->head : d->o, offset;
  17. float flDist = GetDistance(o), flScale;
  18. if (m_pBotSkill->bCanPredict)
  19. {
  20. // How higher the skill, how 'more' the bot predicts
  21. float flPredictTime = RandomFloat(0.8f, 1.2f) / (m_sSkillNr+1);
  22. o = PredictPos(o, d->vel, flPredictTime);
  23. }
  24. if (flDist > 60.0f)
  25. flScale = 1.0f;
  26. else if (flDist > 6.0f)
  27. flScale = flDist / 60.0f;
  28. else
  29. flScale = 0.1f;
  30. switch (m_sSkillNr)
  31. {
  32. case 0:
  33. // no offset
  34. offset.x = 0;
  35. offset.y = 0;
  36. offset.z = 0;
  37. break;
  38. case 1:
  39. // GOOD, offset a little for x, y, and z
  40. offset.x = RandomFloat(-3, 3) * flScale;
  41. offset.y = RandomFloat(-3, 3) * flScale;
  42. offset.z = RandomFloat(-6, 6) * flScale;
  43. break;
  44. case 2:
  45. // FAIR, offset somewhat for x, y, and z
  46. offset.x = RandomFloat(-8, 8) * flScale;
  47. offset.y = RandomFloat(-8, 8) * flScale;
  48. offset.z = RandomFloat(-12, 12) * flScale;
  49. break;
  50. case 3:
  51. // POOR, offset for x, y, and z
  52. offset.x = RandomFloat(-15, 15) * flScale;
  53. offset.y = RandomFloat(-15, 15) * flScale;
  54. offset.z = RandomFloat(-25, 25) * flScale;
  55. break;
  56. case 4:
  57. // BAD, offset lots for x, y, and z
  58. offset.x = RandomFloat(-20, 20) * flScale;
  59. offset.y = RandomFloat(-20, 20) * flScale;
  60. offset.z = RandomFloat(-35, 35) * flScale;
  61. break;
  62. }
  63. o.add(offset);
  64. return o;
  65. }
  66. // WIP
  67. bool CBot::BotsAgainstHumans()
  68. {
  69. return false;
  70. }
  71. bool CBot::DetectEnemy(playerent *p)
  72. {
  73. return (IsInFOV(p) || (m_pBotSkill->flAlwaysDetectDistance > m_pMyEnt->o.dist(p->o)))
  74. && IsVisible(p);
  75. }
  76. bool CBot::FindEnemy(void)
  77. {
  78. // UNDONE: Enemies are now only scored on their distance
  79. if(BotsAgainstHumans())
  80. {
  81. m_pMyEnt->enemy = NULL;
  82. if(player1->state == CS_ALIVE)
  83. {
  84. m_pMyEnt->enemy = player1;
  85. }
  86. return m_pMyEnt->enemy != NULL;
  87. }
  88. if (m_pMyEnt->enemy) // Bot already has an enemy
  89. {
  90. // Check if the enemy is still in game
  91. bool found = IsInGame(m_pMyEnt->enemy);
  92. // Check if the enemy is still ingame, still alive, not joined my team and is visible
  93. if (found && !isteam(m_pMyEnt->team, m_pMyEnt->enemy->team))
  94. {
  95. if ((m_pMyEnt->enemy->state == CS_ALIVE) && (IsVisible(m_pMyEnt->enemy)))
  96. return true;
  97. else
  98. m_pPrevEnemy = m_pMyEnt->enemy;
  99. }
  100. else
  101. m_pMyEnt->enemy = NULL;
  102. }
  103. if (m_iEnemySearchDelay > lastmillis) return (m_pMyEnt->enemy!=NULL);
  104. m_pMyEnt->enemy = NULL;
  105. // Add enemy searchy delay
  106. float MinDelay = m_pBotSkill->flMinEnemySearchDelay;
  107. float MaxDelay = m_pBotSkill->flMaxEnemySearchDelay;
  108. m_iEnemySearchDelay = lastmillis + int(RandomFloat(MinDelay, MaxDelay) * 1000.0f);
  109. playerent *pNewEnemy = NULL, *d = NULL;
  110. float flDist, flNearestDist = 99999.9f;
  111. short EnemyVal, BestEnemyVal = -100;
  112. // First loop through all bots
  113. loopv(bots)
  114. {
  115. d = bots[i]; // Handy shortcut
  116. if (d == m_pMyEnt || !d || isteam(d->team, m_pMyEnt->team) || (d->state != CS_ALIVE))
  117. continue;
  118. // Check if the enemy is visible
  119. if(!DetectEnemy(d))
  120. continue;
  121. flDist = GetDistance(d->o);
  122. EnemyVal = 1;
  123. if (flDist < flNearestDist)
  124. {
  125. EnemyVal+=2;
  126. flNearestDist = flDist;
  127. }
  128. if (EnemyVal > BestEnemyVal)
  129. {
  130. pNewEnemy = d;
  131. BestEnemyVal = EnemyVal;
  132. }
  133. }
  134. // Then examine the local player
  135. if (player1 && !isteam(player1->team, m_pMyEnt->team) &&
  136. (player1->state == CS_ALIVE))
  137. {
  138. // Check if the enemy is visible
  139. if(DetectEnemy(player1))
  140. {
  141. flDist = GetDistance(player1->o);
  142. EnemyVal = 1;
  143. if (flDist < flNearestDist)
  144. {
  145. EnemyVal+=2;
  146. flNearestDist = flDist;
  147. }
  148. if (EnemyVal > BestEnemyVal)
  149. {
  150. pNewEnemy = player1;
  151. BestEnemyVal = EnemyVal;
  152. }
  153. }
  154. }
  155. //}
  156. if (pNewEnemy)
  157. {
  158. if (!m_pMyEnt->enemy) // Add shoot delay if new enemy is found
  159. {
  160. float flMinShootDelay = m_pBotSkill->flMinAttackDelay;
  161. float flMaxShootDelay = m_pBotSkill->flMaxAttackDelay;
  162. m_iShootDelay = lastmillis + int(RandomFloat(flMinShootDelay,
  163. flMaxShootDelay) * 1000.0f);
  164. }
  165. if ((m_pMyEnt->enemy != pNewEnemy) && m_pMyEnt->enemy)
  166. m_pPrevEnemy = m_pMyEnt->enemy;
  167. m_pMyEnt->enemy = pNewEnemy;
  168. return true;
  169. }
  170. return false;
  171. }
  172. bool CBot::CheckHunt(void)
  173. {
  174. if (!BotManager.BotsShoot()) return false;
  175. if (m_pHuntTarget) // Bot already has an enemy to hunt
  176. {
  177. // Check if the enemy is still in game
  178. bool found = IsInGame(m_pHuntTarget);
  179. // Check if the enemy is still ingame, still alive, not joined my team and is visible
  180. if (found && !isteam(m_pMyEnt->team, m_pHuntTarget->team))
  181. {
  182. if ((m_pHuntTarget->state == CS_ALIVE) && IsReachable(m_vHuntLocation))
  183. return true;
  184. }
  185. else
  186. m_pHuntTarget = NULL;
  187. }
  188. if (m_iHuntDelay > lastmillis) return (m_pHuntTarget!=NULL);
  189. if (m_vHuntLocation!=g_vecZero)
  190. m_vPrevHuntLocation = m_vHuntLocation;
  191. m_pHuntTarget = NULL;
  192. m_vHuntLocation = g_vecZero;
  193. // Add enemy hunt search delay
  194. m_iHuntDelay = lastmillis + 1500;
  195. playerent *pNewEnemy = NULL, *d = NULL;
  196. float flDist, flNearestDist = 99999.9f, flNearestOldPosDistToEnemy = 99999.9f;
  197. float flNearestOldPosDistToBot = 99999.9f;
  198. short EnemyVal, BestEnemyVal = -100;
  199. vec BestOldPos;
  200. // First loop through all bots
  201. loopv(bots)
  202. {
  203. d = bots[i]; // Handy shortcut
  204. if (d == m_pMyEnt || !d || isteam(d->team, m_pMyEnt->team) || (d->state != CS_ALIVE))
  205. continue;
  206. flDist = GetDistance(d->o);
  207. if (flDist > 250.0f) continue;
  208. EnemyVal = 1;
  209. if (flDist < flNearestDist)
  210. {
  211. EnemyVal+=2;
  212. flNearestDist = flDist;
  213. }
  214. if (d == m_pPrevEnemy)
  215. EnemyVal+=2;
  216. if (EnemyVal < BestEnemyVal) continue;
  217. vec bestfromenemy = g_vecZero, bestfrombot = g_vecZero;
  218. flNearestOldPosDistToEnemy = flNearestOldPosDistToBot = 9999.9f;
  219. // Check previous locations of enemy
  220. for (int j=0;j<d->history.size();j++)
  221. {
  222. const vec &v = d->history.getpos(j);
  223. if (v==m_vPrevHuntLocation) continue;
  224. flDist = GetDistance(d->o, v);
  225. if ((flDist < flNearestOldPosDistToEnemy) && IsReachable(v))
  226. {
  227. flNearestOldPosDistToEnemy = flDist;
  228. bestfromenemy = v;
  229. }
  230. }
  231. // Check previous locations of bot hisself
  232. for (int j=0;j<m_pMyEnt->history.size();j++)
  233. {
  234. const vec &v = m_pMyEnt->history.getpos(j);
  235. if (v==m_vPrevHuntLocation) continue;
  236. flDist = GetDistance(v);
  237. if ((flDist < flNearestOldPosDistToBot) && ::IsVisible(d->o, v) &&
  238. IsReachable(v))
  239. {
  240. flNearestOldPosDistToBot = flDist;
  241. bestfrombot = v;
  242. }
  243. }
  244. if (bestfromenemy!=g_vecZero)
  245. {
  246. pNewEnemy = d;
  247. BestEnemyVal = EnemyVal;
  248. BestOldPos = bestfromenemy;
  249. }
  250. else if (bestfrombot!=g_vecZero)
  251. {
  252. pNewEnemy = d;
  253. BestEnemyVal = EnemyVal;
  254. BestOldPos = bestfrombot;
  255. }
  256. // Then examine the local player
  257. if (player1 && !isteam(player1->team, m_pMyEnt->team) &&
  258. (player1->state == CS_ALIVE) && ((flDist = GetDistance(player1->o)) <= 250.0f))
  259. {
  260. d = player1;
  261. EnemyVal = 1;
  262. if (flDist < flNearestDist)
  263. {
  264. EnemyVal+=2;
  265. flNearestDist = flDist;
  266. }
  267. if (d == m_pPrevEnemy)
  268. EnemyVal+=2;
  269. if (EnemyVal >= BestEnemyVal)
  270. {
  271. BestEnemyVal = EnemyVal;
  272. vec bestfromenemy = g_vecZero, bestfrombot = g_vecZero;
  273. flNearestOldPosDistToEnemy = flNearestOldPosDistToBot = 9999.9f;
  274. // Check previous locations of enemy
  275. for (int j=0;j<d->history.size();j++)
  276. {
  277. const vec &v = d->history.getpos(j);
  278. if (v==m_vPrevHuntLocation) continue;
  279. flDist = GetDistance(d->o, v);
  280. if ((flDist < flNearestOldPosDistToEnemy) && IsReachable(v))
  281. {
  282. flNearestOldPosDistToEnemy = flDist;
  283. bestfromenemy = v;
  284. }
  285. }
  286. // Check previous locations of bot hisself
  287. for (int j=0;j<m_pMyEnt->history.size();j++)
  288. {
  289. const vec &v = m_pMyEnt->history.getpos(j);
  290. if (v==m_vPrevHuntLocation) continue;
  291. flDist = GetDistance(v);
  292. if ((flDist < flNearestOldPosDistToBot) && ::IsVisible(d->o, v) &&
  293. IsReachable(v))
  294. {
  295. flNearestOldPosDistToBot = flDist;
  296. bestfrombot = v;
  297. }
  298. }
  299. if (bestfromenemy!=g_vecZero)
  300. {
  301. pNewEnemy = d;
  302. BestEnemyVal = EnemyVal;
  303. BestOldPos = bestfromenemy;
  304. }
  305. else if (bestfrombot!=g_vecZero)
  306. {
  307. pNewEnemy = d;
  308. BestEnemyVal = EnemyVal;
  309. BestOldPos = bestfrombot;
  310. }
  311. }
  312. }
  313. }
  314. if (pNewEnemy)
  315. {
  316. if (!m_pHuntTarget) // Add shoot delay if new enemy is found
  317. {
  318. float flMinShootDelay = m_pBotSkill->flMinAttackDelay;
  319. float flMaxShootDelay = m_pBotSkill->flMaxAttackDelay;
  320. m_iShootDelay = lastmillis + int(RandomFloat(flMinShootDelay,
  321. flMaxShootDelay) * 1000.0f);
  322. }
  323. if (m_vHuntLocation!=g_vecZero)
  324. m_vPrevHuntLocation = m_vHuntLocation;
  325. m_pHuntTarget = pNewEnemy;
  326. m_vHuntLocation = BestOldPos;
  327. m_fPrevHuntDist = 0.0f;
  328. return true;
  329. }
  330. return false;
  331. }
  332. bool CBot::HuntEnemy(void)
  333. {
  334. if (m_iCombatNavTime > lastmillis)
  335. {
  336. SetMoveDir(m_iMoveDir, false);
  337. return true;
  338. }
  339. m_iCombatNavTime = m_iMoveDir = 0;
  340. bool bDone = false, bNew = false;
  341. float flDist = GetDistance(m_vHuntLocation);
  342. if (flDist <= 3.0f)
  343. bDone = true;
  344. if ((m_fPrevHuntDist > 0.0) && (flDist > m_fPrevHuntDist))
  345. bDone = true;
  346. m_fPrevHuntDist = flDist;
  347. if ((m_iHuntPlayerUpdateTime < lastmillis) || bDone)
  348. {
  349. m_iHuntPlayerUpdateTime = lastmillis + 1250;
  350. short BestPosIndexFromEnemy = -1, BestPosIndexFromBot = -1, j;
  351. float NearestDistToEnemy = 9999.9f, NearestDistToBot = 9999.9f;
  352. // Check previous locations of enemy
  353. for (j=0;j<m_pHuntTarget->history.size();j++)
  354. {
  355. const vec &OldPos = m_pHuntTarget->history.getpos(j);
  356. if (bDone && m_vHuntLocation==OldPos)
  357. continue;
  358. if (GetDistance(OldPos) > 250.0f)
  359. continue;
  360. flDist = GetDistance(m_pHuntTarget->o, OldPos);
  361. if ((flDist < NearestDistToEnemy) && (IsReachable(OldPos)))
  362. {
  363. NearestDistToEnemy = flDist;
  364. BestPosIndexFromEnemy = j;
  365. break;
  366. }
  367. }
  368. // Check previous locations of bot
  369. for (j=0;j<m_pMyEnt->history.size();j++)
  370. {
  371. const vec &OldPos = m_pMyEnt->history.getpos(j);
  372. if (bDone && m_vHuntLocation==OldPos)
  373. continue;
  374. if (GetDistance(OldPos) > 25.0f)
  375. continue;
  376. flDist = GetDistance(OldPos);
  377. if ((flDist < NearestDistToBot) && ::IsVisible(m_pHuntTarget->o, OldPos) &&
  378. (IsReachable(OldPos)))
  379. {
  380. NearestDistToBot = flDist;
  381. BestPosIndexFromBot = j;
  382. break;
  383. }
  384. }
  385. if (BestPosIndexFromEnemy > -1)
  386. {
  387. m_vPrevHuntLocation = m_vHuntLocation;
  388. m_vHuntLocation = m_pHuntTarget->history.getpos(BestPosIndexFromEnemy);
  389. bNew = true;
  390. m_fPrevHuntDist = 0.0f;
  391. }
  392. else if (BestPosIndexFromBot > -1)
  393. {
  394. m_vPrevHuntLocation = m_vHuntLocation;
  395. m_vHuntLocation = m_pMyEnt->history.getpos(BestPosIndexFromEnemy);
  396. bNew = true;
  397. m_fPrevHuntDist = 0.0f;
  398. }
  399. }
  400. if (!bNew) // Check if current location is still reachable
  401. {
  402. if (bDone || !IsReachable(m_vHuntLocation))
  403. {
  404. m_pHuntTarget = NULL;
  405. m_vPrevHuntLocation = m_vHuntLocation;
  406. m_vHuntLocation = g_vecZero;
  407. m_fPrevHuntDist = 0.0f;
  408. m_iHuntDelay = lastmillis + 3500;
  409. return false;
  410. }
  411. }
  412. else
  413. condebug("New hunt pos");
  414. // Aim to position
  415. //AimToVec(m_vHuntLocation);
  416. int iMoveDir = GetDirection(GetViewAngles(), m_pMyEnt->o, m_vHuntLocation);
  417. if (iMoveDir != DIR_NONE)
  418. {
  419. m_iMoveDir = iMoveDir;
  420. m_iCombatNavTime = lastmillis + 125;
  421. }
  422. bool aimtopos = true;
  423. if ((lastmillis - m_iSawEnemyTime) > 1500)
  424. {
  425. if (m_iLookAroundDelay < lastmillis)
  426. {
  427. if (m_iLookAroundTime > lastmillis)
  428. {
  429. if (m_iLookAroundUpdateTime < lastmillis)
  430. {
  431. float flAddAngle;
  432. if (m_bLookLeft) flAddAngle = RandomFloat(-110, -80);
  433. else flAddAngle = RandomFloat(80, 110);
  434. m_pMyEnt->targetyaw = WrapYZAngle(m_pMyEnt->targetyaw + flAddAngle);
  435. m_iLookAroundUpdateTime = lastmillis + RandomLong(400, 800);
  436. }
  437. aimtopos = false;
  438. }
  439. else if (m_iLookAroundTime > 0)
  440. {
  441. m_iLookAroundTime = 0;
  442. m_iLookAroundDelay = lastmillis + RandomLong(750, 1000);
  443. }
  444. else
  445. m_iLookAroundTime = lastmillis + RandomLong(2200, 3200);
  446. }
  447. }
  448. if (aimtopos)
  449. AimToVec(m_vHuntLocation);
  450. debugbeam(m_pMyEnt->o, m_vHuntLocation);
  451. if (m_fYawToTurn <= 25.0f)
  452. m_iHuntLastTurnLessTime = lastmillis;
  453. // Bot had to turn much for a while?
  454. if ((m_iHuntLastTurnLessTime > 0) && (m_iHuntLastTurnLessTime < (lastmillis - 1000)))
  455. {
  456. m_iHuntPauseTime = lastmillis + 200;
  457. }
  458. if (m_iHuntPauseTime >= lastmillis)
  459. {
  460. m_pMyEnt->move = 0;
  461. m_fPrevHuntDist = 0.0f;
  462. }
  463. else
  464. {
  465. // Check if bot has to jump over a wall...
  466. if (CheckJump())
  467. m_pMyEnt->jumpnext = true;
  468. else // Check if bot has to jump to reach this location
  469. {
  470. float flHeightDiff = m_vHuntLocation.z - m_pMyEnt->o.z;
  471. bool bToHigh = false;
  472. if (Get2DDistance(m_vHuntLocation) <= 2.0f)
  473. {
  474. if (flHeightDiff >= 1.5f)
  475. {
  476. if (flHeightDiff <= JUMP_HEIGHT)
  477. {
  478. #ifndef RELEASE_BUILD
  479. char sz[64];
  480. sprintf(sz, "OldPos z diff: %f", m_vHuntLocation.z-m_pMyEnt->o.z);
  481. condebug(sz);
  482. #endif
  483. // Jump if close to pos and the pos is high
  484. m_pMyEnt->jumpnext = true;
  485. }
  486. else
  487. bToHigh = true;
  488. }
  489. }
  490. if (bToHigh)
  491. {
  492. m_pHuntTarget = NULL;
  493. m_vPrevHuntLocation = m_vHuntLocation;
  494. m_vHuntLocation = g_vecZero;
  495. m_fPrevHuntDist = 0.0f;
  496. m_iHuntDelay = lastmillis + 3500;
  497. return false;
  498. }
  499. }
  500. }
  501. return true;
  502. }
  503. void CBot::CheckWeaponSwitch()
  504. {
  505. if(m_pMyEnt->nextweaponsel == NULL) m_pMyEnt->weaponchanging = 0;
  506. if(!m_pMyEnt->weaponchanging) return;
  507. int timeprogress = lastmillis-m_pMyEnt->weaponchanging;
  508. if(timeprogress>weapon::weaponchangetime)
  509. {
  510. m_pMyEnt->prevweaponsel = m_pMyEnt->weaponsel;
  511. m_pMyEnt->weaponsel = m_pMyEnt->nextweaponsel;
  512. m_pMyEnt->weaponchanging = 0;
  513. m_iChangeWeaponDelay = 0;
  514. if(!m_pMyEnt->weaponsel->mag)
  515. {
  516. tryreload(m_pMyEnt);
  517. }
  518. }
  519. }
  520. void CBot::ShootEnemy()
  521. {
  522. if(!m_pMyEnt->enemy) return;
  523. if(!IsVisible(m_pMyEnt->enemy)) return;
  524. m_iSawEnemyTime = lastmillis;
  525. // Aim to enemy
  526. vec enemypos = GetEnemyPos(m_pMyEnt->enemy);
  527. AimToVec(enemypos);
  528. // Time to shoot?
  529. if (m_iShootDelay < lastmillis)
  530. //if ((lastmillis-m_pMyEnt->lastaction) >= m_pMyEnt->gunwait)
  531. {
  532. if (m_pMyEnt->mag[m_pMyEnt->gunselect])
  533. {
  534. // If the bot is using a sniper only shoot if crosshair is near the enemy
  535. if (WeaponInfoTable[m_pMyEnt->gunselect].eWeaponType == TYPE_SNIPER)
  536. {
  537. float yawtoturn = fabs(WrapYZAngle(m_pMyEnt->yaw - m_pMyEnt->targetyaw));
  538. float pitchtoturn = fabs(WrapYZAngle(m_pMyEnt->pitch - m_pMyEnt->targetpitch));
  539. if ((yawtoturn > 5) || (pitchtoturn > 15)) // UNDONE: Should be skill based
  540. return;
  541. }
  542. float flDist = GetDistance(enemypos);
  543. // Check if bot is in fire range
  544. if ((flDist < WeaponInfoTable[m_pMyEnt->gunselect].flMinFireDistance) ||
  545. (flDist > WeaponInfoTable[m_pMyEnt->gunselect].flMaxFireDistance))
  546. return;
  547. // Now shoot!
  548. m_pMyEnt->attacking = true;
  549. // Get the position the bot is aiming at
  550. vec forward, right, up, dest;
  551. traceresult_s tr;
  552. AnglesToVectors(GetViewAngles(), forward, right, up);
  553. dest = m_pMyEnt->o;
  554. forward.mul(1000);
  555. dest.add(forward);
  556. TraceLine(m_pMyEnt->o, dest, m_pMyEnt, false, &tr);
  557. debugbeam(m_pMyEnt->o, tr.end);
  558. shoot(m_pMyEnt, tr.end);
  559. // Add shoot delay
  560. m_iShootDelay = lastmillis + GetShootDelay();
  561. }
  562. }
  563. #ifndef RELEASE_BUILD
  564. else
  565. {
  566. char sz[64];
  567. sprintf(sz, "shootdelay: %d\n", (m_iShootDelay-lastmillis));
  568. AddDebugText(sz);
  569. }
  570. #endif
  571. }
  572. bool CBot::ChoosePreferredWeapon()
  573. {
  574. return true;
  575. }
  576. int CBot::GetShootDelay()
  577. {
  578. // UNDONE
  579. return m_pMyEnt->gunwait[m_pMyEnt->gunselect];
  580. if ((WeaponInfoTable[m_pMyEnt->gunselect].eWeaponType == TYPE_MELEE) ||
  581. (WeaponInfoTable[m_pMyEnt->gunselect].eWeaponType == TYPE_AUTO))
  582. return m_pMyEnt->gunwait[m_pMyEnt->gunselect];
  583. float flMinShootDelay = m_pBotSkill->flMinAttackDelay;
  584. float flMaxShootDelay = m_pBotSkill->flMaxAttackDelay;
  585. return max(m_pMyEnt->gunwait[m_pMyEnt->gunselect], int(RandomFloat(flMinShootDelay, flMaxShootDelay) * 1000.0f));
  586. }
  587. void CBot::CheckReload() // reload gun if no enemies are around
  588. {
  589. if(m_pMyEnt->mag[m_pMyEnt->weaponsel->type] >= WeaponInfoTable[m_pMyEnt->weaponsel->type].sMinDesiredAmmo) return; // do not reload if mindesiredammo is satisfied
  590. if(m_pMyEnt->enemy && m_pMyEnt->mag[m_pMyEnt->weaponsel->type])
  591. {
  592. return; // ignore the enemy, if no ammo in mag.
  593. }
  594. tryreload(m_pMyEnt);
  595. return;
  596. }
  597. void CBot::CheckScope()
  598. {
  599. #define MINSCOPEDIST 15
  600. #define MINSCOPETIME 1000
  601. if(m_pMyEnt->weaponsel->type != GUN_SNIPER) return;
  602. sniperrifle *sniper = (sniperrifle *)m_pMyEnt->weaponsel;
  603. if(m_pMyEnt->enemy && m_pMyEnt->o.dist(m_pMyEnt->enemy->o) > MINSCOPEDIST)
  604. {
  605. sniper->setscope(true);
  606. }
  607. else if(m_pMyEnt->scoping && lastmillis - sniper->scoped_since < MINSCOPETIME)
  608. {
  609. sniper->setscope(false);
  610. }
  611. }
  612. void CBot::MainAI()
  613. {
  614. // Default bots will run forward
  615. m_pMyEnt->move = 1;
  616. // Default bots won't strafe
  617. m_pMyEnt->strafe = 0;
  618. // Whatever the bot is doing, check for needed crouch
  619. if(CheckCrouch()) m_pMyEnt->trycrouch = true;
  620. else m_pMyEnt->trycrouch = false;
  621. if (!BotManager.BotsShoot() && m_pMyEnt->enemy)
  622. m_pMyEnt->enemy = NULL; // Clear enemy when bots may not shoot
  623. if (m_bGoToDebugGoal) // For debugging the waypoint navigation
  624. {
  625. if (!HeadToGoal())
  626. {
  627. ResetWaypointVars();
  628. m_vGoal = g_vecZero;
  629. }
  630. else
  631. AddDebugText("Heading to debug goal...");
  632. }
  633. if (BotManager.BotsShoot() && FindEnemy()) // Combat
  634. {
  635. CheckReload();
  636. CheckScope();
  637. AddDebugText("has enemy");
  638. // Use best weapon
  639. ChoosePreferredWeapon();
  640. // Shoot at enemy
  641. ShootEnemy();
  642. if (m_eCurrentBotState != STATE_ENEMY)
  643. {
  644. m_vGoal = g_vecZero;
  645. ResetWaypointVars();
  646. }
  647. m_eCurrentBotState = STATE_ENEMY;
  648. if (!CheckJump())
  649. DoCombatNav();
  650. }
  651. else if (CheckHunt() && HuntEnemy())
  652. {
  653. CheckReload();
  654. CheckScope();
  655. AddDebugText("Hunting to %s", m_pHuntTarget->name);
  656. m_eCurrentBotState = STATE_HUNT;
  657. }
  658. // Heading to an interesting entity(ammo, armour etc)
  659. else if (CheckItems())
  660. {
  661. CheckReload();
  662. AddDebugText("has ent");
  663. m_eCurrentBotState = STATE_ENT;
  664. }
  665. else if (m_classicsp && DoSPStuff()) // Home to goal, find/follow friends etc.
  666. {
  667. AddDebugText("SP stuff");
  668. m_eCurrentBotState = STATE_SP;
  669. }
  670. else // Normal navigation
  671. {
  672. CheckReload();
  673. if (m_eCurrentBotState != STATE_NORMAL)
  674. {
  675. m_vGoal = g_vecZero;
  676. ResetWaypointVars();
  677. }
  678. m_eCurrentBotState = STATE_NORMAL;
  679. bool bDoNormalNav = true;
  680. AddDebugText("normal nav");
  681. // Make sure the bot looks straight forward and not up or down
  682. m_pMyEnt->pitch = 0;
  683. // if it is time to look for a waypoint AND if there are waypoints in this
  684. // level...
  685. if (WaypointClass.m_iWaypointCount >= 1)
  686. {
  687. // check if we need to find a waypoint...
  688. if (CurrentWPIsValid() == false)
  689. {
  690. if (m_iLookForWaypointTime <= lastmillis)
  691. {
  692. // find the nearest reachable waypoint
  693. waypoint_s *pWP = GetNearestWaypoint(10.0f);
  694. if (pWP && (pWP != m_pCurrentWaypoint))
  695. {
  696. SetCurrentWaypoint(pWP);
  697. condebug("New nav wp");
  698. bDoNormalNav = !HeadToWaypoint();
  699. if (bDoNormalNav)
  700. ResetWaypointVars();
  701. }
  702. else
  703. ResetWaypointVars();
  704. m_iLookForWaypointTime = lastmillis + 250;
  705. }
  706. }
  707. else
  708. {
  709. bDoNormalNav = !HeadToWaypoint();
  710. if (bDoNormalNav)
  711. ResetWaypointVars();
  712. AddDebugText("Using wps for nav");
  713. }
  714. }
  715. // If nothing special, do regular (waypointless) navigation
  716. if(bDoNormalNav)
  717. {
  718. // Is the bot underwater?
  719. if (UnderWater(m_pMyEnt->o) && WaterNav())
  720. {
  721. // Bot is under water, navigation happens in WaterNav
  722. }
  723. // Time to check the environment?
  724. else if (m_iCheckEnvDelay < lastmillis)
  725. {
  726. if (m_vWaterGoal!=g_vecZero) m_vWaterGoal = g_vecZero;
  727. // Check for stuck and strafe
  728. if (UnderWater(m_pMyEnt->o) || !CheckStuck())
  729. {
  730. // Only do this when the bot is underwater or when the bot isn't stuck
  731. // Check field of view (FOV)
  732. CheckFOV();
  733. }
  734. }
  735. // Check if the bot has to strafe
  736. CheckStrafe();
  737. m_pMyEnt->move = 1;
  738. }
  739. }
  740. }
  741. void CBot::DoCombatNav()
  742. {
  743. if (m_iCombatNavTime > lastmillis)
  744. {
  745. // If bot has a lower skill and has to turn much, wait
  746. if ((m_sSkillNr > 2) && (m_fYawToTurn > 90.0f))
  747. {
  748. ResetMoveSpeed();
  749. }
  750. else
  751. {
  752. SetMoveDir(m_iMoveDir, false);
  753. }
  754. return;
  755. }
  756. if (m_bCombatJump)
  757. {
  758. m_pMyEnt->jumpnext = true;
  759. m_bCombatJump = false;
  760. m_iCombatJumpDelay = lastmillis + RandomLong(1500, 2800);
  761. return;
  762. }
  763. m_iMoveDir = DIR_NONE;
  764. // Check if bot is on top of his enemy
  765. float r = m_pMyEnt->radius+m_pMyEnt->enemy->radius;
  766. if ((fabs(m_pMyEnt->enemy->o.x-m_pMyEnt->o.x)<r &&
  767. fabs(m_pMyEnt->enemy->o.y-m_pMyEnt->o.y)<r) &&
  768. ((m_pMyEnt->enemy->o.z+m_pMyEnt->enemy->aboveeye) < (m_pMyEnt->o.z + m_pMyEnt->aboveeye)))
  769. {
  770. // Try to get off him!
  771. condebug("On enemy!");
  772. TMultiChoice<int> AwayDirChoices;
  773. if (IsVisible(LEFT, 4.0f, false))
  774. AwayDirChoices.Insert(LEFT);
  775. if (IsVisible(RIGHT, 4.0f, false))
  776. AwayDirChoices.Insert(RIGHT);
  777. if (IsVisible(FORWARD, 4.0f, false))
  778. AwayDirChoices.Insert(FORWARD);
  779. if (IsVisible(BACKWARD, 4.0f, false))
  780. AwayDirChoices.Insert(BACKWARD);
  781. int iDir;
  782. if (AwayDirChoices.GetSelection(iDir))
  783. {
  784. m_iMoveDir = iDir;
  785. m_iCombatNavTime = lastmillis + 500;
  786. }
  787. }
  788. float flDist = GetDistance(m_pMyEnt->enemy->o);
  789. // Check for nearby items?
  790. if (((m_iCheckEntsDelay < lastmillis) || m_pTargetEnt) &&
  791. m_pBotSkill->bCanSearchItemsInCombat)
  792. {
  793. m_iCheckEntsDelay = lastmillis + 125;
  794. bool bSearchItems = false;
  795. if (m_pTargetEnt)
  796. {
  797. // Bot has already found an entity, still valid?
  798. vec v(m_pTargetEnt->x, m_pTargetEnt->y,
  799. S(m_pTargetEnt->x, m_pTargetEnt->y)->floor+m_pMyEnt->eyeheight);
  800. if ((GetDistance(v) > 25.0f) || !IsVisible(m_pTargetEnt))
  801. m_pTargetEnt = NULL;
  802. }
  803. if (!m_pTargetEnt && (m_iCheckEntsDelay <= lastmillis))
  804. {
  805. if (WeaponInfoTable[m_pMyEnt->gunselect].eWeaponType == TYPE_MELEE)
  806. bSearchItems = (flDist >= 8.0f);
  807. else
  808. bSearchItems = (m_pMyEnt->ammo[m_pMyEnt->gunselect] <=
  809. WeaponInfoTable[m_pMyEnt->gunselect].sMinDesiredAmmo);
  810. if (bSearchItems)
  811. m_pTargetEnt = SearchForEnts(false, 25.0f, 1.0f);
  812. }
  813. if (m_pTargetEnt)
  814. {
  815. condebug("Combat ent");
  816. vec v(m_pTargetEnt->x, m_pTargetEnt->y,
  817. S(m_pTargetEnt->x, m_pTargetEnt->y)->floor+m_pMyEnt->eyeheight);
  818. debugbeam(m_pMyEnt->o, v);
  819. float flHeightDiff = v.z - m_pMyEnt->o.z;
  820. bool bToHigh = false;
  821. // Check he height for this ent
  822. if (Get2DDistance(v) <= 2.0f)
  823. {
  824. if (flHeightDiff >= 1.5f)
  825. {
  826. if (flHeightDiff <= JUMP_HEIGHT)
  827. {
  828. #ifndef RELEASE_BUILD
  829. char sz[64];
  830. sprintf(sz, "Ent z diff: %f", v.z-m_pMyEnt->o.z);
  831. condebug(sz);
  832. #endif
  833. m_pMyEnt->jumpnext = true; // Jump if close to ent and the ent is high
  834. }
  835. else
  836. bToHigh = true;
  837. }
  838. }
  839. if (!bToHigh)
  840. {
  841. int iMoveDir = GetDirection(GetViewAngles(), m_pMyEnt->o, v);
  842. if (iMoveDir != DIR_NONE)
  843. {
  844. m_iMoveDir = iMoveDir;
  845. m_iCombatNavTime = lastmillis + RandomLong(125, 250);
  846. }
  847. // Check if bot needs to jump over something
  848. vec from = m_pMyEnt->o;
  849. from.z -= 1.0f;
  850. if (!IsVisible(from, iMoveDir, 3.0f, false))
  851. m_pMyEnt->jumpnext = true;
  852. return;
  853. }
  854. }
  855. }
  856. // High skill and enemy is close?
  857. if ((m_sSkillNr <= 1) && (m_fYawToTurn < 80.0f) && (flDist <= 20.0f) &&
  858. (m_iCombatJumpDelay < lastmillis))
  859. {
  860. // Randomly jump a bit, to avoid some basic firepower ;)
  861. // How lower the distance to the enemy, how higher the chance for a jump
  862. short sJumpPercent = (100 - ((short)flDist * 8));
  863. if (RandomLong(1, 100) <= sJumpPercent)
  864. {
  865. // Choose a nice direction to jump to
  866. // Is the enemy close?
  867. if ((GetDistance(m_pMyEnt->enemy->o) <= 4.0f) ||
  868. (WeaponInfoTable[m_pMyEnt->gunselect].eWeaponType == TYPE_MELEE))
  869. {
  870. m_iMoveDir = FORWARD; // Jump forward
  871. SetMoveDir(FORWARD, false);
  872. m_bCombatJump = true;
  873. }
  874. else if (WeaponInfoTable[m_pMyEnt->gunselect].eWeaponType != TYPE_MELEE)// else jump to a random direction
  875. {
  876. /*
  877. Directions to choose:
  878. - Forward-right
  879. - Right
  880. - Backward-right
  881. - Backward
  882. - Backward-left
  883. - Left
  884. - Forward-left
  885. */
  886. TMultiChoice<int> JumpDirChoices;
  887. short sForwardScore = ((flDist > 8.0f) || (flDist < 4.0f)) ? 20 : 10;
  888. short sBackwardScore = (flDist <= 6.0f) ? 20 : 10;
  889. short sStrafeScore = (flDist < 6.0f) ? 20 : 10;
  890. if (IsVisible((FORWARD | LEFT), 4.0f, false))
  891. JumpDirChoices.Insert((FORWARD | LEFT), sForwardScore);
  892. if (IsVisible((FORWARD | RIGHT), 4.0f, false))
  893. JumpDirChoices.Insert((FORWARD | RIGHT), sForwardScore);
  894. if (IsVisible(BACKWARD, 4.0f, false))
  895. JumpDirChoices.Insert(BACKWARD, sBackwardScore);
  896. if (IsVisible((BACKWARD | LEFT), 4.0f, false))
  897. JumpDirChoices.Insert((BACKWARD | LEFT), sBackwardScore);
  898. if (IsVisible((BACKWARD | RIGHT), 4.0f, false))
  899. JumpDirChoices.Insert((BACKWARD | RIGHT), sBackwardScore);
  900. if (IsVisible(LEFT, 4.0f, false))
  901. JumpDirChoices.Insert(LEFT, sStrafeScore);
  902. if (IsVisible(RIGHT, 4.0f, false))
  903. JumpDirChoices.Insert(RIGHT, sStrafeScore);
  904. int JumpDir;
  905. if (JumpDirChoices.GetSelection(JumpDir))
  906. {
  907. m_iMoveDir = JumpDir;
  908. SetMoveDir(JumpDir, false);
  909. m_bCombatJump = true;
  910. }
  911. }
  912. if (m_bCombatJump)
  913. {
  914. m_iCombatNavTime = lastmillis + RandomLong(125, 250);
  915. return;
  916. }
  917. }
  918. }
  919. if (WeaponInfoTable[m_pMyEnt->gunselect].eWeaponType == TYPE_MELEE)
  920. return; // Simply walk towards enemy if using a melee type
  921. flDist = Get2DDistance(m_pMyEnt->enemy->o);
  922. // Out of desired range for current weapon?
  923. if ((flDist <= WeaponInfoTable[m_pMyEnt->gunselect].flMinDesiredDistance) ||
  924. (flDist >= WeaponInfoTable[m_pMyEnt->gunselect].flMaxDesiredDistance))
  925. {
  926. if (flDist >= WeaponInfoTable[m_pMyEnt->gunselect].flMaxDesiredDistance)
  927. {
  928. m_iMoveDir = FORWARD;
  929. }
  930. else
  931. {
  932. m_iMoveDir = BACKWARD;
  933. }
  934. vec src, forward, right, up, dest, MyAngles = GetViewAngles(), o = m_pMyEnt->o;
  935. traceresult_s tr;
  936. // Is it furthest or farthest? bleh
  937. float flFurthestDist = 0;
  938. int bestdir = -1, dir = 0;
  939. bool moveback = (m_pMyEnt->move == -1);
  940. for(int j=-45;j<=45;j+=45)
  941. {
  942. src = MyAngles;
  943. src.y = WrapYZAngle(src.y + j);
  944. src.x = 0.0f;
  945. // If we're moving backwards, trace backwards
  946. if (moveback)
  947. src.y = WrapYZAngle(src.y + 180);
  948. AnglesToVectors(src, forward, right, up);
  949. dest = o;
  950. forward.mul(40);
  951. dest.add(forward);
  952. TraceLine(o, dest, m_pMyEnt, false, &tr);
  953. //debugbeam(origin, end);
  954. flDist = GetDistance(tr.end);
  955. if (flFurthestDist < flDist)
  956. {
  957. flFurthestDist = flDist;
  958. bestdir = dir;
  959. }
  960. dir++;
  961. }
  962. switch(bestdir)
  963. {
  964. case 0:
  965. if (moveback)
  966. m_iMoveDir |= RIGHT; // Strafe right
  967. else
  968. m_iMoveDir |= LEFT; // Strafe left
  969. break;
  970. case 2:
  971. if (moveback)
  972. m_iMoveDir |= LEFT; // Strafe left
  973. else
  974. m_iMoveDir |= RIGHT; // Strafe right
  975. break;
  976. }
  977. if (m_iMoveDir != DIR_NONE)
  978. {
  979. SetMoveDir(m_iMoveDir, false);
  980. m_iCombatNavTime = lastmillis + 500;
  981. }
  982. }
  983. else if (m_pBotSkill->bCircleStrafe) // Circle strafe when in desired range...
  984. {
  985. traceresult_s tr;
  986. vec angles, end, forward, right, up;
  987. TMultiChoice<int> StrafeDirChoices;
  988. // Check the left side...
  989. angles = GetViewAngles();
  990. angles.y = WrapYZAngle(angles.y - 75.0f); // Not 90 degrees because the bot
  991. // doesn't strafe in a straight line
  992. // (aims still to enemy).
  993. AnglesToVectors(angles, forward, right, up);
  994. end = m_pMyEnt->o;
  995. forward.mul(15.0f);
  996. end.add(forward);
  997. TraceLine(m_pMyEnt->o, end, m_pMyEnt, true, &tr);
  998. StrafeDirChoices.Insert(LEFT, (int)GetDistance(m_pMyEnt->o, tr.end));
  999. // Check the right side...
  1000. angles = GetViewAngles();
  1001. angles.y = WrapYZAngle(angles.y + 75.0f); // Not 90 degrees because the bot
  1002. // doesn't strafe in a straight line
  1003. // (aims still to enemy).
  1004. AnglesToVectors(angles, forward, right, up);
  1005. end = m_pMyEnt->o;
  1006. forward.mul(15.0f);
  1007. end.add(forward);
  1008. TraceLine(m_pMyEnt->o, end, m_pMyEnt, true, &tr);
  1009. StrafeDirChoices.Insert(RIGHT, (int)GetDistance(m_pMyEnt->o, tr.end));
  1010. int StrafeDir;
  1011. if (StrafeDirChoices.GetSelection(StrafeDir))
  1012. {
  1013. m_iMoveDir = StrafeDir;
  1014. SetMoveDir(StrafeDir, false);
  1015. m_iCombatNavTime = lastmillis + RandomLong(1500, 3000);
  1016. }
  1017. }
  1018. else // Bot can't circle strafe(low skill), just stand still
  1019. ResetMoveSpeed();
  1020. }
  1021. bool CBot::CheckStuck()
  1022. {
  1023. if (m_iStuckCheckDelay + (CheckCrouch() ? 2000 : 0) >= lastmillis)
  1024. return false;
  1025. if ((m_vGoal!=g_vecZero) && (GetDistance(m_vGoal) < 2.0f))
  1026. return false;
  1027. bool IsStuck = false;
  1028. vec CurPos = m_pMyEnt->o, PrevPos = m_vPrevOrigin;
  1029. CurPos.z = PrevPos.z = 0;
  1030. // Did the bot hardly move the last frame?
  1031. if (GetDistance(CurPos, PrevPos) <= 0.1f)
  1032. {
  1033. if (m_bStuck)
  1034. {
  1035. if (m_iStuckTime < lastmillis)
  1036. IsStuck = true;
  1037. }
  1038. else
  1039. {
  1040. m_bStuck = true;
  1041. m_iStuckTime = lastmillis + 1000;
  1042. }
  1043. }
  1044. else
  1045. {
  1046. m_bStuck = false;
  1047. m_iStuckTime = 0;
  1048. }
  1049. if (IsStuck)
  1050. {
  1051. #ifndef RELEASE_BUILD
  1052. char msg[64];
  1053. sprintf(msg, "stuck (%f)", GetDistance(m_vPrevOrigin));
  1054. condebug(msg);
  1055. #endif
  1056. m_bStuck = false;
  1057. m_iStuckTime = 0;
  1058. // Crap bot is stuck, lets just try some random things
  1059. // Check if the bot can turn around
  1060. vec src = GetViewAngles();
  1061. src.x = 0;
  1062. vec forward, right, up, dir, dest;
  1063. traceresult_s tr;
  1064. AnglesToVectors(src, forward, right, up);
  1065. // Check the left side...
  1066. dir = right;
  1067. dest = m_pMyEnt->o;
  1068. dir.mul(3);
  1069. dest.sub(dir);
  1070. TraceLine(m_pMyEnt->o, dest, m_pMyEnt, false, &tr);
  1071. //debugbeam(m_pMyEnt->o, end);
  1072. if (!tr.collided)
  1073. {
  1074. // Bot can turn left, do so
  1075. m_pMyEnt->targetyaw = WrapYZAngle(m_pMyEnt->yaw - 90);
  1076. m_iStuckCheckDelay = m_iCheckEnvDelay = lastmillis + 500;
  1077. return true;
  1078. }
  1079. // Check the right side...
  1080. dir = right;
  1081. dest = m_pMyEnt->o;
  1082. dir.mul(3);
  1083. dest.add(dir);
  1084. TraceLine(m_pMyEnt->o, dest, m_pMyEnt, true, &tr);
  1085. //debugbeam(m_pMyEnt->o, end);
  1086. if (!tr.collided)
  1087. {
  1088. // Bot can turn right, do so
  1089. m_pMyEnt->targetyaw = WrapYZAngle(m_pMyEnt->yaw + 90);
  1090. m_iStuckCheckDelay = m_iCheckEnvDelay = lastmillis + 500;
  1091. return true;
  1092. }
  1093. // Check if bot can turn 180 degrees
  1094. dir = forward;
  1095. dest = m_pMyEnt->o;
  1096. dir.mul(3);
  1097. dest.add(dir);
  1098. TraceLine(m_pMyEnt->o, dest, m_pMyEnt, true, &tr);
  1099. //debugbeam(m_pMyEnt->o, end);
  1100. if (!tr.collided)
  1101. {
  1102. // Bot can turn around, do so
  1103. m_pMyEnt->targetyaw = WrapYZAngle(m_pMyEnt->yaw + 180);
  1104. m_iStuckCheckDelay = m_iCheckEnvDelay = lastmillis + 500;
  1105. return true;
  1106. }
  1107. // Bleh bot couldn't turn, lets just randomly jump :|
  1108. condebug("Randomly avoiding stuck...");
  1109. if (RandomLong(0, 2) == 0)
  1110. m_pMyEnt->jumpnext = true;
  1111. else
  1112. m_pMyEnt->targetyaw = WrapYZAngle(m_pMyEnt->yaw + RandomLong(60, 160));
  1113. return true;
  1114. }
  1115. return false;
  1116. }
  1117. // Check if a near wall is blocking and we can jump over it
  1118. bool CBot::CheckJump()
  1119. {
  1120. bool bHasGoal = m_vGoal!=g_vecZero;
  1121. float flGoalDist = (bHasGoal) ? GetDistance(m_vGoal) : 0.0f;
  1122. // if ((bHasGoal) && (flGoalDist < 2.0f))
  1123. // return false; UNDONE?
  1124. vec start = m_pMyEnt->o;
  1125. float flTraceDist = 3.0f;
  1126. if (bHasGoal && (flGoalDist < flTraceDist))
  1127. flTraceDist = flGoalDist;
  1128. // Something blocks at eye hight?
  1129. if (!IsVisible(start, FORWARD, flTraceDist, false))
  1130. {
  1131. // Check if the bot can jump over it
  1132. start.z += (JUMP_HEIGHT - 1.0f);
  1133. if (IsVisible(start) && !IsVisible(start, FORWARD, flTraceDist, false))
  1134. {
  1135. // Jump
  1136. debugnav("High wall");
  1137. m_pMyEnt->jumpnext = true;
  1138. return true;
  1139. }
  1140. }
  1141. else
  1142. {
  1143. // Check if something is blocking at feet height, so the bot can jump over it
  1144. start.z -= 1.7f;
  1145. // Trace was blocked?
  1146. if (!IsVisible(start, FORWARD, flTraceDist, false))
  1147. {
  1148. //debugbeam(start, end);
  1149. // Jump
  1150. debugnav("Low wall");
  1151. m_pMyEnt->jumpnext = true;
  1152. return true;
  1153. }
  1154. }
  1155. return false; // Bot didn't had to jump(or couldn't)
  1156. }
  1157. bool CBot::CheckCrouch()
  1158. {
  1159. bool bHasGoal = m_vGoal!=g_vecZero;
  1160. float flGoalDist = (bHasGoal) ? GetDistance(m_vGoal) : 0.0f;
  1161. vec start = m_pMyEnt->o;
  1162. vec crouch = vec(0, 0, 2.0f);
  1163. float flTraceDist = 3.0f;
  1164. if (bHasGoal && (flGoalDist < flTraceDist))
  1165. flTraceDist = flGoalDist;
  1166. if (!IsVisible(vec(start).add(crouch), FORWARD, flTraceDist, false) && IsVisible(vec(start).sub(crouch), FORWARD, flTraceDist, false)) return true;
  1167. return false;
  1168. }
  1169. bool CBot::CheckStrafe()
  1170. {
  1171. if (m_iStrafeTime >= lastmillis)
  1172. {
  1173. SetMoveDir(m_iMoveDir, true);
  1174. return true;
  1175. }
  1176. if (m_iStrafeCheckDelay >= lastmillis)
  1177. return false;
  1178. // Check for near walls
  1179. traceresult_s tr;
  1180. vec from = m_pMyEnt->o, to, forward, right, up, dir;
  1181. float flLeftDist = -1.0f, flRightDist = -1.0f;
  1182. bool bStrafe = false;
  1183. int iStrafeDir = DIR_NONE;
  1184. AnglesToVectors(GetViewAngles(), forward, right, up);
  1185. // Check for a near left wall
  1186. to = from;
  1187. dir = right;
  1188. dir.mul(3.0f);
  1189. to.sub(dir);
  1190. TraceLine(from, to, m_pMyEnt, false, &tr);
  1191. if (tr.collided)
  1192. flLeftDist = GetDistance(from, tr.end);
  1193. //debugbeam(m_pMyEnt->o, to);
  1194. // Check for a near right wall
  1195. to = from;
  1196. dir = right;
  1197. dir.mul(3.0f);
  1198. to.add(dir);
  1199. TraceLine(from, to, m_pMyEnt, false, &tr);
  1200. if (tr.collided)
  1201. flRightDist = GetDistance(from, tr.end);
  1202. //debugbeam(m_pMyEnt->o, to);
  1203. if ((flLeftDist == -1.0f) && (flRightDist == -1.0f))
  1204. {
  1205. dir = right;
  1206. dir.mul(m_pMyEnt->radius);
  1207. // Check left
  1208. from = m_pMyEnt->o;
  1209. from.sub(dir);
  1210. if (IsVisible(from, FORWARD, 3.0f, false, &flLeftDist))
  1211. flLeftDist = -1.0f;
  1212. // Check right
  1213. from = m_pMyEnt->o;
  1214. from.add(dir);
  1215. if (IsVisible(from, FORWARD, 3.0f, false, &flRightDist))
  1216. flRightDist = -1.0f;
  1217. }
  1218. if ((flLeftDist != -1.0f) && (flRightDist != -1.0f))
  1219. {
  1220. if (flLeftDist < flRightDist)
  1221. {
  1222. // Strafe right
  1223. bStrafe = true;
  1224. iStrafeDir = RIGHT;
  1225. }
  1226. else if (flRightDist < flLeftDist)
  1227. {
  1228. // Strafe left
  1229. bStrafe = true;
  1230. iStrafeDir = LEFT;
  1231. }
  1232. else
  1233. {
  1234. // Randomly choose a strafe direction
  1235. bStrafe = true;
  1236. if (RandomLong(0, 1))
  1237. iStrafeDir = LEFT;
  1238. else
  1239. iStrafeDir = RIGHT;
  1240. }
  1241. }
  1242. else if (flLeftDist != -1.0f)
  1243. {
  1244. // Strafe right
  1245. bStrafe = true;
  1246. iStrafeDir = RIGHT;
  1247. }
  1248. else if (flRightDist != -1.0f)
  1249. {
  1250. // Strafe left
  1251. bStrafe = true;
  1252. iStrafeDir = LEFT;
  1253. }
  1254. if (bStrafe)
  1255. {
  1256. SetMoveDir(iStrafeDir, true);
  1257. m_iMoveDir = iStrafeDir;
  1258. m_iStrafeTime = lastmillis + RandomLong(75, 150);
  1259. }
  1260. return bStrafe;
  1261. }
  1262. void CBot::CheckFOV()
  1263. {
  1264. m_iCheckEnvDelay = lastmillis + RandomLong(125, 250);
  1265. vec MyAngles = GetViewAngles();
  1266. vec src, forward, right, up, dest, best(0, 0, 0);
  1267. vec origin = m_pMyEnt->o;
  1268. float flDist, flFurthestDist = 0;
  1269. bool WallLeft = false;
  1270. traceresult_s tr;
  1271. //origin.z -= 1.5; // Slightly under eye level
  1272. // Scan 90 degrees FOV
  1273. for(int angle=-45;angle<=45;angle+=5)
  1274. {
  1275. src = MyAngles;
  1276. src.y = WrapYZAngle(src.y + angle);
  1277. AnglesToVectors(src, forward, right, up);
  1278. dest = origin;
  1279. forward.mul(40);
  1280. dest.add(forward);
  1281. TraceLine(origin, dest, m_pMyEnt, false, &tr);
  1282. //debugbeam(origin, end);
  1283. flDist = GetDistance(tr.end);
  1284. if (flFurthestDist < flDist)
  1285. {
  1286. flFurthestDist = flDist;
  1287. best = tr.end;
  1288. }
  1289. }
  1290. if (best.x && best.y && best.z)
  1291. {
  1292. AimToVec(best);
  1293. // Update MyAngles, since their (going to be) change(d)
  1294. MyAngles.x = m_pMyEnt->targetpitch;
  1295. MyAngles.y = m_pMyEnt->targetyaw;
  1296. }
  1297. float flNearestHitDist = GetDistance(best);
  1298. if (!UnderWater(m_pMyEnt->o) && m_pMyEnt->onfloor)
  1299. {
  1300. // Check if a near wall is blocking and we can jump over it
  1301. if (flNearestHitDist < 4)
  1302. {
  1303. // Check if the bot can jump over it
  1304. src = MyAngles;
  1305. src.x = 0;
  1306. AnglesToVectors(src, forward, right, up);
  1307. vec start = origin;
  1308. start.z += 2.0f;
  1309. dest = start;
  1310. forward.mul(6);
  1311. dest.add(forward);
  1312. TraceLine(start, dest, m_pMyEnt, false, &tr);
  1313. //debugbeam(start, end);
  1314. if (!tr.collided)
  1315. {
  1316. // Jump
  1317. debugnav("High wall");
  1318. m_pMyEnt->jumpnext = true;
  1319. m_iStrafeCheckDelay = lastmillis + RandomLong(250, 500);
  1320. return;
  1321. }
  1322. }
  1323. else
  1324. {
  1325. // Check if something is blocking below us, so the bot can jump over it
  1326. src = MyAngles;
  1327. src.x = 0;
  1328. AnglesToVectors(src, forward, right, up);
  1329. vec start = origin;
  1330. start.z -= 1.7f;
  1331. dest = start;
  1332. forward.mul(4);
  1333. dest.add(forward);
  1334. TraceLine(start, dest, m_pMyEnt, false, &tr);
  1335. // Trace was blocked?
  1336. if (tr.collided)
  1337. {
  1338. //debugbeam(start, end);
  1339. // Jump
  1340. debugnav("Low wall");
  1341. m_pMyEnt->jumpnext = true;
  1342. m_iStrafeCheckDelay = lastmillis + RandomLong(250, 500);
  1343. return;
  1344. }
  1345. }
  1346. // Check if the bot is going to fall...
  1347. src = MyAngles;
  1348. src.x = 0.0f;
  1349. AnglesToVectors(src, forward, right, up);
  1350. dest = origin;
  1351. forward.mul(3.0f);
  1352. dest.add(forward);
  1353. TraceLine(origin, dest, m_pMyEnt, false, &tr);
  1354. int cx = int(tr.end.x), cy = int(tr.end.y);
  1355. short CubesInWater=0;
  1356. for(int x=cx-1;x<=(cx+1);x++)
  1357. {
  1358. for(int y=cy-1;y<=(cy+1);y++)
  1359. {
  1360. if (OUTBORD(x, y)) continue;
  1361. //sqr *s = S(fast_f2nat(x), fast_f2nat(y));
  1362. //if (!SOLID(s))
  1363. {
  1364. vec from(x, y, m_pMyEnt->o.z);
  1365. dest = from;
  1366. dest.z -= 6.0f;
  1367. TraceLine(from, dest, m_pMyEnt, false, &tr);
  1368. bool turn = false;
  1369. if (UnderWater(tr.end)) CubesInWater++;
  1370. if (CubesInWater > 2) turn = true; // Always avoid water
  1371. if (!tr.collided && RandomLong(0, 1))
  1372. turn = true; // Randomly avoid a fall
  1373. if (turn)
  1374. {
  1375. m_pMyEnt->targetyaw = WrapYZAngle(m_pMyEnt->yaw + 180);
  1376. m_iCheckEnvDelay = m_iStrafeCheckDelay = lastmillis + RandomLong(750, 1500);
  1377. debugnav("Water or a fall in front");
  1378. return;
  1379. }
  1380. }
  1381. }
  1382. }
  1383. }
  1384. // Is the bot about to head a corner?
  1385. if (flNearestHitDist <= 4.0f)
  1386. {
  1387. src = MyAngles;
  1388. src.y = WrapYZAngle(src.y - 45.0f);
  1389. AnglesToVectors(src, forward, right, up);
  1390. dest = origin;
  1391. forward.mul(4.0f);
  1392. dest.add(forward);
  1393. TraceLine(origin, dest, m_pMyEnt, false, &tr);
  1394. WallLeft = (tr.collided);
  1395. src = MyAngles;
  1396. src.y += WrapYZAngle(src.y + 45.0f);
  1397. AnglesToVectors(src, forward, right, up);
  1398. dest = origin;
  1399. forward.mul(4.0f);
  1400. dest.add(forward);
  1401. TraceLine(origin, dest, m_pMyEnt, false, &tr);
  1402. if (WallLeft && tr.collided)
  1403. {
  1404. // We're about to hit a corner, turn away
  1405. debugnav("Corner");
  1406. m_pMyEnt->targetyaw = WrapYZAngle(m_pMyEnt->yaw + RandomFloat(160.0f, 200.0f));
  1407. m_iCheckEnvDelay = m_iStrafeCheckDelay = lastmillis + RandomLong(750, 1500);
  1408. return;
  1409. }
  1410. }
  1411. }
  1412. // Called when bot is underwater
  1413. bool CBot::WaterNav()
  1414. {
  1415. const int iSearchRange = 4;
  1416. if (m_vWaterGoal==g_vecZero)
  1417. {
  1418. AddDebugText("WaterNav");
  1419. // Find the nearest and reachable cube which isn't underwater
  1420. int cx = int(m_pMyEnt->o.x);
  1421. int cy = int(m_pMyEnt->o.y);
  1422. float flNearestDist = 9999.0f, flDist;
  1423. if (OUTBORD(cx, cy)) return false;
  1424. // Check all cubes in range...
  1425. for (int x=(cx-iSearchRange);x<=(cx+iSearchRange);x++)
  1426. {
  1427. for (int y=(cy-iSearchRange);y<=(cy+iSearchRange);y++)
  1428. {
  1429. sqr *s = S(x, y);
  1430. if (SOLID(s)) continue;
  1431. if ((x==cx) && (y==cy)) continue;
  1432. vec v(x, y, GetCubeFloor(x, y));
  1433. if (UnderWater(v)) continue; // Skip, cube is underwater
  1434. if (waterlevel < (v.z - 2.0f)) continue; // Cube is too high
  1435. // Check if the bot 'can fit' on the cube(no near obstacles)
  1436. bool small_ = false;
  1437. for (int a=(x-2);a<=(x+2);a++)
  1438. {
  1439. if (small_) break;
  1440. for (int b=(y-2);b<=(y+2);b++)
  1441. {
  1442. if ((x==a) && (y==b)) continue;
  1443. vec v2(a, b, GetCubeFloor(a, b));
  1444. if (v.z < (v2.z-1-JUMP_HEIGHT))
  1445. {
  1446. small_=true;
  1447. break;
  1448. }
  1449. if ((a >= (x-1)) && (a <= (x+1)) && (b >= (y-1)) && (b <= (y+1)))
  1450. {
  1451. if ((v2.z) < (v.z-2.0f))
  1452. {
  1453. small_ = true;
  1454. break;
  1455. }
  1456. }
  1457. traceresult_s tr;
  1458. TraceLine(v, v2, NULL, false, &tr);
  1459. if (tr.collided)
  1460. {
  1461. small_=true;
  1462. break;
  1463. }
  1464. }
  1465. if (small_) break;
  1466. }
  1467. if (small_)
  1468. {
  1469. debugbeam(m_pMyEnt->o, v);
  1470. continue;
  1471. }
  1472. // Okay, cube is valid.
  1473. flDist = GetDistance(v);
  1474. if (flDist < flNearestDist)
  1475. {
  1476. flNearestDist = flDist;
  1477. m_vWaterGoal = v;
  1478. }
  1479. }
  1480. }
  1481. }
  1482. if (m_vWaterGoal!=g_vecZero)
  1483. {
  1484. AddDebugText("WaterNav");
  1485. //debugbeam(m_pMyEnt->o, m_vWaterGoal);
  1486. vec aim = m_vWaterGoal;
  1487. aim.z += 1.5f; // Aim a bit further up
  1488. AimToVec(aim);
  1489. if ((RandomLong(1, 100) <= 15) && (Get2DDistance(m_vWaterGoal) <= 7.0f))
  1490. m_pMyEnt->jumpnext = true;
  1491. return true;
  1492. }
  1493. return false;
  1494. }
  1495. bool CBot::CheckItems()
  1496. {
  1497. if (!m_pCurrentGoalWaypoint && !CheckJump() && CheckStuck())
  1498. {
  1499. // Don't check for ents a while when stuck
  1500. m_iCheckEntsDelay = lastmillis + RandomLong(1000, 2000);
  1501. return false;
  1502. }
  1503. if (m_vGoal==g_vecZero)
  1504. m_pTargetEnt = NULL;
  1505. if (!m_pTargetEnt)
  1506. {
  1507. if (m_iCheckEntsDelay > lastmillis)
  1508. return false;
  1509. else
  1510. {
  1511. m_pTargetEnt = SearchForEnts(!m_classicsp);
  1512. m_iCheckEntsDelay = lastmillis + RandomLong(2500, 5000);
  1513. }
  1514. }
  1515. if (m_pTargetEnt)
  1516. {
  1517. if (HeadToTargetEnt())
  1518. return true;
  1519. }
  1520. if (m_eCurrentBotState == STATE_ENT)
  1521. {
  1522. ResetWaypointVars();
  1523. m_vGoal = g_vecZero;
  1524. m_pTargetEnt = NULL;
  1525. }
  1526. return false;
  1527. }
  1528. bool CBot::InUnreachableList(entity *e)
  1529. {
  1530. TLinkedList<unreachable_ent_s *>::node_s *p = m_UnreachableEnts.GetFirst();
  1531. while(p)
  1532. {
  1533. if (p->Entry->ent == e) return true;
  1534. p = p->next;
  1535. }
  1536. return false;
  1537. }
  1538. bool CBot::IsReachable(vec to, float flMaxHeight)
  1539. {
  1540. vec from = m_pMyEnt->o;
  1541. traceresult_s tr;
  1542. float curr_height, last_height;
  1543. float distance = GetDistance(from, to);
  1544. // is the destination close enough?
  1545. //if (distance < REACHABLE_RANGE)
  1546. {
  1547. if (IsVisible(to))
  1548. {
  1549. // Look if bot can 'fit trough'
  1550. vec src = from, forward, right, up;
  1551. AnglesToVectors(GetViewAngles(), forward, right, up);
  1552. // Trace from 1 cube to the left
  1553. vec temp = right;
  1554. temp.mul(1.0f);
  1555. src.sub(temp);
  1556. if (!::IsVisible(src, to)) return false;
  1557. // Trace from 1 cube to the right
  1558. src.add(temp);
  1559. if (!::IsVisible(src, to)) return false;
  1560. if (UnderWater(from) && UnderWater(to))
  1561. {
  1562. // No need to worry about heights in water
  1563. return true;
  1564. }
  1565. /*
  1566. if (to.z > (from.z + JUMP_HEIGHT))
  1567. {
  1568. vec v_new_src = to;
  1569. vec v_new_dest = to;
  1570. v_new_dest.z = v_new_dest.z - (JUMP_HEIGHT + 1.0f);
  1571. // check if we didn't hit anything, if so then it's in mid-air
  1572. if (::IsVisible(v_new_src, v_new_dest, NULL))
  1573. {
  1574. condebug("to is in midair");
  1575. debugbeam(from, to);
  1576. return false; // can't reach this one
  1577. }
  1578. }
  1579. */
  1580. // check if distance to ground increases more than jump height
  1581. // at points between from and to...
  1582. vec v_temp = to;
  1583. v_temp.sub(from);
  1584. vec v_direction = Normalize(v_temp); // 1 unit long
  1585. vec v_check = from;
  1586. vec v_down = from;
  1587. v_down.z = v_down.z - 100.0f; // straight down
  1588. TraceLine(v_check, v_down, NULL, false, &tr);
  1589. // height from ground
  1590. last_height = GetDistance(v_check, tr.end);
  1591. distance = GetDistance(to, v_check); // distance from goal
  1592. while (distance > 2.0f)
  1593. {
  1594. // move 2 units closer to the goal
  1595. v_temp = v_direction;
  1596. v_temp.mul(2.0f);
  1597. v_check.add(v_temp);
  1598. v_down = v_check;
  1599. v_down.z = v_down.z - 100.0f;
  1600. TraceLine(v_check, v_down, NULL, false, &tr);
  1601. curr_height = GetDistance(v_check, tr.end);
  1602. // is the difference in the last height and the current height
  1603. // higher that the jump height?
  1604. if ((last_height - curr_height) >= flMaxHeight)
  1605. {
  1606. // can't get there from here...
  1607. //condebug("traces failed to to");
  1608. debugbeam(from, to);
  1609. return false;
  1610. }
  1611. last_height = curr_height;
  1612. distance = GetDistance(to, v_check); // distance from goal
  1613. }
  1614. return true;
  1615. }
  1616. }
  1617. return false;
  1618. }
  1619. void CBot::HearSound(int n, vec *o)
  1620. {
  1621. // Has the bot already an enemy?
  1622. if (m_pMyEnt->enemy) return;
  1623. //fixmebot
  1624. // Is the sound not interesting?
  1625. if(n == S_DIE1 || n == S_DIE2) return;
  1626. int soundvol = m_pBotSkill->iMaxHearVolume -
  1627. (int)(GetDistance(*o)*3*m_pBotSkill->iMaxHearVolume/255);
  1628. if (soundvol == 0) return;
  1629. // Look who made the sound(check for the nearest enemy)
  1630. float flDist, flNearestDist = 3.0f; // Range of 3 units
  1631. playerent *pNearest = NULL;
  1632. // Check all bots first
  1633. loopv(bots)
  1634. {
  1635. playerent *d = bots[i];
  1636. if (d == m_pMyEnt || !d || (d->state != CS_ALIVE) ||
  1637. isteam(m_pMyEnt->team, d->team))
  1638. continue;
  1639. flDist = GetDistance(*o, d->o);
  1640. if ((flDist < flNearestDist) && IsVisible(d))
  1641. {
  1642. pNearest = d;
  1643. flNearestDist = flDist;
  1644. }
  1645. }
  1646. // Check local player
  1647. if (player1 && (player1->state == CS_ALIVE) &&
  1648. !isteam(m_pMyEnt->team, player1->team))
  1649. {
  1650. flDist = GetDistance(*o, player1->o);
  1651. if ((flDist < flNearestDist) && IsVisible(player1))
  1652. {
  1653. pNearest = player1;
  1654. flNearestDist = flDist;
  1655. }
  1656. }
  1657. if (pNearest)
  1658. {
  1659. if (m_pMyEnt->enemy != pNearest)
  1660. m_iShootDelay = lastmillis + GetShootDelay(); // Add shoot delay when new enemy found
  1661. m_pMyEnt->enemy = pNearest;
  1662. }
  1663. }
  1664. bool CBot::IsInFOV(const vec &o)
  1665. {
  1666. vec target, dir, forward, right, up;
  1667. float flDot, flAngle;
  1668. AnglesToVectors(GetViewAngles(), forward, right, up);
  1669. // direction the bot is aiming at
  1670. dir = forward;
  1671. dir.z = 0; // Make 2D
  1672. // ideal direction
  1673. target = o;
  1674. target.sub(m_pMyEnt->o);
  1675. target.z = 0.0f; // Make 2D
  1676. // angle between these two directions
  1677. flDot = target.dot(dir);
  1678. flAngle = acos(flDot/(target.magnitude() * dir.magnitude()));
  1679. return m_pBotSkill->iFov/2.0f >= flAngle/RAD;
  1680. }
  1681. // Code of CBot - End