~ubuntu-branches/debian/stretch/assaultcube-data/stretch

« back to all changes in this revision

Viewing changes to source/src/bot/bot_ai.cpp

  • Committer: Bazaar Package Importer
  • Author(s): Gonéri Le Bouder, Ansgar Burchardt, Gonéri Le Bouder
  • Date: 2010-04-02 23:37:55 UTC
  • mfrom: (1.1.1 upstream)
  • Revision ID: james.westby@ubuntu.com-20100402233755-kf74fxwlu634o6vg
Tags: 1.0.4+repack1-1
[ Ansgar Burchardt ]
* debian/control: fix typo in short description

[ Gonéri Le Bouder ]
* Upgrade to 1.0.4
* bump standards-version to 3.8.4
* Add Depends: ${misc:Depends} just to avoid a lintian warning
* Add a debian/source/format file for the same reason

Show diffs side-by-side

added added

removed removed

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