summaryrefslogtreecommitdiff
path: root/game/server/cstrike/bot/cs_bot.h
blob: e9530d5db14123cc2ba45e6531a45e3193c7b856 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150
1151
1152
1153
1154
1155
1156
1157
1158
1159
1160
1161
1162
1163
1164
1165
1166
1167
1168
1169
1170
1171
1172
1173
1174
1175
1176
1177
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
1193
1194
1195
1196
1197
1198
1199
1200
1201
1202
1203
1204
1205
1206
1207
1208
1209
1210
1211
1212
1213
1214
1215
1216
1217
1218
1219
1220
1221
1222
1223
1224
1225
1226
1227
1228
1229
1230
1231
1232
1233
1234
1235
1236
1237
1238
1239
1240
1241
1242
1243
1244
1245
1246
1247
1248
1249
1250
1251
1252
1253
1254
1255
1256
1257
1258
1259
1260
1261
1262
1263
1264
1265
1266
1267
1268
1269
1270
1271
1272
1273
1274
1275
1276
1277
1278
1279
1280
1281
1282
1283
1284
1285
1286
1287
1288
1289
1290
1291
1292
1293
1294
1295
1296
1297
1298
1299
1300
1301
1302
1303
1304
1305
1306
1307
1308
1309
1310
1311
1312
1313
1314
1315
1316
1317
1318
1319
1320
1321
1322
1323
1324
1325
1326
1327
1328
1329
1330
1331
1332
1333
1334
1335
1336
1337
1338
1339
1340
1341
1342
1343
1344
1345
1346
1347
1348
1349
1350
1351
1352
1353
1354
1355
1356
1357
1358
1359
1360
1361
1362
1363
1364
1365
1366
1367
1368
1369
1370
1371
1372
1373
1374
1375
1376
1377
1378
1379
1380
1381
1382
1383
1384
1385
1386
1387
1388
1389
1390
1391
1392
1393
1394
1395
1396
1397
1398
1399
1400
1401
1402
1403
1404
1405
1406
1407
1408
1409
1410
1411
1412
1413
1414
1415
1416
1417
1418
1419
1420
1421
1422
1423
1424
1425
1426
1427
1428
1429
1430
1431
1432
1433
1434
1435
1436
1437
1438
1439
1440
1441
1442
1443
1444
1445
1446
1447
1448
1449
1450
1451
1452
1453
1454
1455
1456
1457
1458
1459
1460
1461
1462
1463
1464
1465
1466
1467
1468
1469
1470
1471
1472
1473
1474
1475
1476
1477
1478
1479
1480
1481
1482
1483
1484
1485
1486
1487
1488
1489
1490
1491
1492
1493
1494
1495
1496
1497
1498
1499
1500
1501
1502
1503
1504
1505
1506
1507
1508
1509
1510
1511
1512
1513
1514
1515
1516
1517
1518
1519
1520
1521
1522
1523
1524
1525
1526
1527
1528
1529
1530
1531
1532
1533
1534
1535
1536
1537
1538
1539
1540
1541
1542
1543
1544
1545
1546
1547
1548
1549
1550
1551
1552
1553
1554
1555
1556
1557
1558
1559
1560
1561
1562
1563
1564
1565
1566
1567
1568
1569
1570
1571
1572
1573
1574
1575
1576
1577
1578
1579
1580
1581
1582
1583
1584
1585
1586
1587
1588
1589
1590
1591
1592
1593
1594
1595
1596
1597
1598
1599
1600
1601
1602
1603
1604
1605
1606
1607
1608
1609
1610
1611
1612
1613
1614
1615
1616
1617
1618
1619
1620
1621
1622
1623
1624
1625
1626
1627
1628
1629
1630
1631
1632
1633
1634
1635
1636
1637
1638
1639
1640
1641
1642
1643
1644
1645
1646
1647
1648
1649
1650
1651
1652
1653
1654
1655
1656
1657
1658
1659
1660
1661
1662
1663
1664
1665
1666
1667
1668
1669
1670
1671
1672
1673
1674
1675
1676
1677
1678
1679
1680
1681
1682
1683
1684
1685
1686
1687
1688
1689
1690
1691
1692
1693
1694
1695
1696
1697
1698
1699
1700
1701
1702
1703
1704
1705
1706
1707
1708
1709
1710
1711
1712
1713
1714
1715
1716
1717
1718
1719
1720
1721
1722
1723
1724
1725
1726
1727
1728
1729
1730
1731
1732
1733
1734
1735
1736
1737
1738
1739
1740
1741
1742
1743
1744
1745
1746
1747
1748
1749
1750
1751
1752
1753
1754
1755
1756
1757
1758
1759
1760
1761
1762
1763
1764
1765
1766
1767
1768
1769
1770
1771
1772
1773
1774
1775
1776
1777
1778
1779
1780
1781
1782
1783
1784
1785
1786
1787
1788
1789
1790
1791
1792
1793
1794
1795
1796
1797
1798
1799
1800
1801
1802
1803
1804
1805
1806
1807
1808
1809
1810
1811
1812
1813
1814
1815
1816
1817
1818
1819
1820
1821
1822
1823
1824
1825
1826
1827
1828
1829
1830
1831
1832
1833
1834
1835
1836
1837
1838
1839
1840
1841
1842
1843
1844
1845
1846
1847
1848
1849
1850
1851
1852
1853
1854
1855
1856
1857
1858
1859
1860
1861
1862
1863
1864
1865
1866
1867
1868
1869
1870
1871
1872
1873
1874
1875
1876
1877
1878
1879
1880
1881
1882
1883
1884
1885
1886
1887
1888
1889
1890
1891
1892
1893
1894
1895
1896
1897
1898
1899
1900
1901
1902
1903
1904
1905
1906
1907
1908
1909
1910
1911
1912
1913
1914
1915
1916
1917
1918
1919
1920
1921
1922
1923
1924
1925
1926
1927
1928
1929
1930
1931
1932
1933
1934
1935
1936
1937
1938
1939
1940
1941
1942
1943
1944
1945
1946
1947
1948
1949
1950
1951
1952
1953
1954
1955
1956
1957
1958
1959
1960
1961
1962
1963
1964
1965
1966
1967
1968
1969
1970
1971
1972
1973
1974
1975
1976
1977
1978
1979
1980
1981
1982
1983
1984
1985
1986
1987
1988
1989
1990
1991
1992
1993
1994
1995
1996
1997
1998
1999
2000
2001
2002
2003
2004
//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose: 
//
// $NoKeywords: $
//=============================================================================//

//
// Author: Michael S. Booth ([email protected]), 2003
//
// NOTE: The CS Bot code uses Doxygen-style comments. If you run Doxygen over this code, it will 
// auto-generate documentation.  Visit www.doxygen.org to download the system for free.
//

#ifndef _CS_BOT_H_
#define _CS_BOT_H_

#include "bot/bot.h"
#include "bot/cs_bot_manager.h"
#include "bot/cs_bot_chatter.h"
#include "cs_gamestate.h"
#include "cs_player.h"
#include "weapon_csbase.h"
#include "cs_nav_pathfind.h"
#include "cs_nav_area.h"

class CBaseDoor;
class CBasePropDoor;
class CCSBot;
class CPushAwayEnumerator;

//--------------------------------------------------------------------------------------------------------------
/**
 * For use with player->m_rgpPlayerItems[]
 */
enum InventorySlotType
{
	PRIMARY_WEAPON_SLOT = 1,
	PISTOL_SLOT,
	KNIFE_SLOT,
	GRENADE_SLOT,
	C4_SLOT
};


//--------------------------------------------------------------------------------------------------------------
/**
 * The definition of a bot's behavior state.  One or more finite state machines 
 * using these states implement a bot's behaviors.
 */
class BotState
{
public:
	virtual void OnEnter( CCSBot *bot ) { }				///< when state is entered
	virtual void OnUpdate( CCSBot *bot ) { }			///< state behavior
	virtual void OnExit( CCSBot *bot ) { }				///< when state exited
	virtual const char *GetName( void ) const = 0;		///< return state name
};


//--------------------------------------------------------------------------------------------------------------
/**
 * The state is invoked when a bot has nothing to do, or has finished what it was doing.
 * A bot never stays in this state - it is the main action selection mechanism.
 */
class IdleState : public BotState
{
public:
	virtual void OnEnter( CCSBot *bot );
	virtual void OnUpdate( CCSBot *bot );
	virtual const char *GetName( void ) const		{ return "Idle"; }
};


//--------------------------------------------------------------------------------------------------------------
/**
 * When a bot is actively searching for an enemy.
 */
class HuntState : public BotState
{
public:
	virtual void OnEnter( CCSBot *bot );
	virtual void OnUpdate( CCSBot *bot );
	virtual void OnExit( CCSBot *bot );
	virtual const char *GetName( void ) const		{ return "Hunt"; }

	void ClearHuntArea( void )						{ m_huntArea = NULL; }

private:
	CNavArea *m_huntArea;										///< "far away" area we are moving to
};


//--------------------------------------------------------------------------------------------------------------
/**
 * When a bot has an enemy and is attempting to kill it
 */
class AttackState : public BotState
{
public:
	virtual void OnEnter( CCSBot *bot );
	virtual void OnUpdate( CCSBot *bot );
	virtual void OnExit( CCSBot *bot );
	virtual const char *GetName( void ) const		{ return "Attack"; }
	
	void SetCrouchAndHold( bool crouch )			{ m_crouchAndHold = crouch; }

protected:
	enum DodgeStateType
	{
		STEADY_ON,
		SLIDE_LEFT,
		SLIDE_RIGHT,
		JUMP,

		NUM_ATTACK_STATES
	};
	DodgeStateType m_dodgeState;
	float m_nextDodgeStateTimestamp;

	CountdownTimer m_repathTimer;
	float m_scopeTimestamp;

	bool m_haveSeenEnemy;										///< false if we haven't yet seen the enemy since we started this attack (told by a friend, etc)
	bool m_isEnemyHidden;										///< true we if we have lost line-of-sight to our enemy
	float m_reacquireTimestamp;									///< time when we can fire again, after losing enemy behind cover
	float m_shieldToggleTimestamp;								///< time to toggle shield deploy state
	bool m_shieldForceOpen;										///< if true, open up and shoot even if in danger

	float m_pinnedDownTimestamp;								///< time when we'll consider ourselves "pinned down" by the enemy

	bool m_crouchAndHold;
	bool m_didAmbushCheck;
	bool m_shouldDodge;
	bool m_firstDodge;

	bool m_isCoward;											///< if true, we'll retreat if outnumbered during this fight
	CountdownTimer m_retreatTimer;

