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
|
//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose:
//
//=============================================================================//
#ifndef AI_BEHAVIOR_ACTBUSY_H
#define AI_BEHAVIOR_ACTBUSY_H
#ifdef _WIN32
#pragma once
#endif
#include "ai_behavior.h"
#include "ai_goalentity.h"
//-----------------------------------------------------------------------------
enum
{
ACTBUSY_TYPE_DEFAULT = 0,
ACTBUSY_TYPE_COMBAT,
};
enum busyinterrupt_t
{
BA_INT_NONE, // Nothing breaks us out of this
BA_INT_DANGER, // Only danger signals interrupts this busy anim. The player will be ignored.
BA_INT_PLAYER, // The Player's presence interrupts this busy anim
BA_INT_AMBUSH, // We're waiting to ambush enemies. Don't break on danger sounds in front of us.
BA_INT_COMBAT, // Only break out if we're shot at.
BA_INT_ZOMBIESLUMP, // Zombies who are slumped on the ground.
BA_INT_SIEGE_DEFENSE,
};
enum busyanimparts_t
{
BA_BUSY,
BA_ENTRY,
BA_EXIT,
BA_MAX_ANIMS,
};
struct busyanim_t
{
string_t iszName;
Activity iActivities[BA_MAX_ANIMS];
string_t iszSequences[BA_MAX_ANIMS];
string_t iszSounds[BA_MAX_ANIMS];
float flMinTime; // Min time spent in this busy animation
float flMaxTime; // Max time spent in this busy animation. 0 means continue until interrupted.
busyinterrupt_t iBusyInterruptType;
bool bUseAutomovement;
};
struct busysafezone_t
{
Vector vecMins;
Vector vecMaxs;
};
#define NO_MAX_TIME -1
class CAI_ActBusyGoal;
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
class CAI_ActBusyBehavior : public CAI_SimpleBehavior
{
DECLARE_CLASS( CAI_ActBusyBehavior, CAI_SimpleBehavior );
public:
DECLARE_DATADESC();
CAI_ActBusyBehavior();
enum
{
// Schedules
SCHED_ACTBUSY_START_BUSYING = BaseClass::NEXT_SCHEDULE,
SCHED_ACTBUSY_BUSY,
SCHED_ACTBUSY_STOP_BUSYING,
SCHED_ACTBUSY_LEAVE,
SCHED_ACTBUSY_TELEPORT_TO_BUSY,
NEXT_SCHEDULE,
// Tasks
TASK_ACTBUSY_PLAY_BUSY_ANIM = BaseClass::NEXT_TASK,
TASK_ACTBUSY_PLAY_ENTRY,
TASK_ACTBUSY_PLAY_EXIT,
TASK_ACTBUSY_TELEPORT_TO_BUSY,
TASK_ACTBUSY_WALK_PATH_TO_BUSY,
TASK_ACTBUSY_GET_PATH_TO_ACTBUSY,
TASK_ACTBUSY_VERIFY_EXIT,
NEXT_TASK,
// Conditions
COND_ACTBUSY_LOST_SEE_ENTITY = BaseClass::NEXT_CONDITION,
COND_ACTBUSY_AWARE_OF_ENEMY_IN_SAFE_ZONE,
COND_ACTBUSY_ENEMY_TOO_CLOSE,
NEXT_CONDITION,
};
virtual const char *GetName() { return "ActBusy"; }
void Enable( CAI_ActBusyGoal *pGoal, float flRange, bool bVisibleOnly );
void OnRestore();
void SetBusySearchRange( float flRange );
void Disable( void );
void ForceActBusy( CAI_ActBusyGoal *pGoal, CAI_Hint *pHintNode = NULL, float flMaxTime = NO_MAX_TIME, bool bVisibleOnly = false, bool bTeleportToBusy = false, bool bUseNearestBusy = false, CBaseEntity *pSeeEntity = NULL, Activity activity = ACT_INVALID );
void ForceActBusyLeave( bool bVisibleOnly = false );
void StopBusying( void );
bool IsStopBusying();
CAI_Hint *FindActBusyHintNode( void );
CAI_Hint *FindCombatActBusyHintNode( void );
CAI_Hint *FindCombatActBusyTeleportHintNode( void );
bool CanSelectSchedule( void );
bool IsCurScheduleOverridable( void );
bool ShouldIgnoreSound( CSound *pSound );
void OnFriendDamaged( CBaseCombatCharacter *pSquadmate, CBaseEntity *pAttacker );
int OnTakeDamage_Alive( const CTakeDamageInfo &info );
void GatherConditions( void );
void BuildScheduleTestBits( void );
void EndScheduleSelection( void );
Activity NPC_TranslateActivity( Activity nActivity );
void HandleAnimEvent( animevent_t *pEvent );
void CheckAndCleanupOnExit( void );
bool FValidateHintType( CAI_Hint *pHint );
bool ActBusyNodeStillActive( void );
bool IsMovingToBusy( void ) { return m_bMovingToBusy; }
bool IsEnabled( void ) { return m_bEnabled; }
float GetReasonableFacingDist( void ) { return 0; } // Actbusy ignores reasonable facing
bool IsInterruptable( void );
bool ShouldPlayerAvoid( void );
void SetUseRenderBounds( bool bUseBounds ) { m_bUseRenderBoundsForCollision = bUseBounds; }
void ComputeAndSetRenderBounds();
bool CanFlinch( void );
bool CanRunAScriptedNPCInteraction( bool bForced );
void OnScheduleChange();
bool QueryHearSound( CSound *pSound );
void OnSeeEntity( CBaseEntity *pEntity );
bool NeedsToPlayExitAnim() { return m_bNeedsToPlayExitAnim; }
// Returns true if the current NPC is acting busy, or moving to an actbusy
bool IsActive( void );
// Returns true if the current NPC is actually acting busy (i.e. inside an act busy anim)
bool IsInsideActBusy( void ) { return m_bBusy; }
// Combat act busy stuff
bool IsCombatActBusy();
void CollectSafeZoneVolumes( CAI_ActBusyGoal *pActBusyGoal );
bool IsInSafeZone( CBaseEntity *pEntity );
int CountEnemiesInSafeZone();
private:
virtual int SelectSchedule( void );
int SelectScheduleForLeaving( void );
int SelectScheduleWhileNotBusy( int iBase );
int SelectScheduleWhileBusy( void );
virtual void StartTask( const Task_t *pTask );
virtual void RunTask( const Task_t *pTask );
void NotifyBusyEnding( void );
bool HasAnimForActBusy( int iActBusy, busyanimparts_t AnimPart );
bool PlayAnimForActBusy( busyanimparts_t AnimPart );
void PlaySoundForActBusy( busyanimparts_t AnimPart );
private:
bool m_bEnabled;
bool m_bForceActBusy;
Activity m_ForcedActivity;
bool m_bTeleportToBusy;
bool m_bUseNearestBusy;
bool m_bLeaving;
bool m_bVisibleOnly;
bool m_bUseRenderBoundsForCollision;
float m_flForcedMaxTime;
bool m_bBusy;
bool m_bMovingToBusy;
bool m_bNeedsToPlayExitAnim;
float m_flNextBusySearchTime;
float m_flEndBusyAt;
float m_flBusySearchRange;
bool m_bInQueue;
int m_iCurrentBusyAnim;
CHandle<CAI_ActBusyGoal> m_hActBusyGoal;
bool m_bNeedToSetBounds;
EHANDLE m_hSeeEntity;
float m_fTimeLastSawSeeEntity;
bool m_bExitedBusyToDueLostSeeEntity;
bool m_bExitedBusyToDueSeeEnemy;
int m_iNumConsecutivePathFailures; // Count how many times we failed to find a path to a node, so we can consider teleporting.
bool m_bAutoFireWeapon;
float m_flDeferUntil;
int m_iNumEnemiesInSafeZone;
CUtlVector<busysafezone_t>m_SafeZones;
DEFINE_CUSTOM_SCHEDULE_PROVIDER;
};
//-----------------------------------------------------------------------------
// Purpose: A level tool to control the actbusy behavior.
//-----------------------------------------------------------------------------
class CAI_ActBusyGoal : public CAI_GoalEntity
{
DECLARE_CLASS( CAI_ActBusyGoal, CAI_GoalEntity );
public:
CAI_ActBusyGoal()
{
// Support legacy maps, where this value used to be set from a constant (with a value of 1).
// Now designers can specify whatever they want in Hammer. Take care of old maps by setting
// this in the constructor. (sjb)
m_flSeeEntityTimeout = 1;
}
virtual void NPCMovingToBusy( CAI_BaseNPC *pNPC );
virtual void NPCAbortedMoveTo( CAI_BaseNPC *pNPC );
virtual void NPCStartedBusy( CAI_BaseNPC *pNPC );
virtual void NPCStartedLeavingBusy( CAI_BaseNPC *pNPC );
virtual void NPCFinishedBusy( CAI_BaseNPC *pNPC );
virtual void NPCLeft( CAI_BaseNPC *pNPC );
virtual void NPCLostSeeEntity( CAI_BaseNPC *pNPC );
virtual void NPCSeeEnemy( CAI_BaseNPC *pNPC );
int GetType() { return m_iType; }
bool IsCombatActBusyTeleportAllowed() { return m_bAllowCombatActBusyTeleport; }
protected:
CAI_ActBusyBehavior *GetBusyBehaviorForNPC( const char *pszActorName, CBaseEntity *pActivator, CBaseEntity *pCaller, const char *sInputName );
CAI_ActBusyBehavior *GetBusyBehaviorForNPC( CBaseEntity *pEntity, const char *sInputName );
void EnableGoal( CAI_BaseNPC *pAI );
// Inputs
virtual void InputActivate( inputdata_t &inputdata );
virtual void InputDeactivate( inputdata_t &inputdata );
void InputSetBusySearchRange( inputdata_t &inputdata );
void InputForceNPCToActBusy( inputdata_t &inputdata );
void InputForceThisNPCToActBusy( inputdata_t &inputdata );
void InputForceThisNPCToLeave( inputdata_t &inputdata );
DECLARE_DATADESC();
protected:
float m_flBusySearchRange;
bool m_bVisibleOnly;
int m_iType;
bool m_bAllowCombatActBusyTeleport;
public:
// Let the actbusy behavior query these so we don't have to duplicate the data.
string_t m_iszSeeEntityName;
float m_flSeeEntityTimeout;
string_t m_iszSafeZoneVolume;
int m_iSightMethod;
protected:
COutputEHANDLE m_OnNPCStartedBusy;
COutputEHANDLE m_OnNPCFinishedBusy;
COutputEHANDLE m_OnNPCLeft;
COutputEHANDLE m_OnNPCLostSeeEntity;
COutputEHANDLE m_OnNPCSeeEnemy;
};
// Maximum number of nodes allowed in an actbusy queue
#define MAX_QUEUE_NODES 20
//-----------------------------------------------------------------------------
// Purpose: A level tool to control the actbusy behavior to create NPC queues
//-----------------------------------------------------------------------------
class CAI_ActBusyQueueGoal : public CAI_ActBusyGoal
{
DECLARE_CLASS( CAI_ActBusyQueueGoal, CAI_ActBusyGoal );
public:
virtual void Spawn( void );
virtual void DrawDebugGeometryOverlays( void );
virtual void NPCMovingToBusy( CAI_BaseNPC *pNPC );
virtual void NPCStartedBusy( CAI_BaseNPC *pNPC );
virtual void NPCAbortedMoveTo( CAI_BaseNPC *pNPC );
virtual void NPCFinishedBusy( CAI_BaseNPC *pNPC );
virtual void NPCStartedLeavingBusy( CAI_BaseNPC *pNPC );
virtual void InputActivate( inputdata_t &inputdata );
void InputPlayerStartedBlocking( inputdata_t &inputdata );
void InputPlayerStoppedBlocking( inputdata_t &inputdata );
void InputMoveQueueUp( inputdata_t &inputdata );
void PushNPCBackInQueue( CAI_BaseNPC *pNPC, int iStartingNode );
void RemoveNPCFromQueue( CAI_BaseNPC *pNPC );
void RecalculateQueueCount( void );
void QueueThink( void );
void MoveQueueUp( void );
void MoveQueueUpThink( void );
bool NodeIsOccupied( int i );
CAI_BaseNPC *GetNPCOnNode( int iNode );
CAI_ActBusyBehavior *GetQueueBehaviorForNPC( CAI_BaseNPC *pNPC );
DECLARE_DATADESC();
private:
int m_iCurrentQueueCount;
CHandle<CAI_Hint> m_hNodes[ MAX_QUEUE_NODES ];
bool m_bPlayerBlockedNodes[ MAX_QUEUE_NODES ];
EHANDLE m_hExitNode;
EHANDLE m_hExitingNPC;
bool m_bForceReachFront;
// Read from mapdata
string_t m_iszNodes[ MAX_QUEUE_NODES ];
string_t m_iszExitNode;
// Outputs
COutputInt m_OnQueueMoved;
COutputEHANDLE m_OnNPCLeftQueue;
COutputEHANDLE m_OnNPCStartedLeavingQueue;
};
#endif // AI_BEHAVIOR_ACTBUSY_H
|