	void StopAttacking( CCSBot *bot );
	void Dodge( CCSBot *bot );									///< do dodge behavior
};


//--------------------------------------------------------------------------------------------------------------
/**
 * When a bot has heard an enemy noise and is moving to find out what it was.
 */
class InvestigateNoiseState : public BotState
{
public:
	virtual void OnEnter( CCSBot *bot );
	virtual void OnUpdate( CCSBot *bot );
	virtual void OnExit( CCSBot *bot );
	virtual const char *GetName( void ) const		{ return "InvestigateNoise"; }

private:
	void AttendCurrentNoise( CCSBot *bot );						///< move towards currently heard noise
	Vector m_checkNoisePosition;								///< the position of the noise we're investigating
	CountdownTimer m_minTimer;									///< minimum time we will investigate our current noise
};


//--------------------------------------------------------------------------------------------------------------
/**
 * When a bot is buying equipment at the start of a round.
 */
class BuyState : public BotState
{
public:
	virtual void OnEnter( CCSBot *bot );
	virtual void OnUpdate( CCSBot *bot );
	virtual void OnExit( CCSBot *bot );
	virtual const char *GetName( void ) const		{ return "Buy"; }

private:
	bool m_isInitialDelay;
	int m_prefRetries;											///< for retrying buying preferred weapon at current index
	int m_prefIndex;											///< where are we in our list of preferred weapons

	int m_retries;
	bool m_doneBuying;
	bool m_buyDefuseKit;
	bool m_buyGrenade;
	bool m_buyShield;
	bool m_buyPistol;
};


//--------------------------------------------------------------------------------------------------------------
/**
 * When a bot is moving to a potentially far away position in the world.
 */
class MoveToState : public BotState
{
public:
	virtual void OnEnter( CCSBot *bot );
	virtual void OnUpdate( CCSBot *bot );
	virtual void OnExit( CCSBot *bot );
	virtual const char *GetName( void ) const		{ return "MoveTo"; }
	void SetGoalPosition( const Vector &pos )		{ m_goalPosition = pos; }
	void SetRouteType( RouteType route )			{ m_routeType = route; }

private:
	Vector m_goalPosition;										///< goal position of move
	RouteType m_routeType;										///< the kind of route to build
	bool m_radioedPlan;
	bool m_askedForCover;
};


//--------------------------------------------------------------------------------------------------------------
/**
 * When a Terrorist bot is moving to pick up a dropped bomb.
 */
class FetchBombState : public BotState
{
public:
	virtual void OnEnter( CCSBot *bot );
	virtual void OnUpdate( CCSBot *bot );
	virtual const char *GetName( void ) const	{ return "FetchBomb"; }
};


//--------------------------------------------------------------------------------------------------------------
/**
 * When a Terrorist bot is actually planting the bomb.
 */
class PlantBombState : public BotState
{
public:
	virtual void OnEnter( CCSBot *bot );
	virtual void OnUpdate( CCSBot *bot );
	virtual void OnExit( CCSBot *bot );
	virtual const char *GetName( void ) const	{ return "PlantBomb"; }
};


//--------------------------------------------------------------------------------------------------------------
/**
 * When a CT bot is actually defusing a live bomb.
 */
class DefuseBombState : public BotState
{
public:
	virtual void OnEnter( CCSBot *bot );
	virtual void OnUpdate( CCSBot *bot );
	virtual void OnExit( CCSBot *bot );
	virtual const char *GetName( void ) const	{ return "DefuseBomb"; }
};


//--------------------------------------------------------------------------------------------------------------
/**
 * When a bot is hiding in a corner.
 * NOTE: This state also includes MOVING TO that hiding spot, which may be all the way
 * across the map!
 */
class HideState : public BotState
{
public:
	virtual void OnEnter( CCSBot *bot );
	virtual void OnUpdate( CCSBot *bot );
	virtual void OnExit( CCSBot *bot );
	virtual const char *GetName( void ) const	{ return "Hide"; }

	void SetHidingSpot( const Vector &pos )		{ m_hidingSpot = pos; }
	const Vector &GetHidingSpot( void ) const	{ return m_hidingSpot; }

	void SetSearchArea( CNavArea *area )		{ m_searchFromArea = area; }
	void SetSearchRange( float range )			{ m_range = range; }
	void SetDuration( float time )				{ m_duration = time; }
	void SetHoldPosition( bool hold )			{ m_isHoldingPosition = hold; }

	bool IsAtSpot( void ) const					{ return m_isAtSpot; }

	float GetHideTime( void ) const
	{
		if (IsAtSpot())
		{
			return m_duration - m_hideTimer.GetRemainingTime();
		}

		return 0.0f;
	}

private:
	CNavArea *m_searchFromArea;
	float m_range;

	Vector m_hidingSpot;
	bool m_isLookingOutward;
	bool m_isAtSpot;
	float m_duration;
	CountdownTimer m_hideTimer;								///< how long to hide

	bool m_isHoldingPosition;
	float m_holdPositionTime;								///< how long to hold our position after we hear nearby enemy noise

	bool m_heardEnemy;										///< set to true when we first hear an enemy
	float m_firstHeardEnemyTime;							///< when we first heard the enemy

	int m_retry;											///< counter for retrying hiding spot

	Vector m_leaderAnchorPos;								///< the position of our follow leader when we decided to hide

	bool m_isPaused;										///< if true, we have paused in our retreat for a moment
	CountdownTimer m_pauseTimer;							///< for stoppping and starting our pauses while we retreat
};


//--------------------------------------------------------------------------------------------------------------
/**
 * When a bot is attempting to flee from a bomb that is about to explode.
 */
class EscapeFromBombState : public BotState
{
public:
	virtual void OnEnter( CCSBot *bot );
	virtual void OnUpdate( CCSBot *bot );
	virtual void OnExit( CCSBot *bot );
	virtual const char *GetName( void ) const		{ return "EscapeFromBomb"; }
};


//--------------------------------------------------------------------------------------------------------------
/**
 * When a bot is following another player.
 */
class FollowState : public BotState
{
public:
	virtual void OnEnter( CCSBot *bot );
	virtual void OnUpdate( CCSBot *bot );
	virtual void OnExit( CCSBot *bot );
	virtual const char *GetName( void ) const		{ return "Follow"; }

	void SetLeader( CCSPlayer *player )				{ m_leader = player; }

private:
	CHandle< CCSPlayer > m_leader;								///< the player we are following
	Vector m_lastLeaderPos;										///< where the leader was when we computed our follow path
	bool m_isStopped;
	float m_stoppedTimestamp;

	enum LeaderMotionStateType
	{
		INVALID,
		STOPPED,
		WALKING,
		RUNNING
	};
	LeaderMotionStateType m_leaderMotionState;
	IntervalTimer m_leaderMotionStateTime;

	bool m_isSneaking;
	float m_lastSawLeaderTime;
	CountdownTimer m_repathInterval;

	IntervalTimer m_walkTime;
	bool m_isAtWalkSpeed;

	float m_waitTime;
	CountdownTimer m_idleTimer;

	void ComputeLeaderMotionState( float leaderSpeed );
};


//--------------------------------------------------------------------------------------------------------------
/**
 * When a bot is actually using another entity (ie: facing towards it and pressing the use key)
 */
class UseEntityState : public BotState
{
public:
	virtual void OnEnter( CCSBot *bot );
	virtual void OnUpdate( CCSBot *bot );
	virtual void OnExit( CCSBot *bot );
	virtual const char *GetName( void ) const			{ return "UseEntity"; }

	void SetEntity( CBaseEntity *entity )				{ m_entity = entity; }

private:
	EHANDLE m_entity;											///< the entity we will use
};


//--------------------------------------------------------------------------------------------------------------
/**
 * When a bot is opening a door
 */
class OpenDoorState : public BotState
{
public:
	virtual void OnEnter( CCSBot *bot );
	virtual void OnUpdate( CCSBot *bot );
	virtual void OnExit( CCSBot *bot );
	virtual const char *GetName( void ) const		{ return "OpenDoor"; }

	void SetDoor( CBaseEntity *door );

	bool IsDone( void ) const						{ return m_isDone; }	///< return true if behavior is done

private:
	CHandle< CBaseDoor > m_funcDoor;									///< the func_door we are opening
	CHandle< CBasePropDoor > m_propDoor;								///< the prop_door we are opening
	bool m_isDone;
	CountdownTimer m_timeout;
};


//--------------------------------------------------------------------------------------------------------------
//--------------------------------------------------------------------------------------------------------------
/**
 * The Counter-strike Bot
 */
class CCSBot : public CBot< CCSPlayer >
{
public:
	DECLARE_CLASS( CCSBot, CBot< CCSPlayer > );
	DECLARE_DATADESC();

	CCSBot( void );												///< constructor initializes all values to zero
	virtual ~CCSBot();
	virtual bool Initialize( const BotProfile *profile, int team );		///< (EXTEND) prepare bot for action

	virtual void Spawn( void );									///< (EXTEND) spawn the bot into the game
	virtual void Touch( CBaseEntity *other );					///< (EXTEND) when touched by another entity

	virtual void Upkeep( void );								///< lightweight maintenance, invoked frequently
	virtual void Update( void );								///< heavyweight algorithms, invoked less often
	virtual void BuildUserCmd( CUserCmd& cmd, const QAngle& viewangles, float forwardmove, float sidemove, float upmove, int buttons, byte impulse );
	virtual float GetMoveSpeed( void );							///< returns current movement speed (for walk/run)

	virtual void Walk( void );
	virtual bool Jump( bool mustJump = false );					///< returns true if jump was started

	//- behavior properties ------------------------------------------------------------------------------------------
	float GetCombatRange( void ) const;
	bool IsRogue( void ) const;									///< return true if we dont listen to teammates or pursue scenario goals
	void SetRogue( bool rogue );
	bool IsHurrying( void ) const;								///< return true if we are in a hurry 
	void Hurry( float duration );								///< force bot to hurry
	bool IsSafe( void ) const;									///< return true if we are in a safe region
	bool IsWellPastSafe( void ) const;							///< return true if it is well past the early, "safe", part of the round
	bool IsEndOfSafeTime( void ) const;							///< return true if we were in the safe time last update, but not now
	float GetSafeTimeRemaining( void ) const;					///< return the amount of "safe time" we have left
	float GetSafeTime( void ) const;							///< return what we think the total "safe time" for this map is
	virtual void Blind( float holdTime, float fadeTime, float startingAlpha = 255 );	// player blinded by a flashbang
	bool IsUnhealthy( void ) const;								///< returns true if bot is low on health
	
	bool IsAlert( void ) const;									///< return true if bot is in heightened "alert" mode
	void BecomeAlert( void );									///< bot becomes "alert" for immediately nearby enemies

	bool IsSneaking( void ) const;								///< return true if bot is sneaking
	void Sneak( float duration );								///< sneak for given duration

	//- behaviors ---------------------------------------------------------------------------------------------------
	void Idle( void );

	void Hide( CNavArea *searchFromArea = NULL, float duration = -1.0f, float hideRange = 750.0f, bool holdPosition = false );	///< DEPRECATED: Use TryToHide() instead
	#define USE_NEAREST true
	bool TryToHide( CNavArea *searchFromArea = NULL, float duration = -1.0f, float hideRange = 750.0f, bool holdPosition = false, bool useNearest = false );	///< try to hide nearby, return false if cannot
	void Hide( const Vector &hidingSpot, float duration = -1.0f, bool holdPosition = false );	///< move to the given hiding place
	bool IsHiding( void ) const;								///< returns true if bot is currently hiding
	bool IsAtHidingSpot( void ) const;							///< return true if we are hiding and at our hiding spot
	float GetHidingTime( void ) const;							///< return number of seconds we have been at our current hiding spot

	bool MoveToInitialEncounter( void );						///< move to a hiding spot and wait for initial encounter with enemy team (return false if no spots are available)

	bool TryToRetreat( float maxRange = 1000.0f, float duration = -1.0f );	///< retreat to a nearby hiding spot, away from enemies

	void Hunt( void );
	bool IsHunting( void ) const;								///< returns true if bot is currently hunting

	void Attack( CCSPlayer *victim );
	void FireWeaponAtEnemy( void );								///< fire our active weapon towards our current enemy
	void StopAttacking( void );
	bool IsAttacking( void ) const;								///< returns true if bot is currently engaging a target

	void MoveTo( const Vector &pos, RouteType route = SAFEST_ROUTE );	///< move to potentially distant position
	bool IsMovingTo( void ) const;								///< return true if we are in the MoveTo state

	void PlantBomb( void );

	void FetchBomb( void );										///< bomb has been dropped - go get it
	bool NoticeLooseBomb( void ) const;							///< return true if we noticed the bomb on the ground or on radar
	bool CanSeeLooseBomb( void ) const;							///< return true if we directly see the loose bomb

	void DefuseBomb( void );
	bool IsDefusingBomb( void ) const;							///< returns true if bot is currently defusing the bomb
	bool CanSeePlantedBomb( void ) const;						///< return true if we directly see the planted bomb

	void EscapeFromBomb( void );
	bool IsEscapingFromBomb( void ) const;						///< return true if we are escaping from the bomb

	void RescueHostages( void );								///< begin process of rescuing hostages

	void UseEntity( CBaseEntity *entity );						///< use the entity

	void OpenDoor( CBaseEntity *door );							///< open the door (assumes we are right in front of it)
	bool IsOpeningDoor( void ) const;							///< return true if we are in the process of opening a door

	void Buy( void );											///< enter the buy state
	bool IsBuying( void ) const;

	void Panic( void );											///< look around in panic
	bool IsPanicking( void ) const;								///< return true if bot is panicked
	void StopPanicking( void );									///< end our panic
	void UpdatePanicLookAround( void );							///< do panic behavior

	void TryToJoinTeam( int team );								///< try to join the given team

	void Follow( CCSPlayer *player );							///< begin following given Player
	void ContinueFollowing( void );								///< continue following our leader after finishing what we were doing
	void StopFollowing( void );									///< stop following
	bool IsFollowing( void ) const;								///< return true if we are following someone (not necessarily in the follow state)
	CCSPlayer *GetFollowLeader( void ) const;					///< return the leader we are following
	float GetFollowDuration( void ) const;						///< return how long we've been following our leader
	bool CanAutoFollow( void ) const;							///< return true if we can auto-follow

	bool IsNotMoving( float minDuration = 0.0f ) const;			///< return true if we are currently standing still and have been for minDuration

	void AimAtEnemy( void );									///< point our weapon towards our enemy
	void StopAiming( void );									///< stop aiming at enemy
	bool IsAimingAtEnemy( void ) const;							///< returns true if we are trying to aim at an enemy

	float GetStateTimestamp( void ) const;						///< get time current state was entered

	bool IsDoingScenario( void ) const;							///< return true if we will do scenario-related tasks

	//- scenario / gamestate -----------------------------------------------------------------------------------------
	CSGameState *GetGameState( void );							///< return an interface to this bot's gamestate
	const CSGameState *GetGameState( void ) const;				///< return an interface to this bot's gamestate

	bool IsAtBombsite( void );									///< return true if we are in a bomb planting zone
	bool GuardRandomZone( float range = 500.0f );				///< pick a random zone and hide near it

	bool IsBusy( void ) const;									///< return true if we are busy doing something important

	//- high-level tasks ---------------------------------------------------------------------------------------------
	enum TaskType
	{
		SEEK_AND_DESTROY,
		PLANT_BOMB,
		FIND_TICKING_BOMB,
		DEFUSE_BOMB,
		GUARD_TICKING_BOMB,
		GUARD_BOMB_DEFUSER,
		GUARD_LOOSE_BOMB,
		GUARD_BOMB_ZONE,
		GUARD_INITIAL_ENCOUNTER,
		ESCAPE_FROM_BOMB,
		HOLD_POSITION,
		FOLLOW,
		VIP_ESCAPE,
		GUARD_VIP_ESCAPE_ZONE,
		COLLECT_HOSTAGES,
		RESCUE_HOSTAGES,
		GUARD_HOSTAGES,
		GUARD_HOSTAGE_RESCUE_ZONE,
		MOVE_TO_LAST_KNOWN_ENEMY_POSITION,
		MOVE_TO_SNIPER_SPOT,
		SNIPING,

		NUM_TASKS
	};
	void SetTask( TaskType task, CBaseEntity *entity = NULL );	///< set our current "task"
	TaskType GetTask( void ) const;
	CBaseEntity *GetTaskEntity( void );
	const char *GetTaskName( void ) const;						///< return string describing current task

	//- behavior modifiers ------------------------------------------------------------------------------------------
	enum DispositionType
	{
		ENGAGE_AND_INVESTIGATE,								///< engage enemies on sight and investigate enemy noises
		OPPORTUNITY_FIRE,									///< engage enemies on sight, but only look towards enemy noises, dont investigate
		SELF_DEFENSE,										///< only engage if fired on, or very close to enemy
		IGNORE_ENEMIES,										///< ignore all enemies - useful for ducking around corners, running away, etc

		NUM_DISPOSITIONS
	};
	void SetDisposition( DispositionType disposition );		///< define how we react to enemies
	DispositionType GetDisposition( void ) const;
	const char *GetDispositionName( void ) const;			///< return string describing current disposition

	void IgnoreEnemies( float duration );					///< ignore enemies for a short duration

	enum MoraleType
	{
		TERRIBLE = -3,
		BAD = -2,
		NEGATIVE = -1,
		NEUTRAL = 0,
		POSITIVE = 1,
		GOOD = 2,
		EXCELLENT = 3,
	};
	MoraleType GetMorale( void ) const;
	const char *GetMoraleName( void ) const;				///< return string describing current morale
	void IncreaseMorale( void );
	void DecreaseMorale( void );

	void Surprise( float duration );						///< become "surprised" - can't attack
	bool IsSurprised( void ) const;							///< return true if we are "surprised"


	//- listening for noises ----------------------------------------------------------------------------------------
	bool IsNoiseHeard( void ) const;							///< return true if we have heard a noise
	bool HeardInterestingNoise( void );							///< return true if we heard an enemy noise worth checking in to
	void InvestigateNoise( void );								///< investigate recent enemy noise
	bool IsInvestigatingNoise( void ) const;					///< return true if we are investigating a noise
	const Vector *GetNoisePosition( void ) const;				///< return position of last heard noise, or NULL if none heard
	CNavArea *GetNoiseArea( void ) const;						///< return area where noise was heard
	void ForgetNoise( void );									///< clear the last heard noise
	bool CanSeeNoisePosition( void ) const;						///< return true if we directly see where we think the noise came from
	float GetNoiseRange( void ) const;							///< return approximate distance to last noise heard

	bool CanHearNearbyEnemyGunfire( float range = -1.0f ) const;///< return true if we hear nearby threatening enemy gunfire within given range (-1 == infinite)
	PriorityType GetNoisePriority( void ) const;				///< return priority of last heard noise

	//- radio and chatter--------------------------------------------------------------------------------------------
	void SendRadioMessage( RadioType event );					///< send a radio message
	void SpeakAudio( const char *voiceFilename, float duration, int pitch );	///< send voice chatter
	BotChatterInterface *GetChatter( void );					///< return an interface to this bot's chatter system
	bool RespondToHelpRequest( CCSPlayer *player, Place place, float maxRange = -1.0f );	///< decide if we should move to help the player, return true if we will
	bool IsUsingVoice() const;									///< new-style "voice" chatter gets voice feedback


	//- enemies ------------------------------------------------------------------------------------------------------
	// BOTPORT: GetEnemy() collides with GetEnemy() in CBaseEntity - need to use different nomenclature
	void SetBotEnemy( CCSPlayer *enemy );						///< set given player as our current enemy
	CCSPlayer *GetBotEnemy( void ) const;
	int GetNearbyEnemyCount( void ) const;						///< return max number of nearby enemies we've seen recently
	unsigned int GetEnemyPlace( void ) const;					///< return location where we see the majority of our enemies
	bool CanSeeBomber( void ) const;							///< return true if we can see the bomb carrier
	CCSPlayer *GetBomber( void ) const;

	int GetNearbyFriendCount( void ) const;						///< return number of nearby teammates
	CCSPlayer *GetClosestVisibleFriend( void ) const;			///< return the closest friend that we can see
	CCSPlayer *GetClosestVisibleHumanFriend( void ) const;		///< return the closest human friend that we can see

	bool IsOutnumbered( void ) const;							///< return true if we are outnumbered by enemies
	int OutnumberedCount( void ) const;							///< return number of enemies we are outnumbered by

	#define ONLY_VISIBLE_ENEMIES true
	CCSPlayer *GetImportantEnemy( bool checkVisibility = false ) const;	///< return the closest "important" enemy for the given scenario (bomb carrier, VIP, hostage escorter)

	void UpdateReactionQueue( void );							///< update our reaction time queue
	CCSPlayer *GetRecognizedEnemy( void );						///< return the most dangerous threat we are "conscious" of
	bool IsRecognizedEnemyReloading( void );					///< return true if the enemy we are "conscious" of is reloading
	bool IsRecognizedEnemyProtectedByShield( void );			///< return true if the enemy we are "conscious" of is hiding behind a shield
	float GetRangeToNearestRecognizedEnemy( void );				///< return distance to closest enemy we are "conscious" of

	CCSPlayer *GetAttacker( void ) const;						///< return last enemy that hurt us
	float GetTimeSinceAttacked( void ) const;					///< return duration since we were last injured by an attacker
	float GetFirstSawEnemyTimestamp( void ) const;				///< time since we saw any enemies
	float GetLastSawEnemyTimestamp( void ) const;
	float GetTimeSinceLastSawEnemy( void ) const;
	float GetTimeSinceAcquiredCurrentEnemy( void ) const;
	bool HasNotSeenEnemyForLongTime( void ) const;				///< return true if we haven't seen an enemy for "a long time"
	const Vector &GetLastKnownEnemyPosition( void ) const;
	bool IsEnemyVisible( void ) const;							///< is our current enemy visible
	float GetEnemyDeathTimestamp( void ) const;
	bool IsFriendInLineOfFire( void );							///< return true if a friend is in our weapon's way
	bool IsAwareOfEnemyDeath( void ) const;						///< return true if we *noticed* that our enemy died
	int GetLastVictimID( void ) const;							///< return the ID (entindex) of the last victim we killed, or zero

	bool CanSeeSniper( void ) const;							///< return true if we can see an enemy sniper
	bool HasSeenSniperRecently( void ) const;					///< return true if we have seen a sniper recently

	float GetTravelDistanceToPlayer( CCSPlayer *player ) const;	///< return shortest path travel distance to this player	
	bool DidPlayerJustFireWeapon( const CCSPlayer *player ) const;	///< return true if the given player just fired their weapon

	//- navigation --------------------------------------------------------------------------------------------------
	bool HasPath( void ) const;
	void DestroyPath( void );

	float GetFeetZ( void ) const;								///< return Z of bottom of feet

	enum PathResult
	{
		PROGRESSING,		///< we are moving along the path
		END_OF_PATH,		///< we reached the end of the path
		PATH_FAILURE		///< we failed to reach the end of the path
	};
	#define NO_SPEED_CHANGE false
	PathResult UpdatePathMovement( bool allowSpeedChange = true );	///< move along our computed path - if allowSpeedChange is true, bot will walk when near goal to ensure accuracy

	//bool AStarSearch( CNavArea *startArea, CNavArea *goalArea );	///< find shortest path from startArea to goalArea - don't actually buid the path
	bool ComputePath( const Vector &goal, RouteType route = SAFEST_ROUTE );	///< compute path to goal position
	bool StayOnNavMesh( void );
	CNavArea *GetLastKnownArea( void ) const;						///< return the last area we know we were inside of
	const Vector &GetPathEndpoint( void ) const;					///< return final position of our current path
	float GetPathDistanceRemaining( void ) const;					///< return estimated distance left to travel along path
	void ResetStuckMonitor( void );
	bool IsAreaVisible( const CNavArea *area ) const;				///< is any portion of the area visible to this bot
	const Vector &GetPathPosition( int index ) const;
	bool GetSimpleGroundHeightWithFloor( const Vector &pos, float *height, Vector *normal = NULL );	///< find "simple" ground height, treating current nav area as part of the floor
	void BreakablesCheck( void );
	void DoorCheck( void );											///< Check for any doors along our path that need opening

	virtual void PushawayTouch( CBaseEntity *pOther );

	Place GetPlace( void ) const;									///< get our current radio chatter place

	bool IsUsingLadder( void ) const;								///< returns true if we are in the process of negotiating a ladder
	void GetOffLadder( void );										///< immediately jump off of our ladder, if we're on one

	void SetGoalEntity( CBaseEntity *entity );
	CBaseEntity *GetGoalEntity( void );

	bool IsNearJump( void ) const;									///< return true if nearing a jump in the path
	float GetApproximateFallDamage( float height ) const;			///< return how much damage will will take from the given fall height

	void ForceRun( float duration );								///< force the bot to run if it moves for the given duration
	virtual bool IsRunning( void ) const;

	void Wait( float duration );									///< wait where we are for the given duration
	bool IsWaiting( void ) const;									///< return true if we are waiting
	void StopWaiting( void );										///< stop waiting

	void Wiggle( void );											///< random movement, for getting un-stuck

	bool IsFriendInTheWay( const Vector &goalPos );					///< return true if a friend is between us and the given position
	void FeelerReflexAdjustment( Vector *goalPosition );			///< do reflex avoidance movements if our "feelers" are touched

	bool HasVisitedEnemySpawn( void ) const;						///< return true if we have visited enemy spawn at least once
	bool IsAtEnemySpawn( void ) const;								///< return true if we are at the/an enemy spawn right now

	//- looking around ----------------------------------------------------------------------------------------------

	// BOTPORT: EVIL VILE HACK - why is EyePosition() not const?!?!?
	const Vector &EyePositionConst( void ) const;
	
	void SetLookAngles( float yaw, float pitch );					///< set our desired look angles
	void UpdateLookAngles( void );									///< move actual view angles towards desired ones
	void UpdateLookAround( bool updateNow = false );				///< update "looking around" mechanism
	void InhibitLookAround( float duration );						///< block all "look at" and "looking around" behavior for given duration - just look ahead

	/// @todo Clean up notion of "forward angle" and "look ahead angle"
	void SetForwardAngle( float angle );							///< define our forward facing
	void SetLookAheadAngle( float angle );							///< define default look ahead angle

	/// look at the given point in space for the given duration (-1 means forever)
	void SetLookAt( const char *desc, const Vector &pos, PriorityType pri, float duration = -1.0f, bool clearIfClose = false, float angleTolerance = 5.0f, bool attack = false );
	void ClearLookAt( void );										///< stop looking at a point in space and just look ahead
	bool IsLookingAtSpot( PriorityType pri = PRIORITY_LOW ) const;	///< return true if we are looking at spot with equal or higher priority
	bool IsViewMoving( float angleVelThreshold = 1.0f ) const;		///< returns true if bot's view angles are rotating (not still)
	bool HasViewBeenSteady( float duration ) const;					///< how long has our view been "steady" (ie: not moving) for given duration

	bool HasLookAtTarget( void ) const;								///< return true if we are in the process of looking at a target

	enum VisiblePartType
	{
		NONE		= 0x00,
		GUT			= 0x01,
		HEAD		= 0x02,
		LEFT_SIDE	= 0x04,			///< the left side of the object from our point of view (not their left side)
		RIGHT_SIDE	= 0x08,			///< the right side of the object from our point of view (not their right side)
		FEET		= 0x10
	};

	#define CHECK_FOV true
	bool IsVisible( const Vector &pos, bool testFOV = false, const CBaseEntity *ignore = NULL ) const;	///< return true if we can see the point
	bool IsVisible( CCSPlayer *player, bool testFOV = false, unsigned char *visParts = NULL ) const;	///< return true if we can see any part of the player

	bool IsNoticable( const CCSPlayer *player, unsigned char visibleParts ) const;	///< return true if we "notice" given player 

	bool IsEnemyPartVisible( VisiblePartType part ) const;			///< if enemy is visible, return the part we see for our current enemy
	const Vector &GetPartPosition( CCSPlayer *player, VisiblePartType part ) const;	///< return world space position of given part on player

	float ComputeWeaponSightRange( void );							///< return line-of-sight distance to obstacle along weapon fire ray

	bool IsAnyVisibleEnemyLookingAtMe( bool testFOV = false ) const;///< return true if any enemy I have LOS to is looking directly at me

	bool IsSignificantlyCloser( const CCSPlayer *testPlayer, const CCSPlayer *referencePlayer ) const;	///< return true if testPlayer is significantly closer than referencePlayer

	//- approach points ---------------------------------------------------------------------------------------------
	void ComputeApproachPoints( void );								///< determine the set of "approach points" representing where the enemy can enter this region
	void UpdateApproachPoints( void );								///< recompute the approach point set if we have moved far enough to invalidate the current ones
	void ClearApproachPoints( void );
	void DrawApproachPoints( void ) const;							///< for debugging
	float GetHidingSpotCheckTimestamp( HidingSpot *spot ) const;	///< return time when given spot was last checked
	void SetHidingSpotCheckTimestamp( HidingSpot *spot );			///< set the timestamp of the given spot to now

	const CNavArea *GetInitialEncounterArea( void ) const;			///< return area where we think we will first meet the enemy
	void SetInitialEncounterArea( const CNavArea *area );

	//- weapon query and equip --------------------------------------------------------------------------------------
	#define MUST_EQUIP true
	void EquipBestWeapon( bool mustEquip = false );					///< equip the best weapon we are carrying that has ammo
	void EquipPistol( void );										///< equip our pistol
	void EquipKnife( void );										///< equip the knife

	#define DONT_USE_SMOKE_GRENADE true
	bool EquipGrenade( bool noSmoke = false );						///< equip a grenade, return false if we cant

	bool IsUsingKnife( void ) const;								///< returns true if we have knife equipped
	bool IsUsingPistol( void ) const;								///< returns true if we have pistol equipped
	bool IsUsingGrenade( void ) const;								///< returns true if we have grenade equipped
	bool IsUsingSniperRifle( void ) const;							///< returns true if using a "sniper" rifle
	bool IsUsing( CSWeaponID weapon ) const;						///< returns true if using the specific weapon
	bool IsSniper( void ) const;									///< return true if we have a sniper rifle in our inventory
	bool IsSniping( void ) const;									///< return true if we are actively sniping (moving to sniper spot or settled in)
	bool IsUsingShotgun( void ) const;								///< returns true if using a shotgun
	bool IsUsingMachinegun( void ) const;							///< returns true if using the big 'ol machinegun
	void ThrowGrenade( const Vector &target );						///< begin the process of throwing the grenade
	bool IsThrowingGrenade( void ) const;							///< return true if we are in the process of throwing a grenade
	bool HasGrenade( void ) const;									///< return true if we have a grenade in our inventory
	void AvoidEnemyGrenades( void );								///< react to enemy grenades we see
	bool IsAvoidingGrenade( void ) const;							///< return true if we are in the act of avoiding a grenade
	bool DoesActiveWeaponHaveSilencer( void ) const;				///< returns true if we are using a weapon with a removable silencer
	bool CanActiveWeaponFire( void ) const;							///< returns true if our current weapon can attack
	CWeaponCSBase *GetActiveCSWeapon( void ) const;					///< get our current Counter-Strike weapon

	void GiveWeapon( const char *weaponAlias );						///< Debug command to give a named weapon

	virtual void PrimaryAttack( void );								///< presses the fire button, unless we're holding a pistol that can't fire yet (so we can just always call PrimaryAttack())

	enum ZoomType { NO_ZOOM, LOW_ZOOM, HIGH_ZOOM };
	ZoomType GetZoomLevel( void );									///< return the current zoom level of our weapon

	bool AdjustZoom( float range );									///< change our zoom level to be appropriate for the given range
	bool IsWaitingForZoom( void ) const;							///< return true if we are reacquiring after our zoom

	bool IsPrimaryWeaponEmpty( void ) const;						///< return true if primary weapon doesn't exist or is totally out of ammo
	bool IsPistolEmpty( void ) const;								///< return true if pistol doesn't exist or is totally out of ammo

	int GetHostageEscortCount( void ) const;						///< return the number of hostages following me
	void IncreaseHostageEscortCount( void );
	float GetRangeToFarthestEscortedHostage( void ) const;			///< return euclidean distance to farthest escorted hostage
	void ResetWaitForHostagePatience( void );

	//------------------------------------------------------------------------------------
	// Event hooks
	//

	/// invoked when injured by something (EXTEND) - returns the amount of damage inflicted
	virtual int OnTakeDamage( const CTakeDamageInfo &info );

	/// invoked when killed (EXTEND)
	virtual void Event_Killed( const CTakeDamageInfo &info );

	virtual bool BumpWeapon( CBaseCombatWeapon *pWeapon );		///< invoked when in contact with a CWeaponBox


	/// invoked when event occurs in the game (some events have NULL entity)
	void OnPlayerFootstep( IGameEvent *event );
	void OnPlayerRadio( IGameEvent *event );
	void OnPlayerDeath( IGameEvent *event );
	void OnPlayerFallDamage( IGameEvent *event );

	void OnBombPickedUp( IGameEvent *event );
	void OnBombPlanted( IGameEvent *event );
	void OnBombBeep( IGameEvent *event );
	void OnBombDefuseBegin( IGameEvent *event );
	void OnBombDefused( IGameEvent *event );
	void OnBombDefuseAbort( IGameEvent *event );
	void OnBombExploded( IGameEvent *event );

	void OnRoundEnd( IGameEvent *event );
	void OnRoundStart( IGameEvent *event );

	void OnDoorMoving( IGameEvent *event );

	void OnBreakProp( IGameEvent *event );
	void OnBreakBreakable( IGameEvent *event );

	void OnHostageFollows( IGameEvent *event );
	void OnHostageRescuedAll( IGameEvent *event );

	void OnWeaponFire( IGameEvent *event );
	void OnWeaponFireOnEmpty( IGameEvent *event );
	void OnWeaponReload( IGameEvent *event );
	void OnWeaponZoom( IGameEvent *event );

	void OnBulletImpact( IGameEvent *event );

	void OnHEGrenadeDetonate( IGameEvent *event );
	void OnFlashbangDetonate( IGameEvent *event );
	void OnSmokeGrenadeDetonate( IGameEvent *event );
	void OnGrenadeBounce( IGameEvent *event );

	void OnNavBlocked( IGameEvent *event );

	void OnEnteredNavArea( CNavArea *newArea );						///< invoked when bot enters a nav area

private:
	#define IS_FOOTSTEP true
	void OnAudibleEvent( IGameEvent *event, CBasePlayer *player, float range, PriorityType priority, bool isHostile, bool isFootstep = false, const Vector *actualOrigin = NULL );	///< Checks if the bot can hear the event

private:
	friend class CCSBotManager;

	/// @todo Get rid of these
	friend class AttackState;
	friend class BuyState;

	// BOTPORT: Remove this vile hack
	Vector m_eyePosition;

	void ResetValues( void );										///< reset internal data to initial state
	void BotDeathThink( void );

	char m_name[64];												///< copied from STRING(pev->netname) for debugging
	void DebugDisplay( void ) const;								///< render bot debug info

	//- behavior properties ------------------------------------------------------------------------------------------
	float m_combatRange;											///< desired distance between us and them during gunplay
	mutable bool m_isRogue;											///< if true, the bot is a "rogue" and listens to no-one
	mutable CountdownTimer m_rogueTimer;
	MoraleType m_morale;											///< our current morale, based on our win/loss history
	bool m_diedLastRound;											///< true if we died last round
	float m_safeTime;												///< duration at the beginning of the round where we feel "safe"
	bool m_wasSafe;													///< true if we were in the safe time last update
	void AdjustSafeTime( void );									///< called when enemy seen to adjust safe time for this round
	NavRelativeDirType m_blindMoveDir;								///< which way to move when we're blind
	bool m_blindFire;												///< if true, fire weapon while blinded
	CountdownTimer m_surpriseTimer;									///< when we were surprised

	bool m_isFollowing;												///< true if we are following someone
	CHandle< CCSPlayer > m_leader;									///< the ID of who we are following
	float m_followTimestamp;										///< when we started following
	float m_allowAutoFollowTime;									///< time when we can auto follow

	CountdownTimer m_hurryTimer;									///< if valid, bot is in a hurry
	CountdownTimer m_alertTimer;									///< if valid, bot is alert
	CountdownTimer m_sneakTimer;									///< if valid, bot is sneaking
	CountdownTimer m_panicTimer;									///< if valid, bot is panicking


	// instances of each possible behavior state, to avoid dynamic memory allocation during runtime
	IdleState				m_idleState;
	HuntState				m_huntState;
	AttackState				m_attackState;
	InvestigateNoiseState	m_investigateNoiseState;
	BuyState				m_buyState;
	MoveToState				m_moveToState;
	FetchBombState			m_fetchBombState;
	PlantBombState			m_plantBombState;
	DefuseBombState			m_defuseBombState;
	HideState				m_hideState;
	EscapeFromBombState		m_escapeFromBombState;
	FollowState				m_followState;
	UseEntityState			m_useEntityState;
	OpenDoorState			m_openDoorState;

	/// @todo Allow multiple simultaneous state machines (look around, etc)	
	void SetState( BotState *state );								///< set the current behavior state
	BotState *m_state;												///< current behavior state
	float m_stateTimestamp;											///< time state was entered
	bool m_isAttacking;												///< if true, special Attack state is overriding the state machine
	bool m_isOpeningDoor;											///< if true, special OpenDoor state is overriding the state machine

	TaskType m_task;												///< our current task
	EHANDLE m_taskEntity;											///< an entity used for our task

	//- navigation ---------------------------------------------------------------------------------------------------
	Vector m_goalPosition;
	EHANDLE m_goalEntity;
	void MoveTowardsPosition( const Vector &pos );					///< move towards position, independant of view angle
	void MoveAwayFromPosition( const Vector &pos );					///< move away from position, independant of view angle
	void StrafeAwayFromPosition( const Vector &pos );				///< strafe (sidestep) away from position, independant of view angle
	void StuckCheck( void );										///< check if we have become stuck
	CCSNavArea *m_currentArea;										///< the nav area we are standing on
	CCSNavArea *m_lastKnownArea;										///< the last area we were in
	EHANDLE m_avoid;												///< higher priority player we need to make way for
	float m_avoidTimestamp;
	bool m_isStopping;												///< true if we're trying to stop because we entered a 'stop' nav area
	bool m_hasVisitedEnemySpawn;									///< true if we have been at the enemy spawn
	IntervalTimer m_stillTimer;										///< how long we have been not moving

	//- path navigation data ----------------------------------------------------------------------------------------
	enum { MAX_PATH_LENGTH = 256 };
	struct ConnectInfo
	{
		CNavArea *area;												///< the area along the path
		NavTraverseType how;										///< how to enter this area from the previous one
		Vector pos;													///< our movement goal position at this point in the path
		const CNavLadder *ladder;									///< if "how" refers to a ladder, this is it
	}
	m_path[ MAX_PATH_LENGTH ];
	int m_pathLength;
	int m_pathIndex;												///< index of next area on path
	float m_areaEnteredTimestamp;
	void BuildTrivialPath( const Vector &goal );					///< build trivial path to goal, assuming we are already in the same area

	CountdownTimer m_repathTimer;									///< must have elapsed before bot can pathfind again

	bool ComputePathPositions( void );								///< determine actual path positions bot will move between along the path
	void SetupLadderMovement( void );
	void SetPathIndex( int index );									///< set the current index along the path
	void DrawPath( void );
	int FindOurPositionOnPath( Vector *close, bool local = false ) const;	///< compute the closest point to our current position on our path
	int FindPathPoint( float aheadRange, Vector *point, int *prevIndex = NULL );	///< compute a point a fixed distance ahead along our path.
	bool FindClosestPointOnPath( const Vector &pos, int startIndex, int endIndex, Vector *close ) const;	///< compute closest point on path to given point
	bool IsStraightLinePathWalkable( const Vector &goal ) const;	///< test for un-jumpable height change, or unrecoverable fall
	void ComputeLadderAngles( float *yaw, float *pitch );			///< computes ideal yaw/pitch for traversing the current ladder on our path

	mutable CountdownTimer m_avoidFriendTimer;						///< used to throttle how often we check for friends in our path
	mutable bool m_isFriendInTheWay;								///< true if a friend is blocking our path
	CountdownTimer m_politeTimer;									///< we'll wait for friend to move until this runs out
	bool m_isWaitingBehindFriend;									///< true if we are waiting for a friend to move

	#define ONLY_JUMP_DOWN true
	bool DiscontinuityJump( float ground, bool onlyJumpDown = false, bool mustJump = false ); ///< check if we need to jump due to height change

	enum LadderNavState
	{
		APPROACH_ASCENDING_LADDER,									///< prepare to scale a ladder
		APPROACH_DESCENDING_LADDER,									///< prepare to go down ladder 
		FACE_ASCENDING_LADDER,
		FACE_DESCENDING_LADDER,
		MOUNT_ASCENDING_LADDER,										///< move toward ladder until "on" it
		MOUNT_DESCENDING_LADDER,									///< move toward ladder until "on" it
		ASCEND_LADDER,												///< go up the ladder
		DESCEND_LADDER,												///< go down the ladder
		DISMOUNT_ASCENDING_LADDER,									///< get off of the ladder
		DISMOUNT_DESCENDING_LADDER,									///< get off of the ladder
		MOVE_TO_DESTINATION,										///< dismount ladder and move to destination area
	}
	m_pathLadderState;
	bool m_pathLadderFaceIn;										///< if true, face towards ladder, otherwise face away
	const CNavLadder *m_pathLadder;									///< the ladder we need to use to reach the next area
	bool UpdateLadderMovement( void );								///< called by UpdatePathMovement()
	NavRelativeDirType m_pathLadderDismountDir;						///< which way to dismount
	float m_pathLadderDismountTimestamp;							///< time when dismount started
	float m_pathLadderEnd;											///< if ascending, z of top, if descending z of bottom
	void ComputeLadderEndpoint( bool ascending );
	float m_pathLadderTimestamp;									///< time when we started using ladder - for timeout check

	CountdownTimer m_mustRunTimer;									///< if nonzero, bot cannot walk
	CountdownTimer m_waitTimer;										///< if nonzero, we are waiting where we are

	void UpdateTravelDistanceToAllPlayers( void );					///< periodically compute shortest path distance to each player
	CountdownTimer m_updateTravelDistanceTimer;						///< for throttling travel distance computations
	float m_playerTravelDistance[ MAX_PLAYERS ];					///< current distance from this bot to each player
	unsigned char m_travelDistancePhase;							///< a counter for optimizing when to compute travel distance

	//- game scenario mechanisms -------------------------------------------------------------------------------------
	CSGameState m_gameState;										///< our current knowledge about the state of the scenario

	byte m_hostageEscortCount;										///< the number of hostages we're currently escorting
	void UpdateHostageEscortCount( void );							///< periodic check of hostage count in case we lost some
	float m_hostageEscortCountTimestamp;

	int m_desiredTeam;												///< the team we want to be on
	bool m_hasJoined;												///< true if bot has actually joined the game

	bool m_isWaitingForHostage;
	CountdownTimer m_inhibitWaitingForHostageTimer;					///< if active, inhibits us waiting for lagging hostages
	CountdownTimer m_waitForHostageTimer;							///< stops us waiting too long

	//- listening mechanism ------------------------------------------------------------------------------------------
	Vector m_noisePosition;											///< position we last heard non-friendly noise
	float m_noiseTravelDistance;									///< the travel distance to the noise
	float m_noiseTimestamp;											///< when we heard it (can get zeroed)
	CNavArea *m_noiseArea;											///< the nav area containing the noise
	PriorityType m_noisePriority;									///< priority of currently heard noise
	bool UpdateLookAtNoise( void );									///< return true if we decided to look towards the most recent noise source
	CountdownTimer m_noiseBendTimer;								///< for throttling how often we bend our line of sight to the noise location
	Vector m_bentNoisePosition;										///< the last computed bent line of sight
	bool m_bendNoisePositionValid;

	//- "looking around" mechanism -----------------------------------------------------------------------------------
	float m_lookAroundStateTimestamp;								///< time of next state change
	float m_lookAheadAngle;											///< our desired forward look angle
	float m_forwardAngle;											///< our current forward facing direction
	float m_inhibitLookAroundTimestamp;								///< time when we can look around again

	enum LookAtSpotState
	{
		NOT_LOOKING_AT_SPOT,			///< not currently looking at a point in space
		LOOK_TOWARDS_SPOT,				///< in the process of aiming at m_lookAtSpot
		LOOK_AT_SPOT,					///< looking at m_lookAtSpot
		NUM_LOOK_AT_SPOT_STATES
	}
	m_lookAtSpotState;
	Vector m_lookAtSpot;											///< the spot we're currently looking at
	PriorityType m_lookAtSpotPriority;
	float m_lookAtSpotDuration;										///< how long we need to look at the spot
	float m_lookAtSpotTimestamp;									///< when we actually began looking at the spot
	float m_lookAtSpotAngleTolerance;								///< how exactly we must look at the spot
	bool m_lookAtSpotClearIfClose;									///< if true, the look at spot is cleared if it gets close to us
	bool m_lookAtSpotAttack;										///< if true, the look at spot should be attacked
	const char *m_lookAtDesc;										///< for debugging
	void UpdateLookAt( void );
	void UpdatePeripheralVision();									///< update enounter spot timestamps, etc
	float m_peripheralTimestamp;

	enum { MAX_APPROACH_POINTS = 16 };
	struct ApproachPoint
	{
		Vector m_pos;
		CNavArea *m_area;
	};

	ApproachPoint m_approachPoint[ MAX_APPROACH_POINTS ];
	unsigned char m_approachPointCount;
	Vector m_approachPointViewPosition;								///< the position used when computing current approachPoint set

	CBaseEntity * FindEntitiesOnPath( float distance, CPushAwayEnumerator *enumerator, bool checkStuck );

	IntervalTimer m_viewSteadyTimer;								///< how long has our view been "steady" (ie: not moving)

	bool BendLineOfSight( const Vector &eye, const Vector &target, Vector *bend, float angleLimit = 135.0f ) const;		///< "bend" our line of sight until we can see the target point. Return bend point, false if cant bend.
	bool FindApproachPointNearestPath( Vector *pos );				///< find the approach point that is nearest to our current path, ahead of us
	bool FindGrenadeTossPathTarget( Vector *pos );					///< find spot to throw grenade ahead of us and "around the corner" along our path
	enum GrenadeTossState
	{
		NOT_THROWING,				///< not yet throwing
		START_THROW,				///< lining up throw
		THROW_LINED_UP,				///< pause for a moment when on-line
		FINISH_THROW,				///< throwing
	};
	GrenadeTossState m_grenadeTossState;
	CountdownTimer m_tossGrenadeTimer;								///< timeout timer for grenade tossing
	const CNavArea *m_initialEncounterArea;							///< area where we think we will initially encounter the enemy
	void LookForGrenadeTargets( void );								///< look for grenade throw targets and throw our grenade at them
	void UpdateGrenadeThrow( void );								///< process grenade throwing
	CountdownTimer m_isAvoidingGrenade;								///< if nonzero we are in the act of avoiding a grenade


	SpotEncounter *m_spotEncounter;									///< the spots we will encounter as we move thru our current area
	float m_spotCheckTimestamp;										///< when to check next encounter spot

	/// @todo Add timestamp for each possible client to hiding spots
	enum { MAX_CHECKED_SPOTS = 64 };
	struct HidingSpotCheckInfo
	{
		HidingSpot *spot;
		float timestamp;
	}
	m_checkedHidingSpot[ MAX_CHECKED_SPOTS ];
	int m_checkedHidingSpotCount;

	//- view angle mechanism -----------------------------------------------------------------------------------------
	float m_lookPitch;												///< our desired look pitch angle
	float m_lookPitchVel;
	float m_lookYaw;												///< our desired look yaw angle
	float m_lookYawVel;

	//- aim angle mechanism -----------------------------------------------------------------------------------------
	Vector m_aimOffset;												///< current error added to victim's position to get actual aim spot
	Vector m_aimOffsetGoal;											///< desired aim offset
	float m_aimOffsetTimestamp;										///< time of next offset adjustment
	float m_aimSpreadTimestamp;										///< time used to determine max spread as it begins to tighten up
	void SetAimOffset( float accuracy );							///< set the current aim offset
	void UpdateAimOffset( void );									///< wiggle aim error based on m_accuracy
	Vector m_aimSpot;												///< the spot we are currently aiming to fire at

	struct PartInfo
	{
		Vector m_headPos;											///< current head position
		Vector m_gutPos;											///< current gut position
		Vector m_feetPos;											///< current feet position
		Vector m_leftSidePos;										///< current left side position
		Vector m_rightSidePos;										///< current right side position
		int m_validFrame;											///< frame of last computation (for lazy evaluation)
	};
	static PartInfo m_partInfo[ MAX_PLAYERS ];						///< part positions for each player
	void ComputePartPositions( CCSPlayer *player );					///< compute part positions from bone location

	//- attack state data --------------------------------------------------------------------------------------------
	DispositionType m_disposition;									///< how we will react to enemies
	CountdownTimer m_ignoreEnemiesTimer;							///< how long will we ignore enemies
	mutable CHandle< CCSPlayer > m_enemy;							///< our current enemy
	bool m_isEnemyVisible;											///< result of last visibility test on enemy
	unsigned char m_visibleEnemyParts;								///< which parts of the visible enemy do we see
	Vector m_lastEnemyPosition;										///< last place we saw the enemy
	float m_lastSawEnemyTimestamp;
	float m_firstSawEnemyTimestamp;
	float m_currentEnemyAcquireTimestamp;
	float m_enemyDeathTimestamp;									///< if m_enemy is dead, this is when he died
	float m_friendDeathTimestamp;									///< time since we saw a friend die
	bool m_isLastEnemyDead;											///< true if we killed or saw our last enemy die
	int m_nearbyEnemyCount;											///< max number of enemies we've seen recently
	unsigned int m_enemyPlace;										///< the location where we saw most of our enemies

	struct WatchInfo
	{
		float timestamp;											///< time we last saw this player, zero if never seen
		bool isEnemy;
	}
	m_watchInfo[ MAX_PLAYERS ];
	mutable CHandle< CCSPlayer > m_bomber;							///< points to bomber if we can see him

	int m_nearbyFriendCount;										///< number of nearby teammates
	mutable CHandle< CCSPlayer > m_closestVisibleFriend;			///< the closest friend we can see
	mutable CHandle< CCSPlayer > m_closestVisibleHumanFriend;		///< the closest human friend we can see

	IntervalTimer m_attentionInterval;								///< time between attention checks

	mutable CHandle< CCSPlayer > m_attacker;						///< last enemy that hurt us (may not be same as m_enemy)
	float m_attackedTimestamp;										///< when we were hurt by the m_attacker

	int m_lastVictimID;												///< the entindex of the last victim we killed, or zero
	bool m_isAimingAtEnemy;											///< if true, we are trying to aim at our enemy
	bool m_isRapidFiring;											///< if true, RunUpkeep() will toggle our primary attack as fast as it can
	IntervalTimer m_equipTimer;										///< how long have we had our current weapon equipped
	CountdownTimer m_zoomTimer;										///< for delaying firing immediately after zoom
	bool DoEquip( CWeaponCSBase *gun );								///< equip the given item

	void ReloadCheck( void );										///< reload our weapon if we must
	void SilencerCheck( void );										///< use silencer

	float m_fireWeaponTimestamp;

	bool m_isEnemySniperVisible;									///< do we see an enemy sniper right now
	CountdownTimer m_sawEnemySniperTimer;							///< tracking time since saw enemy sniper
	
	//- reaction time system -----------------------------------------------------------------------------------------
	enum { MAX_ENEMY_QUEUE = 20 };
	struct ReactionState
	{
		// NOTE: player position & orientation is not currently stored separately
		CHandle<CCSPlayer> player;
		bool isReloading;
		bool isProtectedByShield;
	}
	m_enemyQueue[ MAX_ENEMY_QUEUE ];								///< round-robin queue for simulating reaction times
	byte m_enemyQueueIndex;
	byte m_enemyQueueCount;
	byte m_enemyQueueAttendIndex;									///< index of the timeframe we are "conscious" of

	CCSPlayer *FindMostDangerousThreat( void );						///< return most dangerous threat in my field of view (feeds into reaction time queue)


	//- stuck detection ---------------------------------------------------------------------------------------------
	bool m_isStuck;
	float m_stuckTimestamp;											///< time when we got stuck
	Vector m_stuckSpot;												///< the location where we became stuck
	NavRelativeDirType m_wiggleDirection;
	CountdownTimer m_wiggleTimer;
	CountdownTimer m_stuckJumpTimer;								///< time for next jump when stuck

	enum { MAX_VEL_SAMPLES = 10 };	
	float m_avgVel[ MAX_VEL_SAMPLES ];
	int m_avgVelIndex;
	int m_avgVelCount;
	Vector m_lastOrigin;

	//- radio --------------------------------------------------------------------------------------------------------
	RadioType m_lastRadioCommand;									///< last radio command we recieved
	float m_lastRadioRecievedTimestamp;								///< time we recieved a radio message
	float m_lastRadioSentTimestamp;									///< time when we send a radio message
	CHandle< CCSPlayer > m_radioSubject;							///< who issued the radio message
	Vector m_radioPosition;											///< position referred to in radio message
	void RespondToRadioCommands( void );
	bool IsRadioCommand( RadioType event ) const;					///< returns true if the radio message is an order to do something

	/// new-style "voice" chatter gets voice feedback
	float m_voiceEndTimestamp;

	BotChatterInterface m_chatter;									///< chatter mechanism
};


//
// Inlines
//

inline float CCSBot::GetFeetZ( void ) const
{
	return GetAbsOrigin().z;
}

inline const Vector *CCSBot::GetNoisePosition( void ) const
{
	if (m_noiseTimestamp > 0.0f)
		return &m_noisePosition;

	return NULL;
}

inline bool CCSBot::IsAwareOfEnemyDeath( void ) const
{
	if (GetEnemyDeathTimestamp() == 0.0f)
		return false;

	if (m_enemy == NULL)
		return true;

	if (!m_enemy->IsAlive() && gpGlobals->curtime - GetEnemyDeathTimestamp() > (1.0f - 0.8f * GetProfile()->GetSkill()))
		return true;

	return false;
}

inline void CCSBot::Panic( void )
{
	// we are stunned for a moment
	Surprise( RandomFloat( 0.2f, 0.3f ) );

	const float panicTime = 3.0f;
	m_panicTimer.Start( panicTime );

	const float panicRetreatRange = 300.0f;
	TryToRetreat( panicRetreatRange, 0.0f );

	PrintIfWatched( "*** PANIC ***\n" );
}

inline bool CCSBot::IsPanicking( void ) const
{
	return !m_panicTimer.IsElapsed();
}

inline void CCSBot::StopPanicking( void )
{
	m_panicTimer.Invalidate();
}

inline bool CCSBot::IsNotMoving( float minDuration ) const
{
	return (m_stillTimer.HasStarted() && m_stillTimer.GetElapsedTime() >= minDuration);
}

inline CWeaponCSBase *CCSBot::GetActiveCSWeapon( void ) const
{
	return reinterpret_cast<CWeaponCSBase *>( GetActiveWeapon() );
}


inline float CCSBot::GetCombatRange( void ) const
{ 
	return m_combatRange; 
}

inline void CCSBot::SetRogue( bool rogue )
{ 
	m_isRogue = rogue;
}

inline void CCSBot::Hurry( float duration )
{ 
	m_hurryTimer.Start( duration ); 
}

inline float CCSBot::GetSafeTime( void ) const
{
	return m_safeTime;
}

inline bool CCSBot::IsUnhealthy( void ) const
{
	return (GetHealth() <= 40);
}

inline bool CCSBot::IsAlert( void ) const
{
	return !m_alertTimer.IsElapsed();
}

inline void CCSBot::BecomeAlert( void )
{
	const float alertCooldownTime = 10.0f;
	m_alertTimer.Start( alertCooldownTime );
}

inline bool CCSBot::IsSneaking( void ) const
{
	return !m_sneakTimer.IsElapsed();
}

inline void CCSBot::Sneak( float duration )
{
	m_sneakTimer.Start( duration );
}

inline bool CCSBot::IsFollowing( void ) const
{ 
	return m_isFollowing;
}

inline CCSPlayer *CCSBot::GetFollowLeader( void ) const
{ 
	return m_leader;
}

inline float CCSBot::GetFollowDuration( void ) const
{
	return gpGlobals->curtime - m_followTimestamp;
}

inline bool CCSBot::CanAutoFollow( void ) const
{ 
	return (gpGlobals->curtime > m_allowAutoFollowTime);
}

inline void CCSBot::AimAtEnemy( void )
{ 
	m_isAimingAtEnemy = true;
}

inline void CCSBot::StopAiming( void )
{ 
	m_isAimingAtEnemy = false;
}

inline bool CCSBot::IsAimingAtEnemy( void ) const
{
	return m_isAimingAtEnemy;
}

inline float CCSBot::GetStateTimestamp( void ) const
{
	return m_stateTimestamp;
}

inline CSGameState *CCSBot::GetGameState( void )
{
	return &m_gameState;
}

inline const CSGameState *CCSBot::GetGameState( void ) const
{
	return &m_gameState;
}

inline bool CCSBot::IsAtBombsite( void )
{
	return m_bInBombZone;
}

inline void CCSBot::SetTask( TaskType task, CBaseEntity *entity )
{
	m_task = task;
	m_taskEntity = entity;
}

inline CCSBot::TaskType CCSBot::GetTask( void ) const
{
	return m_task;
}

inline CBaseEntity *CCSBot::GetTaskEntity( void )
{
	return static_cast<CBaseEntity *>( m_taskEntity );
}

inline CCSBot::MoraleType CCSBot::GetMorale( void ) const
{
	return m_morale;
}

inline void CCSBot::Surprise( float duration )
{
	m_surpriseTimer.Start( duration );
}

inline bool CCSBot::IsSurprised( void ) const
{
	return !m_surpriseTimer.IsElapsed();
}

inline CNavArea *CCSBot::GetNoiseArea( void ) const
{
	return m_noiseArea;
}

inline void CCSBot::ForgetNoise( void )
{
	m_noiseTimestamp = 0.0f;
}

inline float CCSBot::GetNoiseRange( void ) const
{
	if (IsNoiseHeard())
		return m_noiseTravelDistance;

	return 999999999.9f;
}

inline PriorityType CCSBot::GetNoisePriority( void ) const
{ 
	return m_noisePriority;
}

inline BotChatterInterface *CCSBot::GetChatter( void )
{
	return &m_chatter;
}

inline CCSPlayer *CCSBot::GetBotEnemy( void ) const
{
	return m_enemy;
}

inline int CCSBot::GetNearbyEnemyCount( void ) const
{ 
	return MIN( GetEnemiesRemaining(), m_nearbyEnemyCount );
}

inline unsigned int CCSBot::GetEnemyPlace( void ) const
{
	return m_enemyPlace;
}

inline bool CCSBot::CanSeeBomber( void ) const
{
	return (m_bomber == NULL) ? false : true;
}

inline CCSPlayer *CCSBot::GetBomber( void ) const
{
	return m_bomber;
}

inline int CCSBot::GetNearbyFriendCount( void ) const
{
	return MIN( GetFriendsRemaining(), m_nearbyFriendCount );
}

inline CCSPlayer *CCSBot::GetClosestVisibleFriend( void ) const
{
	return m_closestVisibleFriend;
}

inline CCSPlayer *CCSBot::GetClosestVisibleHumanFriend( void ) const
{
	return m_closestVisibleHumanFriend;
}

inline float CCSBot::GetTimeSinceAttacked( void ) const
{
	return gpGlobals->curtime - m_attackedTimestamp;
}

inline float CCSBot::GetFirstSawEnemyTimestamp( void ) const
{
	return m_firstSawEnemyTimestamp;
}

inline float CCSBot::GetLastSawEnemyTimestamp( void ) const
{
	return m_lastSawEnemyTimestamp;
}

inline float CCSBot::GetTimeSinceLastSawEnemy( void ) const
{
	return gpGlobals->curtime - m_lastSawEnemyTimestamp;
}

inline float CCSBot::GetTimeSinceAcquiredCurrentEnemy( void ) const
{
	return gpGlobals->curtime - m_currentEnemyAcquireTimestamp;
}

inline const Vector &CCSBot::GetLastKnownEnemyPosition( void ) const
{
	return m_lastEnemyPosition;
}

inline bool CCSBot::IsEnemyVisible( void ) const			
{
	return m_isEnemyVisible;
}

inline float CCSBot::GetEnemyDeathTimestamp( void ) const	
{
	return m_enemyDeathTimestamp;
}

inline int CCSBot::GetLastVictimID( void ) const
{
	return m_lastVictimID;
}

inline bool CCSBot::CanSeeSniper( void ) const
{
	return m_isEnemySniperVisible;
}

inline bool CCSBot::HasSeenSniperRecently( void ) const
{
	return !m_sawEnemySniperTimer.IsElapsed();
}

inline float CCSBot::GetTravelDistanceToPlayer( CCSPlayer *player ) const
{
	if (player == NULL)
		return -1.0f;

	if (!player->IsAlive())
		return -1.0f;

	return m_playerTravelDistance[ player->entindex() % MAX_PLAYERS ];
}

inline bool CCSBot::HasPath( void ) const
{
	return (m_pathLength) ? true : false;
}

inline void CCSBot::DestroyPath( void )		
{
	m_isStopping = false;
	m_pathLength = 0;
	m_pathLadder = NULL;
}

inline CNavArea *CCSBot::GetLastKnownArea( void ) const		
{
	return m_lastKnownArea;
}

inline const Vector &CCSBot::GetPathEndpoint( void ) const		
{
	return m_path[ m_pathLength-1 ].pos;
}

inline const Vector &CCSBot::GetPathPosition( int index ) const
{
	return m_path[ index ].pos;
}

inline bool CCSBot::IsUsingLadder( void ) const	
{
	return (m_pathLadder) ? true : false;
}

inline void CCSBot::SetGoalEntity( CBaseEntity *entity )	
{
	m_goalEntity = entity;
}

inline CBaseEntity *CCSBot::GetGoalEntity( void )
{
	return m_goalEntity;
}

inline void CCSBot::ForceRun( float duration )
{
	Run();
	m_mustRunTimer.Start( duration );
}

inline void CCSBot::Wait( float duration )			
{
	m_waitTimer.Start( duration );
}

inline bool CCSBot::IsWaiting( void ) const		
{
	return !m_waitTimer.IsElapsed();
}

inline void CCSBot::StopWaiting( void )			
{
	m_waitTimer.Invalidate();
}

inline bool CCSBot::HasVisitedEnemySpawn( void ) const		
{
	return m_hasVisitedEnemySpawn;
}

inline const Vector &CCSBot::EyePositionConst( void ) const		
{
	return m_eyePosition;
}
	
inline void CCSBot::SetLookAngles( float yaw, float pitch )
{
	m_lookYaw = yaw;
	m_lookPitch = pitch;
}

inline void CCSBot::SetForwardAngle( float angle ) 
{
	m_forwardAngle = angle;
}

inline void CCSBot::SetLookAheadAngle( float angle ) 
{
	m_lookAheadAngle = angle;
}

inline void CCSBot::ClearLookAt( void )
{ 
	//PrintIfWatched( "ClearLookAt()\n" );
	m_lookAtSpotState = NOT_LOOKING_AT_SPOT; 
	m_lookAtDesc = NULL; 
}

inline bool CCSBot::IsLookingAtSpot( PriorityType pri ) const
{ 
	if (m_lookAtSpotState != NOT_LOOKING_AT_SPOT && m_lookAtSpotPriority >= pri)
		return true;

	return false;
}

inline bool CCSBot::IsViewMoving( float angleVelThreshold ) const
{
	if (m_lookYawVel < angleVelThreshold && m_lookYawVel > -angleVelThreshold &&
		m_lookPitchVel < angleVelThreshold && m_lookPitchVel > -angleVelThreshold)
	{
		return false;
	}

	return true;
}

inline bool CCSBot::HasViewBeenSteady( float duration ) const
{
	return (m_viewSteadyTimer.GetElapsedTime() > duration);
}

inline bool CCSBot::HasLookAtTarget( void ) const
{
	return (m_lookAtSpotState != NOT_LOOKING_AT_SPOT);
}

inline bool CCSBot::IsEnemyPartVisible( VisiblePartType part ) const
{ 
	VPROF_BUDGET( "CCSBot::IsEnemyPartVisible", VPROF_BUDGETGROUP_NPCS );

	if (!IsEnemyVisible())
		return false;

	return (m_visibleEnemyParts & part) ? true : false;
}

inline bool CCSBot::IsSignificantlyCloser( const CCSPlayer *testPlayer, const CCSPlayer *referencePlayer ) const
{
	if ( !referencePlayer )
		return true;

	if ( !testPlayer )
		return false;

	float testDist = ( GetAbsOrigin() - testPlayer->GetAbsOrigin() ).Length();
	float referenceDist = ( GetAbsOrigin() - referencePlayer->GetAbsOrigin() ).Length();

	const float significantRangeFraction = 0.7f;
	if ( testDist < referenceDist * significantRangeFraction )
		return true;

	return false;
}

inline void CCSBot::ClearApproachPoints( void )	
{
	m_approachPointCount = 0;
}

inline const CNavArea *CCSBot::GetInitialEncounterArea( void ) const
{
	return m_initialEncounterArea;
}

inline void CCSBot::SetInitialEncounterArea( const CNavArea *area )		
{
	m_initialEncounterArea = area;
}

inline bool CCSBot::IsThrowingGrenade( void ) const		
{
	return m_grenadeTossState != NOT_THROWING;
}

inline bool CCSBot::IsAvoidingGrenade( void ) const
{
	return !m_isAvoidingGrenade.IsElapsed();
}

inline void CCSBot::PrimaryAttack( void )
{
	if ( IsUsingPistol() && !CanActiveWeaponFire() )
		return;

	BaseClass::PrimaryAttack();
}

inline CCSBot::ZoomType CCSBot::GetZoomLevel( void )
{
	if (GetFOV() > 60.0f)
		return NO_ZOOM;
	if (GetFOV() > 25.0f)
		return LOW_ZOOM;
	return HIGH_ZOOM;
}

inline bool CCSBot::IsWaitingForZoom( void ) const		
{
	return !m_zoomTimer.IsElapsed();
}

inline int CCSBot::GetHostageEscortCount( void ) const		
{
	return m_hostageEscortCount;
}

inline void CCSBot::IncreaseHostageEscortCount( void )		
{
	++m_hostageEscortCount;
}

inline void CCSBot::ResetWaitForHostagePatience( void )
{
	m_isWaitingForHostage = false;
	m_inhibitWaitingForHostageTimer.Invalidate();
}


inline bool CCSBot::IsUsingVoice() const
{
	return m_voiceEndTimestamp > gpGlobals->curtime; 
}

inline bool CCSBot::IsOpeningDoor( void ) const
{
	return m_isOpeningDoor;
}


//--------------------------------------------------------------------------------------------------------------
/**
 * Return true if the given weapon is a sniper rifle
 */
inline bool IsSniperRifle( CWeaponCSBase *weapon )
{
	if (weapon == NULL)
		return false;

	return weapon->IsKindOf(WEAPONTYPE_SNIPER_RIFLE);
}


//--------------------------------------------------------------------------------------------------------------
/**
 * Functor used with NavAreaBuildPath()
 */
class PathCost
{
public:
	PathCost( CCSBot *bot, RouteType route = SAFEST_ROUTE )
	{
		m_bot = bot;
		m_route = route;
	}

	// HPE_TODO[pmf]: check that these new parameters are okay to be ignored
	float operator() ( CNavArea *area, CNavArea *fromArea, const CNavLadder *ladder, const CFuncElevator *elevator, float length )
	{
		float baseDangerFactor = 100.0f;	// 100

		// respond to the danger modulated by our aggression (even super-aggressives pay SOME attention to danger)
		float dangerFactor = (1.0f - (0.95f * m_bot->GetProfile()->GetAggression())) * baseDangerFactor;

		if (fromArea == NULL)
		{
			if (m_route == FASTEST_ROUTE)
				return 0.0f;

			// first area in path, cost is just danger
			return dangerFactor * area->GetDanger( m_bot->GetTeamNumber() );
		}
		else if ((fromArea->GetAttributes() & NAV_MESH_JUMP) && (area->GetAttributes() & NAV_MESH_JUMP))
		{
			// cannot actually walk in jump areas - disallow moving from jump area to jump area
			return -1.0f;
		}
		if ( area->GetAttributes() & NAV_MESH_NO_HOSTAGES && m_bot->GetHostageEscortCount() )
		{
			// if we're leading hostages, don't try to go where they can't
			return -1.0f;
		}
		else
		{
			// compute distance from previous area to this area
			float dist;
			if (ladder)
			{
				// ladders are slow to use
				const float ladderPenalty = 1.0f; // 3.0f;
				dist = ladderPenalty * ladder->m_length;

				// if we are currently escorting hostages, avoid ladders (hostages are confused by them)
				//if (m_bot->GetHostageEscortCount())
				//	dist *= 100.0f;
			}
			else
			{
				dist = (area->GetCenter() - fromArea->GetCenter()).Length();
			}

			// compute distance travelled along path so far
			float cost = dist + fromArea->GetCostSoFar();

			// zombies ignore all path penalties
			if (cv_bot_zombie.GetBool())
				return cost;

			// add cost of "jump down" pain unless we're jumping into water
			if (!area->IsUnderwater() && area->IsConnected( fromArea, NUM_DIRECTIONS ) == false)
			{
				// this is a "jump down" (one way drop) transition - estimate damage we will take to traverse it
				float fallDistance = -fromArea->ComputeGroundHeightChange( area );

				// if it's a drop-down ladder, estimate height from the bottom of the ladder to the lower area
				if ( ladder && ladder->m_bottom.z < fromArea->GetCenter().z && ladder->m_bottom.z > area->GetCenter().z )
				{
					fallDistance = ladder->m_bottom.z - area->GetCenter().z;
				}

				float fallDamage = m_bot->GetApproximateFallDamage( fallDistance );

				if (fallDamage > 0.0f)
				{
					// if the fall would kill us, don't use it
					const float deathFallMargin = 10.0f;
					if (fallDamage + deathFallMargin >= m_bot->GetHealth())
						return -1.0f;

					// if we need to get there in a hurry, ignore minor pain
					const float painTolerance = 15.0f * m_bot->GetProfile()->GetAggression() + 10.0f;
					if (m_route != FASTEST_ROUTE || fallDamage > painTolerance)
					{
						// cost is proportional to how much it hurts when we fall
						// 10 points - not a big deal, 50 points - ouch!
						cost += 100.0f * fallDamage * fallDamage;
					}
				}
			}

			// if this is a "crouch" or "walk" area, add penalty
			if (area->GetAttributes() & (NAV_MESH_CROUCH | NAV_MESH_WALK))
			{
				// these areas are very slow to move through
				float penalty = (m_route == FASTEST_ROUTE) ? 20.0f : 5.0f;

				// avoid crouch areas if we are rescuing hostages 
				if ((area->GetAttributes() & NAV_MESH_CROUCH) && m_bot->GetHostageEscortCount())
				{
					penalty *= 3.0f;
				}

				cost += penalty * dist;
			}

			// if this is a "jump" area, add penalty
			if (area->GetAttributes() & NAV_MESH_JUMP)
			{
				// jumping can slow you down
				//const float jumpPenalty = (m_route == FASTEST_ROUTE) ? 100.0f : 0.5f;
				const float jumpPenalty = 1.0f;
				cost += jumpPenalty * dist;
			}

			// if this is an area to avoid, add penalty
			if (area->GetAttributes() & NAV_MESH_AVOID)
			{
				const float avoidPenalty = 20.0f;
				cost += avoidPenalty * dist;
			}

			if (m_route == SAFEST_ROUTE)
			{
				// add in the danger of this path - danger is per unit length travelled
				cost += dist * dangerFactor * area->GetDanger( m_bot->GetTeamNumber() );
			}

			if (!m_bot->IsAttacking())
			{
				// add in cost of teammates in the way

				// approximate density of teammates based on area
				float size = (area->GetSizeX() + area->GetSizeY())/2.0f;

				// degenerate check
				if (size >= 1.0f)
				{
					// cost is proportional to the density of teammates in this area
					const float costPerFriendPerUnit = 50000.0f;
					cost += costPerFriendPerUnit * (float)area->GetPlayerCount( m_bot->GetTeamNumber() ) / size;
				}
			}

			return cost;
		}
	}

private:
	CCSBot *m_bot;
	RouteType m_route;
};


//--------------------------------------------------------------------------------------------------------------
//
// Prototypes
//
extern int GetBotFollowCount( CCSPlayer *leader );
extern const Vector *FindNearbyRetreatSpot( CCSBot *me, float maxRange = 250.0f );
extern const HidingSpot *FindInitialEncounterSpot( CBaseEntity *me, const Vector &searchOrigin, float enemyArriveTime, float maxRange, bool isSniper );


#endif	// _CS_BOT_H_