1 /*
2 ===========================================================================
3 
4 Doom 3 GPL Source Code
5 Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company.
6 
7 This file is part of the Doom 3 GPL Source Code ("Doom 3 Source Code").
8 
9 Doom 3 Source Code is free software: you can redistribute it and/or modify
10 it under the terms of the GNU General Public License as published by
11 the Free Software Foundation, either version 3 of the License, or
12 (at your option) any later version.
13 
14 Doom 3 Source Code is distributed in the hope that it will be useful,
15 but WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17 GNU General Public License for more details.
18 
19 You should have received a copy of the GNU General Public License
20 along with Doom 3 Source Code.  If not, see <http://www.gnu.org/licenses/>.
21 
22 In addition, the Doom 3 Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 Source Code.  If not, please request a copy in writing from id Software at the address below.
23 
24 If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA.
25 
26 ===========================================================================
27 */
28 
29 #include "sys/platform.h"
30 #include "Moveable.h"
31 #include "Misc.h"
32 
33 #include "gamesys/SysCvar.h"
34 #include "ai/AI.h"
35 
36 /***********************************************************************
37 
38 	AI Events
39 
40 ***********************************************************************/
41 
42 const idEventDef AI_FindEnemy( "findEnemy", "d", 'e' );
43 const idEventDef AI_FindEnemyAI( "findEnemyAI", "d", 'e' );
44 const idEventDef AI_FindEnemyInCombatNodes( "findEnemyInCombatNodes", NULL, 'e' );
45 const idEventDef AI_ClosestReachableEnemyOfEntity( "closestReachableEnemyOfEntity", "E", 'e' );
46 const idEventDef AI_HeardSound( "heardSound", "d", 'e' );
47 const idEventDef AI_SetEnemy( "setEnemy", "E" );
48 const idEventDef AI_ClearEnemy( "clearEnemy" );
49 const idEventDef AI_MuzzleFlash( "muzzleFlash", "s" );
50 const idEventDef AI_CreateMissile( "createMissile", "s", 'e' );
51 const idEventDef AI_AttackMissile( "attackMissile", "s", 'e' );
52 const idEventDef AI_FireMissileAtTarget( "fireMissileAtTarget", "ss", 'e' );
53 const idEventDef AI_LaunchMissile( "launchMissile", "vv", 'e' );
54 #ifdef _D3XP
55 const idEventDef AI_LaunchProjectile( "launchProjectile", "s" );
56 #endif
57 const idEventDef AI_AttackMelee( "attackMelee", "s", 'd' );
58 const idEventDef AI_DirectDamage( "directDamage", "es" );
59 const idEventDef AI_RadiusDamageFromJoint( "radiusDamageFromJoint", "ss" );
60 const idEventDef AI_BeginAttack( "attackBegin", "s" );
61 const idEventDef AI_EndAttack( "attackEnd" );
62 const idEventDef AI_MeleeAttackToJoint( "meleeAttackToJoint", "ss", 'd' );
63 const idEventDef AI_RandomPath( "randomPath", NULL, 'e' );
64 const idEventDef AI_CanBecomeSolid( "canBecomeSolid", NULL, 'f' );
65 const idEventDef AI_BecomeSolid( "becomeSolid" );
66 const idEventDef AI_BecomeRagdoll( "becomeRagdoll", NULL, 'd' );
67 const idEventDef AI_StopRagdoll( "stopRagdoll" );
68 const idEventDef AI_SetHealth( "setHealth", "f" );
69 const idEventDef AI_GetHealth( "getHealth", NULL, 'f' );
70 const idEventDef AI_AllowDamage( "allowDamage" );
71 const idEventDef AI_IgnoreDamage( "ignoreDamage" );
72 const idEventDef AI_GetCurrentYaw( "getCurrentYaw", NULL, 'f' );
73 const idEventDef AI_TurnTo( "turnTo", "f" );
74 const idEventDef AI_TurnToPos( "turnToPos", "v" );
75 const idEventDef AI_TurnToEntity( "turnToEntity", "E" );
76 const idEventDef AI_MoveStatus( "moveStatus", NULL, 'd' );
77 const idEventDef AI_StopMove( "stopMove" );
78 const idEventDef AI_MoveToCover( "moveToCover" );
79 const idEventDef AI_MoveToEnemy( "moveToEnemy" );
80 const idEventDef AI_MoveToEnemyHeight( "moveToEnemyHeight" );
81 const idEventDef AI_MoveOutOfRange( "moveOutOfRange", "ef" );
82 const idEventDef AI_MoveToAttackPosition( "moveToAttackPosition", "es" );
83 const idEventDef AI_Wander( "wander" );
84 const idEventDef AI_MoveToEntity( "moveToEntity", "e" );
85 const idEventDef AI_MoveToPosition( "moveToPosition", "v" );
86 const idEventDef AI_SlideTo( "slideTo", "vf" );
87 const idEventDef AI_FacingIdeal( "facingIdeal", NULL, 'd' );
88 const idEventDef AI_FaceEnemy( "faceEnemy" );
89 const idEventDef AI_FaceEntity( "faceEntity", "E" );
90 const idEventDef AI_GetCombatNode( "getCombatNode", NULL, 'e' );
91 const idEventDef AI_EnemyInCombatCone( "enemyInCombatCone", "Ed", 'd' );
92 const idEventDef AI_WaitMove( "waitMove" );
93 const idEventDef AI_GetJumpVelocity( "getJumpVelocity", "vff", 'v' );
94 const idEventDef AI_EntityInAttackCone( "entityInAttackCone", "E", 'd' );
95 const idEventDef AI_CanSeeEntity( "canSee", "E", 'd' );
96 const idEventDef AI_SetTalkTarget( "setTalkTarget", "E" );
97 const idEventDef AI_GetTalkTarget( "getTalkTarget", NULL, 'e' );
98 const idEventDef AI_SetTalkState( "setTalkState", "d" );
99 const idEventDef AI_EnemyRange( "enemyRange", NULL, 'f' );
100 const idEventDef AI_EnemyRange2D( "enemyRange2D", NULL, 'f' );
101 const idEventDef AI_GetEnemy( "getEnemy", NULL, 'e' );
102 const idEventDef AI_GetEnemyPos( "getEnemyPos", NULL, 'v' );
103 const idEventDef AI_GetEnemyEyePos( "getEnemyEyePos", NULL, 'v' );
104 const idEventDef AI_PredictEnemyPos( "predictEnemyPos", "f", 'v' );
105 const idEventDef AI_CanHitEnemy( "canHitEnemy", NULL, 'd' );
106 const idEventDef AI_CanHitEnemyFromAnim( "canHitEnemyFromAnim", "s", 'd' );
107 const idEventDef AI_CanHitEnemyFromJoint( "canHitEnemyFromJoint", "s", 'd' );
108 const idEventDef AI_EnemyPositionValid( "enemyPositionValid", NULL, 'd' );
109 const idEventDef AI_ChargeAttack( "chargeAttack", "s" );
110 const idEventDef AI_TestChargeAttack( "testChargeAttack", NULL, 'f' );
111 const idEventDef AI_TestMoveToPosition( "testMoveToPosition", "v", 'd' );
112 const idEventDef AI_TestAnimMoveTowardEnemy( "testAnimMoveTowardEnemy", "s", 'd' );
113 const idEventDef AI_TestAnimMove( "testAnimMove", "s", 'd' );
114 const idEventDef AI_TestMeleeAttack( "testMeleeAttack", NULL, 'd' );
115 const idEventDef AI_TestAnimAttack( "testAnimAttack", "s", 'd' );
116 const idEventDef AI_Shrivel( "shrivel", "f" );
117 const idEventDef AI_Burn( "burn" );
118 const idEventDef AI_ClearBurn( "clearBurn" );
119 const idEventDef AI_PreBurn( "preBurn" );
120 const idEventDef AI_SetSmokeVisibility( "setSmokeVisibility", "dd" );
121 const idEventDef AI_NumSmokeEmitters( "numSmokeEmitters", NULL, 'd' );
122 const idEventDef AI_WaitAction( "waitAction", "s" );
123 const idEventDef AI_StopThinking( "stopThinking" );
124 const idEventDef AI_GetTurnDelta( "getTurnDelta", NULL, 'f' );
125 const idEventDef AI_GetMoveType( "getMoveType", NULL, 'd' );
126 const idEventDef AI_SetMoveType( "setMoveType", "d" );
127 const idEventDef AI_SaveMove( "saveMove" );
128 const idEventDef AI_RestoreMove( "restoreMove" );
129 const idEventDef AI_AllowMovement( "allowMovement", "f" );
130 const idEventDef AI_JumpFrame( "<jumpframe>" );
131 const idEventDef AI_EnableClip( "enableClip" );
132 const idEventDef AI_DisableClip( "disableClip" );
133 const idEventDef AI_EnableGravity( "enableGravity" );
134 const idEventDef AI_DisableGravity( "disableGravity" );
135 const idEventDef AI_EnableAFPush( "enableAFPush" );
136 const idEventDef AI_DisableAFPush( "disableAFPush" );
137 const idEventDef AI_SetFlySpeed( "setFlySpeed", "f" );
138 const idEventDef AI_SetFlyOffset( "setFlyOffset", "d" );
139 const idEventDef AI_ClearFlyOffset( "clearFlyOffset" );
140 const idEventDef AI_GetClosestHiddenTarget( "getClosestHiddenTarget", "s", 'e' );
141 const idEventDef AI_GetRandomTarget( "getRandomTarget", "s", 'e' );
142 const idEventDef AI_TravelDistanceToPoint( "travelDistanceToPoint", "v", 'f' );
143 const idEventDef AI_TravelDistanceToEntity( "travelDistanceToEntity", "e", 'f' );
144 const idEventDef AI_TravelDistanceBetweenPoints( "travelDistanceBetweenPoints", "vv", 'f' );
145 const idEventDef AI_TravelDistanceBetweenEntities( "travelDistanceBetweenEntities", "ee", 'f' );
146 const idEventDef AI_LookAtEntity( "lookAt", "Ef" );
147 const idEventDef AI_LookAtEnemy( "lookAtEnemy", "f" );
148 const idEventDef AI_SetJointMod( "setBoneMod", "d" );
149 const idEventDef AI_ThrowMoveable( "throwMoveable" );
150 const idEventDef AI_ThrowAF( "throwAF" );
151 const idEventDef AI_RealKill( "<kill>" );
152 const idEventDef AI_Kill( "kill" );
153 const idEventDef AI_WakeOnFlashlight( "wakeOnFlashlight", "d" );
154 const idEventDef AI_LocateEnemy( "locateEnemy" );
155 const idEventDef AI_KickObstacles( "kickObstacles", "Ef" );
156 const idEventDef AI_GetObstacle( "getObstacle", NULL, 'e' );
157 const idEventDef AI_PushPointIntoAAS( "pushPointIntoAAS", "v", 'v' );
158 const idEventDef AI_GetTurnRate( "getTurnRate", NULL, 'f' );
159 const idEventDef AI_SetTurnRate( "setTurnRate", "f" );
160 const idEventDef AI_AnimTurn( "animTurn", "f" );
161 const idEventDef AI_AllowHiddenMovement( "allowHiddenMovement", "d" );
162 const idEventDef AI_TriggerParticles( "triggerParticles", "s" );
163 const idEventDef AI_FindActorsInBounds( "findActorsInBounds", "vv", 'e' );
164 const idEventDef AI_CanReachPosition( "canReachPosition", "v", 'd' );
165 const idEventDef AI_CanReachEntity( "canReachEntity", "E", 'd' );
166 const idEventDef AI_CanReachEnemy( "canReachEnemy", NULL, 'd' );
167 const idEventDef AI_GetReachableEntityPosition( "getReachableEntityPosition", "e", 'v' );
168 #ifdef _D3XP
169 const idEventDef AI_MoveToPositionDirect( "moveToPositionDirect", "v" );
170 const idEventDef AI_AvoidObstacles( "avoidObstacles", "d" );
171 const idEventDef AI_TriggerFX( "triggerFX", "ss" );
172 const idEventDef AI_StartEmitter( "startEmitter", "sss", 'e' );
173 const idEventDef AI_GetEmitter( "getEmitter", "s", 'e' );
174 const idEventDef AI_StopEmitter( "stopEmitter", "s" );
175 
176 
177 #endif
178 
CLASS_DECLARATION(idActor,idAI)179 CLASS_DECLARATION( idActor, idAI )
180 	EVENT( EV_Activate,							idAI::Event_Activate )
181 	EVENT( EV_Touch,							idAI::Event_Touch )
182 	EVENT( AI_FindEnemy,						idAI::Event_FindEnemy )
183 	EVENT( AI_FindEnemyAI,						idAI::Event_FindEnemyAI )
184 	EVENT( AI_FindEnemyInCombatNodes,			idAI::Event_FindEnemyInCombatNodes )
185 	EVENT( AI_ClosestReachableEnemyOfEntity,	idAI::Event_ClosestReachableEnemyOfEntity )
186 	EVENT( AI_HeardSound,						idAI::Event_HeardSound )
187 	EVENT( AI_SetEnemy,							idAI::Event_SetEnemy )
188 	EVENT( AI_ClearEnemy,						idAI::Event_ClearEnemy )
189 	EVENT( AI_MuzzleFlash,						idAI::Event_MuzzleFlash )
190 	EVENT( AI_CreateMissile,					idAI::Event_CreateMissile )
191 	EVENT( AI_AttackMissile,					idAI::Event_AttackMissile )
192 	EVENT( AI_FireMissileAtTarget,				idAI::Event_FireMissileAtTarget )
193 	EVENT( AI_LaunchMissile,					idAI::Event_LaunchMissile )
194 #ifdef _D3XP
195 	EVENT( AI_LaunchProjectile,					idAI::Event_LaunchProjectile )
196 #endif
197 	EVENT( AI_AttackMelee,						idAI::Event_AttackMelee )
198 	EVENT( AI_DirectDamage,						idAI::Event_DirectDamage )
199 	EVENT( AI_RadiusDamageFromJoint,			idAI::Event_RadiusDamageFromJoint )
200 	EVENT( AI_BeginAttack,						idAI::Event_BeginAttack )
201 	EVENT( AI_EndAttack,						idAI::Event_EndAttack )
202 	EVENT( AI_MeleeAttackToJoint,				idAI::Event_MeleeAttackToJoint )
203 	EVENT( AI_RandomPath,						idAI::Event_RandomPath )
204 	EVENT( AI_CanBecomeSolid,					idAI::Event_CanBecomeSolid )
205 	EVENT( AI_BecomeSolid,						idAI::Event_BecomeSolid )
206 	EVENT( EV_BecomeNonSolid,					idAI::Event_BecomeNonSolid )
207 	EVENT( AI_BecomeRagdoll,					idAI::Event_BecomeRagdoll )
208 	EVENT( AI_StopRagdoll,						idAI::Event_StopRagdoll )
209 	EVENT( AI_SetHealth,						idAI::Event_SetHealth )
210 	EVENT( AI_GetHealth,						idAI::Event_GetHealth )
211 	EVENT( AI_AllowDamage,						idAI::Event_AllowDamage )
212 	EVENT( AI_IgnoreDamage,						idAI::Event_IgnoreDamage )
213 	EVENT( AI_GetCurrentYaw,					idAI::Event_GetCurrentYaw )
214 	EVENT( AI_TurnTo,							idAI::Event_TurnTo )
215 	EVENT( AI_TurnToPos,						idAI::Event_TurnToPos )
216 	EVENT( AI_TurnToEntity,						idAI::Event_TurnToEntity )
217 	EVENT( AI_MoveStatus,						idAI::Event_MoveStatus )
218 	EVENT( AI_StopMove,							idAI::Event_StopMove )
219 	EVENT( AI_MoveToCover,						idAI::Event_MoveToCover )
220 	EVENT( AI_MoveToEnemy,						idAI::Event_MoveToEnemy )
221 	EVENT( AI_MoveToEnemyHeight,				idAI::Event_MoveToEnemyHeight )
222 	EVENT( AI_MoveOutOfRange,					idAI::Event_MoveOutOfRange )
223 	EVENT( AI_MoveToAttackPosition,				idAI::Event_MoveToAttackPosition )
224 	EVENT( AI_Wander,							idAI::Event_Wander )
225 	EVENT( AI_MoveToEntity,						idAI::Event_MoveToEntity )
226 	EVENT( AI_MoveToPosition,					idAI::Event_MoveToPosition )
227 	EVENT( AI_SlideTo,							idAI::Event_SlideTo )
228 	EVENT( AI_FacingIdeal,						idAI::Event_FacingIdeal )
229 	EVENT( AI_FaceEnemy,						idAI::Event_FaceEnemy )
230 	EVENT( AI_FaceEntity,						idAI::Event_FaceEntity )
231 	EVENT( AI_WaitAction,						idAI::Event_WaitAction )
232 	EVENT( AI_GetCombatNode,					idAI::Event_GetCombatNode )
233 	EVENT( AI_EnemyInCombatCone,				idAI::Event_EnemyInCombatCone )
234 	EVENT( AI_WaitMove,							idAI::Event_WaitMove )
235 	EVENT( AI_GetJumpVelocity,					idAI::Event_GetJumpVelocity )
236 	EVENT( AI_EntityInAttackCone,				idAI::Event_EntityInAttackCone )
237 	EVENT( AI_CanSeeEntity,						idAI::Event_CanSeeEntity )
238 	EVENT( AI_SetTalkTarget,					idAI::Event_SetTalkTarget )
239 	EVENT( AI_GetTalkTarget,					idAI::Event_GetTalkTarget )
240 	EVENT( AI_SetTalkState,						idAI::Event_SetTalkState )
241 	EVENT( AI_EnemyRange,						idAI::Event_EnemyRange )
242 	EVENT( AI_EnemyRange2D,						idAI::Event_EnemyRange2D )
243 	EVENT( AI_GetEnemy,							idAI::Event_GetEnemy )
244 	EVENT( AI_GetEnemyPos,						idAI::Event_GetEnemyPos )
245 	EVENT( AI_GetEnemyEyePos,					idAI::Event_GetEnemyEyePos )
246 	EVENT( AI_PredictEnemyPos,					idAI::Event_PredictEnemyPos )
247 	EVENT( AI_CanHitEnemy,						idAI::Event_CanHitEnemy )
248 	EVENT( AI_CanHitEnemyFromAnim,				idAI::Event_CanHitEnemyFromAnim )
249 	EVENT( AI_CanHitEnemyFromJoint,				idAI::Event_CanHitEnemyFromJoint )
250 	EVENT( AI_EnemyPositionValid,				idAI::Event_EnemyPositionValid )
251 	EVENT( AI_ChargeAttack,						idAI::Event_ChargeAttack )
252 	EVENT( AI_TestChargeAttack,					idAI::Event_TestChargeAttack )
253 	EVENT( AI_TestAnimMoveTowardEnemy,			idAI::Event_TestAnimMoveTowardEnemy )
254 	EVENT( AI_TestAnimMove,						idAI::Event_TestAnimMove )
255 	EVENT( AI_TestMoveToPosition,				idAI::Event_TestMoveToPosition )
256 	EVENT( AI_TestMeleeAttack,					idAI::Event_TestMeleeAttack )
257 	EVENT( AI_TestAnimAttack,					idAI::Event_TestAnimAttack )
258 	EVENT( AI_Shrivel,							idAI::Event_Shrivel )
259 	EVENT( AI_Burn,								idAI::Event_Burn )
260 	EVENT( AI_PreBurn,							idAI::Event_PreBurn )
261 	EVENT( AI_SetSmokeVisibility,				idAI::Event_SetSmokeVisibility )
262 	EVENT( AI_NumSmokeEmitters,					idAI::Event_NumSmokeEmitters )
263 	EVENT( AI_ClearBurn,						idAI::Event_ClearBurn )
264 	EVENT( AI_StopThinking,						idAI::Event_StopThinking )
265 	EVENT( AI_GetTurnDelta,						idAI::Event_GetTurnDelta )
266 	EVENT( AI_GetMoveType,						idAI::Event_GetMoveType )
267 	EVENT( AI_SetMoveType,						idAI::Event_SetMoveType )
268 	EVENT( AI_SaveMove,							idAI::Event_SaveMove )
269 	EVENT( AI_RestoreMove,						idAI::Event_RestoreMove )
270 	EVENT( AI_AllowMovement,					idAI::Event_AllowMovement )
271 	EVENT( AI_JumpFrame,						idAI::Event_JumpFrame )
272 	EVENT( AI_EnableClip,						idAI::Event_EnableClip )
273 	EVENT( AI_DisableClip,						idAI::Event_DisableClip )
274 	EVENT( AI_EnableGravity,					idAI::Event_EnableGravity )
275 	EVENT( AI_DisableGravity,					idAI::Event_DisableGravity )
276 	EVENT( AI_EnableAFPush,						idAI::Event_EnableAFPush )
277 	EVENT( AI_DisableAFPush,					idAI::Event_DisableAFPush )
278 	EVENT( AI_SetFlySpeed,						idAI::Event_SetFlySpeed )
279 	EVENT( AI_SetFlyOffset,						idAI::Event_SetFlyOffset )
280 	EVENT( AI_ClearFlyOffset,					idAI::Event_ClearFlyOffset )
281 	EVENT( AI_GetClosestHiddenTarget,			idAI::Event_GetClosestHiddenTarget )
282 	EVENT( AI_GetRandomTarget,					idAI::Event_GetRandomTarget )
283 	EVENT( AI_TravelDistanceToPoint,			idAI::Event_TravelDistanceToPoint )
284 	EVENT( AI_TravelDistanceToEntity,			idAI::Event_TravelDistanceToEntity )
285 	EVENT( AI_TravelDistanceBetweenPoints,		idAI::Event_TravelDistanceBetweenPoints )
286 	EVENT( AI_TravelDistanceBetweenEntities,	idAI::Event_TravelDistanceBetweenEntities )
287 	EVENT( AI_LookAtEntity,						idAI::Event_LookAtEntity )
288 	EVENT( AI_LookAtEnemy,						idAI::Event_LookAtEnemy )
289 	EVENT( AI_SetJointMod,						idAI::Event_SetJointMod )
290 	EVENT( AI_ThrowMoveable,					idAI::Event_ThrowMoveable )
291 	EVENT( AI_ThrowAF,							idAI::Event_ThrowAF )
292 	EVENT( EV_GetAngles,						idAI::Event_GetAngles )
293 	EVENT( EV_SetAngles,						idAI::Event_SetAngles )
294 	EVENT( AI_RealKill,							idAI::Event_RealKill )
295 	EVENT( AI_Kill,								idAI::Event_Kill )
296 	EVENT( AI_WakeOnFlashlight,					idAI::Event_WakeOnFlashlight )
297 	EVENT( AI_LocateEnemy,						idAI::Event_LocateEnemy )
298 	EVENT( AI_KickObstacles,					idAI::Event_KickObstacles )
299 	EVENT( AI_GetObstacle,						idAI::Event_GetObstacle )
300 	EVENT( AI_PushPointIntoAAS,					idAI::Event_PushPointIntoAAS )
301 	EVENT( AI_GetTurnRate,						idAI::Event_GetTurnRate )
302 	EVENT( AI_SetTurnRate,						idAI::Event_SetTurnRate )
303 	EVENT( AI_AnimTurn,							idAI::Event_AnimTurn )
304 	EVENT( AI_AllowHiddenMovement,				idAI::Event_AllowHiddenMovement )
305 	EVENT( AI_TriggerParticles,					idAI::Event_TriggerParticles )
306 	EVENT( AI_FindActorsInBounds,				idAI::Event_FindActorsInBounds )
307 	EVENT( AI_CanReachPosition,					idAI::Event_CanReachPosition )
308 	EVENT( AI_CanReachEntity,					idAI::Event_CanReachEntity )
309 	EVENT( AI_CanReachEnemy,					idAI::Event_CanReachEnemy )
310 	EVENT( AI_GetReachableEntityPosition,		idAI::Event_GetReachableEntityPosition )
311 #ifdef _D3XP
312 	EVENT( AI_MoveToPositionDirect,				idAI::Event_MoveToPositionDirect )
313 	EVENT( AI_AvoidObstacles,					idAI::Event_AvoidObstacles )
314 	EVENT( AI_TriggerFX,						idAI::Event_TriggerFX )
315 	EVENT( AI_StartEmitter,						idAI::Event_StartEmitter )
316 	EVENT( AI_GetEmitter,						idAI::Event_GetEmitter )
317 	EVENT( AI_StopEmitter,						idAI::Event_StopEmitter )
318 #endif
319 END_CLASS
320 
321 /*
322 =====================
323 idAI::Event_Activate
324 =====================
325 */
326 void idAI::Event_Activate( idEntity *activator ) {
327 	Activate( activator );
328 }
329 
330 /*
331 =====================
332 idAI::Event_Touch
333 =====================
334 */
Event_Touch(idEntity * other,trace_t * trace)335 void idAI::Event_Touch( idEntity *other, trace_t *trace ) {
336 	if ( !enemy.GetEntity() && !other->fl.notarget && ( ReactionTo( other ) & ATTACK_ON_ACTIVATE ) ) {
337 		Activate( other );
338 	}
339 	AI_PUSHED = true;
340 }
341 
342 /*
343 =====================
344 idAI::Event_FindEnemy
345 =====================
346 */
Event_FindEnemy(int useFOV)347 void idAI::Event_FindEnemy( int useFOV ) {
348 	int			i;
349 	idEntity	*ent;
350 	idActor		*actor;
351 
352 	if ( gameLocal.InPlayerPVS( this ) ) {
353 		for ( i = 0; i < gameLocal.numClients ; i++ ) {
354 			ent = gameLocal.entities[ i ];
355 
356 			if ( !ent || !ent->IsType( idActor::Type ) ) {
357 				continue;
358 			}
359 
360 			actor = static_cast<idActor *>( ent );
361 			if ( ( actor->health <= 0 ) || !( ReactionTo( actor ) & ATTACK_ON_SIGHT ) ) {
362 				continue;
363 			}
364 
365 			if ( CanSee( actor, useFOV != 0 ) ) {
366 				idThread::ReturnEntity( actor );
367 				return;
368 			}
369 		}
370 	}
371 
372 	idThread::ReturnEntity( NULL );
373 }
374 
375 /*
376 =====================
377 idAI::Event_FindEnemyAI
378 =====================
379 */
Event_FindEnemyAI(int useFOV)380 void idAI::Event_FindEnemyAI( int useFOV ) {
381 	idEntity	*ent;
382 	idActor		*actor;
383 	idActor		*bestEnemy;
384 	float		bestDist;
385 	float		dist;
386 	idVec3		delta;
387 	pvsHandle_t pvs;
388 
389 	pvs = gameLocal.pvs.SetupCurrentPVS( GetPVSAreas(), GetNumPVSAreas() );
390 
391 	bestDist = idMath::INFINITY;
392 	bestEnemy = NULL;
393 	for ( ent = gameLocal.activeEntities.Next(); ent != NULL; ent = ent->activeNode.Next() ) {
394 		if ( ent->fl.hidden || ent->fl.isDormant || !ent->IsType( idActor::Type ) ) {
395 			continue;
396 		}
397 
398 		actor = static_cast<idActor *>( ent );
399 		if ( ( actor->health <= 0 ) || !( ReactionTo( actor ) & ATTACK_ON_SIGHT ) ) {
400 			continue;
401 		}
402 
403 		if ( !gameLocal.pvs.InCurrentPVS( pvs, actor->GetPVSAreas(), actor->GetNumPVSAreas() ) ) {
404 			continue;
405 		}
406 
407 		delta = physicsObj.GetOrigin() - actor->GetPhysics()->GetOrigin();
408 		dist = delta.LengthSqr();
409 		if ( ( dist < bestDist ) && CanSee( actor, useFOV != 0 ) ) {
410 			bestDist = dist;
411 			bestEnemy = actor;
412 		}
413 	}
414 
415 	gameLocal.pvs.FreeCurrentPVS( pvs );
416 	idThread::ReturnEntity( bestEnemy );
417 }
418 
419 /*
420 =====================
421 idAI::Event_FindEnemyInCombatNodes
422 =====================
423 */
Event_FindEnemyInCombatNodes(void)424 void idAI::Event_FindEnemyInCombatNodes( void ) {
425 	int				i, j;
426 	idCombatNode	*node;
427 	idEntity		*ent;
428 	idEntity		*targetEnt;
429 	idActor			*actor;
430 
431 	if ( !gameLocal.InPlayerPVS( this ) ) {
432 		// don't locate the player when we're not in his PVS
433 		idThread::ReturnEntity( NULL );
434 		return;
435 	}
436 
437 	for ( i = 0; i < gameLocal.numClients ; i++ ) {
438 		ent = gameLocal.entities[ i ];
439 
440 		if ( !ent || !ent->IsType( idActor::Type ) ) {
441 			continue;
442 		}
443 
444 		actor = static_cast<idActor *>( ent );
445 		if ( ( actor->health <= 0 ) || !( ReactionTo( actor ) & ATTACK_ON_SIGHT ) ) {
446 			continue;
447 		}
448 
449 		for( j = 0; j < targets.Num(); j++ ) {
450 			targetEnt = targets[ j ].GetEntity();
451 			if ( !targetEnt || !targetEnt->IsType( idCombatNode::Type ) ) {
452 				continue;
453 			}
454 
455 			node = static_cast<idCombatNode *>( targetEnt );
456 			if ( !node->IsDisabled() && node->EntityInView( actor, actor->GetPhysics()->GetOrigin() ) ) {
457 				idThread::ReturnEntity( actor );
458 				return;
459 			}
460 		}
461 	}
462 
463 	idThread::ReturnEntity( NULL );
464 }
465 
466 /*
467 =====================
468 idAI::Event_ClosestReachableEnemyOfEntity
469 =====================
470 */
Event_ClosestReachableEnemyOfEntity(idEntity * team_mate)471 void idAI::Event_ClosestReachableEnemyOfEntity( idEntity *team_mate ) {
472 	idActor *actor;
473 	idActor *ent;
474 	idActor	*bestEnt;
475 	float	bestDistSquared;
476 	float	distSquared;
477 	idVec3	delta;
478 	int		areaNum;
479 	int		enemyAreaNum;
480 	aasPath_t path;
481 
482 	if ( !team_mate->IsType( idActor::Type ) ) {
483 		gameLocal.Error( "Entity '%s' is not an AI character or player", team_mate->GetName() );
484 	}
485 
486 	actor = static_cast<idActor *>( team_mate );
487 
488 	const idVec3 &origin = physicsObj.GetOrigin();
489 	areaNum = PointReachableAreaNum( origin );
490 
491 	bestDistSquared = idMath::INFINITY;
492 	bestEnt = NULL;
493 	for( ent = actor->enemyList.Next(); ent != NULL; ent = ent->enemyNode.Next() ) {
494 		if ( ent->fl.hidden ) {
495 			continue;
496 		}
497 		delta = ent->GetPhysics()->GetOrigin() - origin;
498 		distSquared = delta.LengthSqr();
499 		if ( distSquared < bestDistSquared ) {
500 			const idVec3 &enemyPos = ent->GetPhysics()->GetOrigin();
501 			enemyAreaNum = PointReachableAreaNum( enemyPos );
502 			if ( ( areaNum != 0 ) && PathToGoal( path, areaNum, origin, enemyAreaNum, enemyPos ) ) {
503 				bestEnt = ent;
504 				bestDistSquared = distSquared;
505 			}
506 		}
507 	}
508 
509 	idThread::ReturnEntity( bestEnt );
510 }
511 
512 /*
513 =====================
514 idAI::Event_HeardSound
515 =====================
516 */
Event_HeardSound(int ignore_team)517 void idAI::Event_HeardSound( int ignore_team ) {
518 	// check if we heard any sounds in the last frame
519 	idActor	*actor = gameLocal.GetAlertEntity();
520 	if ( actor && ( !ignore_team || ( ReactionTo( actor ) & ATTACK_ON_SIGHT ) ) && gameLocal.InPlayerPVS( this ) ) {
521 		idVec3 pos = actor->GetPhysics()->GetOrigin();
522 		idVec3 org = physicsObj.GetOrigin();
523 		float dist = ( pos - org ).LengthSqr();
524 		if ( dist < Square( AI_HEARING_RANGE ) ) {
525 			idThread::ReturnEntity( actor );
526 			return;
527 		}
528 	}
529 
530 	idThread::ReturnEntity( NULL );
531 }
532 
533 /*
534 =====================
535 idAI::Event_SetEnemy
536 =====================
537 */
Event_SetEnemy(idEntity * ent)538 void idAI::Event_SetEnemy( idEntity *ent ) {
539 	if ( !ent ) {
540 		ClearEnemy();
541 	} else if ( !ent->IsType( idActor::Type ) ) {
542 		gameLocal.Error( "'%s' is not an idActor (player or ai controlled character)", ent->name.c_str() );
543 	} else {
544 		SetEnemy( static_cast<idActor *>( ent ) );
545 	}
546 }
547 
548 /*
549 =====================
550 idAI::Event_ClearEnemy
551 =====================
552 */
Event_ClearEnemy(void)553 void idAI::Event_ClearEnemy( void ) {
554 	ClearEnemy();
555 }
556 
557 /*
558 =====================
559 idAI::Event_MuzzleFlash
560 =====================
561 */
Event_MuzzleFlash(const char * jointname)562 void idAI::Event_MuzzleFlash( const char *jointname ) {
563 	idVec3	muzzle;
564 	idMat3	axis;
565 
566 	GetMuzzle( jointname, muzzle, axis );
567 	TriggerWeaponEffects( muzzle );
568 }
569 
570 /*
571 =====================
572 idAI::Event_CreateMissile
573 =====================
574 */
Event_CreateMissile(const char * jointname)575 void idAI::Event_CreateMissile( const char *jointname ) {
576 	idVec3 muzzle;
577 	idMat3 axis;
578 
579 	if ( !projectileDef ) {
580 		gameLocal.Warning( "%s (%s) doesn't have a projectile specified", name.c_str(), GetEntityDefName() );
581 		return idThread::ReturnEntity( NULL );
582 	}
583 
584 	GetMuzzle( jointname, muzzle, axis );
585 	CreateProjectile( muzzle, viewAxis[ 0 ] * physicsObj.GetGravityAxis() );
586 	if ( projectile.GetEntity() ) {
587 		if ( !jointname || !jointname[ 0 ] ) {
588 			projectile.GetEntity()->Bind( this, true );
589 		} else {
590 			projectile.GetEntity()->BindToJoint( this, jointname, true );
591 		}
592 	}
593 	idThread::ReturnEntity( projectile.GetEntity() );
594 }
595 
596 /*
597 =====================
598 idAI::Event_AttackMissile
599 =====================
600 */
Event_AttackMissile(const char * jointname)601 void idAI::Event_AttackMissile( const char *jointname ) {
602 	idProjectile *proj;
603 
604 	proj = LaunchProjectile( jointname, enemy.GetEntity(), true );
605 	idThread::ReturnEntity( proj );
606 }
607 
608 /*
609 =====================
610 idAI::Event_FireMissileAtTarget
611 =====================
612 */
Event_FireMissileAtTarget(const char * jointname,const char * targetname)613 void idAI::Event_FireMissileAtTarget( const char *jointname, const char *targetname ) {
614 	idEntity		*aent;
615 	idProjectile	*proj;
616 
617 	aent = gameLocal.FindEntity( targetname );
618 	if ( !aent ) {
619 		gameLocal.Warning( "Entity '%s' not found for 'fireMissileAtTarget'", targetname );
620 	}
621 
622 	proj = LaunchProjectile( jointname, aent, false );
623 	idThread::ReturnEntity( proj );
624 }
625 
626 /*
627 =====================
628 idAI::Event_LaunchMissile
629 =====================
630 */
Event_LaunchMissile(const idVec3 & org,const idAngles & ang)631 void idAI::Event_LaunchMissile( const idVec3 &org, const idAngles &ang ) {
632 	idVec3		start;
633 	trace_t		tr;
634 	idBounds	projBounds;
635 	const idClipModel *projClip;
636 	idMat3		axis;
637 	float		distance;
638 
639 	if ( !projectileDef ) {
640 		gameLocal.Warning( "%s (%s) doesn't have a projectile specified", name.c_str(), GetEntityDefName() );
641 		idThread::ReturnEntity( NULL );
642 		return;
643 	}
644 
645 	axis = ang.ToMat3();
646 	if ( !projectile.GetEntity() ) {
647 		CreateProjectile( org, axis[ 0 ] );
648 	}
649 
650 	// make sure the projectile starts inside the monster bounding box
651 	const idBounds &ownerBounds = physicsObj.GetAbsBounds();
652 	projClip = projectile.GetEntity()->GetPhysics()->GetClipModel();
653 	projBounds = projClip->GetBounds().Rotate( projClip->GetAxis() );
654 
655 	// check if the owner bounds is bigger than the projectile bounds
656 	if ( ( ( ownerBounds[1][0] - ownerBounds[0][0] ) > ( projBounds[1][0] - projBounds[0][0] ) ) &&
657 		( ( ownerBounds[1][1] - ownerBounds[0][1] ) > ( projBounds[1][1] - projBounds[0][1] ) ) &&
658 		( ( ownerBounds[1][2] - ownerBounds[0][2] ) > ( projBounds[1][2] - projBounds[0][2] ) ) ) {
659 		if ( (ownerBounds - projBounds).RayIntersection( org, viewAxis[ 0 ], distance ) ) {
660 			start = org + distance * viewAxis[ 0 ];
661 		} else {
662 			start = ownerBounds.GetCenter();
663 		}
664 	} else {
665 		// projectile bounds bigger than the owner bounds, so just start it from the center
666 		start = ownerBounds.GetCenter();
667 	}
668 
669 	gameLocal.clip.Translation( tr, start, org, projClip, projClip->GetAxis(), MASK_SHOT_RENDERMODEL, this );
670 
671 	// launch the projectile
672 	idThread::ReturnEntity( projectile.GetEntity() );
673 	projectile.GetEntity()->Launch( tr.endpos, axis[ 0 ], vec3_origin );
674 	projectile = NULL;
675 
676 	TriggerWeaponEffects( tr.endpos );
677 
678 	lastAttackTime = gameLocal.time;
679 }
680 
681 
682 #ifdef _D3XP
683 /*
684 =====================
685 idAI::Event_LaunchProjectile
686 =====================
687 */
Event_LaunchProjectile(const char * entityDefName)688 void idAI::Event_LaunchProjectile( const char *entityDefName ) {
689 	idVec3				muzzle, start, dir;
690 	const idDict		*projDef;
691 	idMat3				axis;
692 	const idClipModel	*projClip;
693 	idBounds			projBounds;
694 	trace_t				tr;
695 	idEntity			*ent;
696 	const char			*clsname;
697 	float				distance;
698 	idProjectile		*proj = NULL;
699 
700 	projDef = gameLocal.FindEntityDefDict( entityDefName );
701 
702 	gameLocal.SpawnEntityDef( *projDef, &ent, false );
703 	if ( !ent ) {
704 		clsname = projectileDef->GetString( "classname" );
705 		gameLocal.Error( "Could not spawn entityDef '%s'", clsname );
706 	}
707 
708 	if ( !ent->IsType( idProjectile::Type ) ) {
709 		clsname = ent->GetClassname();
710 		gameLocal.Error( "'%s' is not an idProjectile", clsname );
711 	}
712 	proj = ( idProjectile * )ent;
713 
714 	GetMuzzle( "pistol", muzzle, axis );
715 	proj->Create( this, muzzle, axis[0] );
716 
717 	// make sure the projectile starts inside the monster bounding box
718 	const idBounds &ownerBounds = physicsObj.GetAbsBounds();
719 	projClip = proj->GetPhysics()->GetClipModel();
720 	projBounds = projClip->GetBounds().Rotate( projClip->GetAxis() );
721 	if ( (ownerBounds - projBounds).RayIntersection( muzzle, viewAxis[ 0 ], distance ) ) {
722 		start = muzzle + distance * viewAxis[ 0 ];
723 	} else {
724 		start = ownerBounds.GetCenter();
725 	}
726 	gameLocal.clip.Translation( tr, start, muzzle, projClip, projClip->GetAxis(), MASK_SHOT_RENDERMODEL, this );
727 	muzzle = tr.endpos;
728 
729 	GetAimDir( muzzle, enemy.GetEntity(), this, dir );
730 
731 	proj->Launch( muzzle, dir, vec3_origin );
732 
733 	TriggerWeaponEffects( muzzle );
734 }
735 
736 #endif
737 
738 
739 /*
740 =====================
741 idAI::Event_AttackMelee
742 =====================
743 */
Event_AttackMelee(const char * meleeDefName)744 void idAI::Event_AttackMelee( const char *meleeDefName ) {
745 	bool hit;
746 
747 	hit = AttackMelee( meleeDefName );
748 	idThread::ReturnInt( hit );
749 }
750 
751 /*
752 =====================
753 idAI::Event_DirectDamage
754 =====================
755 */
Event_DirectDamage(idEntity * damageTarget,const char * damageDefName)756 void idAI::Event_DirectDamage( idEntity *damageTarget, const char *damageDefName ) {
757 	DirectDamage( damageDefName, damageTarget );
758 }
759 
760 /*
761 =====================
762 idAI::Event_RadiusDamageFromJoint
763 =====================
764 */
Event_RadiusDamageFromJoint(const char * jointname,const char * damageDefName)765 void idAI::Event_RadiusDamageFromJoint( const char *jointname, const char *damageDefName ) {
766 	jointHandle_t joint;
767 	idVec3 org;
768 	idMat3 axis;
769 
770 	if ( !jointname || !jointname[ 0 ] ) {
771 		org = physicsObj.GetOrigin();
772 	} else {
773 		joint = animator.GetJointHandle( jointname );
774 		if ( joint == INVALID_JOINT ) {
775 			gameLocal.Error( "Unknown joint '%s' on %s", jointname, GetEntityDefName() );
776 		}
777 		GetJointWorldTransform( joint, gameLocal.time, org, axis );
778 	}
779 
780 	gameLocal.RadiusDamage( org, this, this, this, this, damageDefName );
781 }
782 
783 /*
784 =====================
785 idAI::Event_RandomPath
786 =====================
787 */
Event_RandomPath(void)788 void idAI::Event_RandomPath( void ) {
789 	idPathCorner *path;
790 
791 	path = idPathCorner::RandomPath( this, NULL );
792 	idThread::ReturnEntity( path );
793 }
794 
795 /*
796 =====================
797 idAI::Event_BeginAttack
798 =====================
799 */
Event_BeginAttack(const char * name)800 void idAI::Event_BeginAttack( const char *name ) {
801 	BeginAttack( name );
802 }
803 
804 /*
805 =====================
806 idAI::Event_EndAttack
807 =====================
808 */
Event_EndAttack(void)809 void idAI::Event_EndAttack( void ) {
810 	EndAttack();
811 }
812 
813 /*
814 =====================
815 idAI::Event_MeleeAttackToJoint
816 =====================
817 */
Event_MeleeAttackToJoint(const char * jointname,const char * meleeDefName)818 void idAI::Event_MeleeAttackToJoint( const char *jointname, const char *meleeDefName ) {
819 	jointHandle_t	joint;
820 	idVec3			start;
821 	idVec3			end;
822 	idMat3			axis;
823 	trace_t			trace;
824 	idEntity		*hitEnt;
825 
826 	joint = animator.GetJointHandle( jointname );
827 	if ( joint == INVALID_JOINT ) {
828 		gameLocal.Error( "Unknown joint '%s' on %s", jointname, GetEntityDefName() );
829 	}
830 	animator.GetJointTransform( joint, gameLocal.time, end, axis );
831 	end = physicsObj.GetOrigin() + ( end + modelOffset ) * viewAxis * physicsObj.GetGravityAxis();
832 	start = GetEyePosition();
833 
834 	if ( ai_debugMove.GetBool() ) {
835 		gameRenderWorld->DebugLine( colorYellow, start, end, gameLocal.msec );
836 	}
837 
838 	gameLocal.clip.TranslationEntities( trace, start, end, NULL, mat3_identity, MASK_SHOT_BOUNDINGBOX, this );
839 	if ( trace.fraction < 1.0f ) {
840 		hitEnt = gameLocal.GetTraceEntity( trace );
841 		if ( hitEnt && hitEnt->IsType( idActor::Type ) ) {
842 			DirectDamage( meleeDefName, hitEnt );
843 			idThread::ReturnInt( true );
844 			return;
845 		}
846 	}
847 
848 	idThread::ReturnInt( false );
849 }
850 
851 /*
852 =====================
853 idAI::Event_CanBecomeSolid
854 =====================
855 */
Event_CanBecomeSolid(void)856 void idAI::Event_CanBecomeSolid( void ) {
857 	int			i;
858 	int			num;
859 #ifdef _D3XP
860 	bool		returnValue = true;
861 #endif
862 	idEntity *	hit;
863 	idClipModel *cm;
864 	idClipModel *clipModels[ MAX_GENTITIES ];
865 
866 	num = gameLocal.clip.ClipModelsTouchingBounds( physicsObj.GetAbsBounds(), MASK_MONSTERSOLID, clipModels, MAX_GENTITIES );
867 	for ( i = 0; i < num; i++ ) {
868 		cm = clipModels[ i ];
869 
870 		// don't check render entities
871 		if ( cm->IsRenderModel() ) {
872 			continue;
873 		}
874 
875 		hit = cm->GetEntity();
876 		if ( ( hit == this ) || !hit->fl.takedamage ) {
877 			continue;
878 		}
879 
880 #ifdef _D3XP
881 		if ( (spawnClearMoveables && hit->IsType( idMoveable::Type )) || (hit->IsType( idBarrel::Type ) || hit->IsType( idExplodingBarrel::Type) ) ) {
882 			idVec3 push;
883 			push = hit->GetPhysics()->GetOrigin() - GetPhysics()->GetOrigin();
884 			push.z = 30.f;
885 			push.NormalizeFast();
886 			if ( (idMath::Fabs(push.x) < 0.15f) && (idMath::Fabs(push.y) < 0.15f) ) {
887 				push.x = 10.f; push.y = 10.f; push.z = 15.f;
888 				push.NormalizeFast();
889 			}
890 			push *= 300.f;
891 			hit->GetPhysics()->SetLinearVelocity( push );
892 		}
893 #endif
894 
895 		if ( physicsObj.ClipContents( cm ) ) {
896 #ifdef _D3XP
897 			returnValue = false;
898 #else
899 			idThread::ReturnFloat( false );
900 			return;
901 #endif
902 		}
903 	}
904 
905 #ifdef _D3XP
906 	idThread::ReturnFloat( returnValue );
907 #else
908 	idThread::ReturnFloat( true );
909 #endif
910 }
911 
912 /*
913 =====================
914 idAI::Event_BecomeSolid
915 =====================
916 */
Event_BecomeSolid(void)917 void idAI::Event_BecomeSolid( void ) {
918 	physicsObj.EnableClip();
919 	if ( spawnArgs.GetBool( "big_monster" ) ) {
920 		physicsObj.SetContents( 0 );
921 	} else if ( use_combat_bbox ) {
922 		physicsObj.SetContents( CONTENTS_BODY|CONTENTS_SOLID );
923 	} else {
924 		physicsObj.SetContents( CONTENTS_BODY );
925 	}
926 	physicsObj.GetClipModel()->Link( gameLocal.clip );
927 	fl.takedamage = !spawnArgs.GetBool( "noDamage" );
928 }
929 
930 /*
931 =====================
932 idAI::Event_BecomeNonSolid
933 =====================
934 */
Event_BecomeNonSolid(void)935 void idAI::Event_BecomeNonSolid( void ) {
936 	fl.takedamage = false;
937 	physicsObj.SetContents( 0 );
938 	physicsObj.GetClipModel()->Unlink();
939 }
940 
941 /*
942 =====================
943 idAI::Event_BecomeRagdoll
944 =====================
945 */
Event_BecomeRagdoll(void)946 void idAI::Event_BecomeRagdoll( void ) {
947 	bool result;
948 
949 	result = StartRagdoll();
950 	idThread::ReturnInt( result );
951 }
952 
953 /*
954 =====================
955 idAI::Event_StopRagdoll
956 =====================
957 */
Event_StopRagdoll(void)958 void idAI::Event_StopRagdoll( void ) {
959 	StopRagdoll();
960 
961 	// set back the monster physics
962 	SetPhysics( &physicsObj );
963 }
964 
965 /*
966 =====================
967 idAI::Event_SetHealth
968 =====================
969 */
Event_SetHealth(float newHealth)970 void idAI::Event_SetHealth( float newHealth ) {
971 	health = newHealth;
972 	fl.takedamage = true;
973 	if ( health > 0 ) {
974 		AI_DEAD = false;
975 	} else {
976 		AI_DEAD = true;
977 	}
978 }
979 
980 /*
981 =====================
982 idAI::Event_GetHealth
983 =====================
984 */
Event_GetHealth(void)985 void idAI::Event_GetHealth( void ) {
986 	idThread::ReturnFloat( health );
987 }
988 
989 /*
990 =====================
991 idAI::Event_AllowDamage
992 =====================
993 */
Event_AllowDamage(void)994 void idAI::Event_AllowDamage( void ) {
995 	fl.takedamage = true;
996 }
997 
998 /*
999 =====================
1000 idAI::Event_IgnoreDamage
1001 =====================
1002 */
Event_IgnoreDamage(void)1003 void idAI::Event_IgnoreDamage( void ) {
1004 	fl.takedamage = false;
1005 }
1006 
1007 /*
1008 =====================
1009 idAI::Event_GetCurrentYaw
1010 =====================
1011 */
Event_GetCurrentYaw(void)1012 void idAI::Event_GetCurrentYaw( void ) {
1013 	idThread::ReturnFloat( current_yaw );
1014 }
1015 
1016 /*
1017 =====================
1018 idAI::Event_TurnTo
1019 =====================
1020 */
Event_TurnTo(float angle)1021 void idAI::Event_TurnTo( float angle ) {
1022 	TurnToward( angle );
1023 }
1024 
1025 /*
1026 =====================
1027 idAI::Event_TurnToPos
1028 =====================
1029 */
Event_TurnToPos(const idVec3 & pos)1030 void idAI::Event_TurnToPos( const idVec3 &pos ) {
1031 	TurnToward( pos );
1032 }
1033 
1034 /*
1035 =====================
1036 idAI::Event_TurnToEntity
1037 =====================
1038 */
Event_TurnToEntity(idEntity * ent)1039 void idAI::Event_TurnToEntity( idEntity *ent ) {
1040 	if ( ent ) {
1041 		TurnToward( ent->GetPhysics()->GetOrigin() );
1042 	}
1043 }
1044 
1045 /*
1046 =====================
1047 idAI::Event_MoveStatus
1048 =====================
1049 */
Event_MoveStatus(void)1050 void idAI::Event_MoveStatus( void ) {
1051 	idThread::ReturnInt( move.moveStatus );
1052 }
1053 
1054 /*
1055 =====================
1056 idAI::Event_StopMove
1057 =====================
1058 */
Event_StopMove(void)1059 void idAI::Event_StopMove( void ) {
1060 	StopMove( MOVE_STATUS_DONE );
1061 }
1062 
1063 /*
1064 =====================
1065 idAI::Event_MoveToCover
1066 =====================
1067 */
Event_MoveToCover(void)1068 void idAI::Event_MoveToCover( void ) {
1069 	idActor *enemyEnt = enemy.GetEntity();
1070 
1071 	StopMove( MOVE_STATUS_DEST_NOT_FOUND );
1072 	if ( !enemyEnt || !MoveToCover( enemyEnt, lastVisibleEnemyPos ) ) {
1073 		return;
1074 	}
1075 }
1076 
1077 /*
1078 =====================
1079 idAI::Event_MoveToEnemy
1080 =====================
1081 */
Event_MoveToEnemy(void)1082 void idAI::Event_MoveToEnemy( void ) {
1083 	StopMove( MOVE_STATUS_DEST_NOT_FOUND );
1084 	if ( !enemy.GetEntity() || !MoveToEnemy() ) {
1085 		return;
1086 	}
1087 }
1088 
1089 /*
1090 =====================
1091 idAI::Event_MoveToEnemyHeight
1092 =====================
1093 */
Event_MoveToEnemyHeight(void)1094 void idAI::Event_MoveToEnemyHeight( void ) {
1095 	StopMove( MOVE_STATUS_DEST_NOT_FOUND );
1096 	MoveToEnemyHeight();
1097 }
1098 
1099 /*
1100 =====================
1101 idAI::Event_MoveOutOfRange
1102 =====================
1103 */
Event_MoveOutOfRange(idEntity * entity,float range)1104 void idAI::Event_MoveOutOfRange( idEntity *entity, float range ) {
1105 	StopMove( MOVE_STATUS_DEST_NOT_FOUND );
1106 	MoveOutOfRange( entity, range );
1107 }
1108 
1109 /*
1110 =====================
1111 idAI::Event_MoveToAttackPosition
1112 =====================
1113 */
Event_MoveToAttackPosition(idEntity * entity,const char * attack_anim)1114 void idAI::Event_MoveToAttackPosition( idEntity *entity, const char *attack_anim ) {
1115 	int anim;
1116 
1117 	StopMove( MOVE_STATUS_DEST_NOT_FOUND );
1118 
1119 	anim = GetAnim( ANIMCHANNEL_LEGS, attack_anim );
1120 	if ( !anim ) {
1121 		gameLocal.Error( "Unknown anim '%s'", attack_anim );
1122 	}
1123 
1124 	MoveToAttackPosition( entity, anim );
1125 }
1126 
1127 /*
1128 =====================
1129 idAI::Event_MoveToEntity
1130 =====================
1131 */
Event_MoveToEntity(idEntity * ent)1132 void idAI::Event_MoveToEntity( idEntity *ent ) {
1133 	StopMove( MOVE_STATUS_DEST_NOT_FOUND );
1134 	if ( ent ) {
1135 		MoveToEntity( ent );
1136 	}
1137 }
1138 
1139 /*
1140 =====================
1141 idAI::Event_MoveToPosition
1142 =====================
1143 */
Event_MoveToPosition(const idVec3 & pos)1144 void idAI::Event_MoveToPosition( const idVec3 &pos ) {
1145 	StopMove( MOVE_STATUS_DONE );
1146 	MoveToPosition( pos );
1147 }
1148 
1149 /*
1150 =====================
1151 idAI::Event_SlideTo
1152 =====================
1153 */
Event_SlideTo(const idVec3 & pos,float time)1154 void idAI::Event_SlideTo( const idVec3 &pos, float time ) {
1155 	SlideToPosition( pos, time );
1156 }
1157 /*
1158 =====================
1159 idAI::Event_Wander
1160 =====================
1161 */
Event_Wander(void)1162 void idAI::Event_Wander( void ) {
1163 	WanderAround();
1164 }
1165 
1166 /*
1167 =====================
1168 idAI::Event_FacingIdeal
1169 =====================
1170 */
Event_FacingIdeal(void)1171 void idAI::Event_FacingIdeal( void ) {
1172 	bool facing = FacingIdeal();
1173 	idThread::ReturnInt( facing );
1174 }
1175 
1176 /*
1177 =====================
1178 idAI::Event_FaceEnemy
1179 =====================
1180 */
Event_FaceEnemy(void)1181 void idAI::Event_FaceEnemy( void ) {
1182 	FaceEnemy();
1183 }
1184 
1185 /*
1186 =====================
1187 idAI::Event_FaceEntity
1188 =====================
1189 */
Event_FaceEntity(idEntity * ent)1190 void idAI::Event_FaceEntity( idEntity *ent ) {
1191 	FaceEntity( ent );
1192 }
1193 
1194 /*
1195 =====================
1196 idAI::Event_WaitAction
1197 =====================
1198 */
Event_WaitAction(const char * waitForState)1199 void idAI::Event_WaitAction( const char *waitForState ) {
1200 	if ( idThread::BeginMultiFrameEvent( this, &AI_WaitAction ) ) {
1201 		SetWaitState( waitForState );
1202 	}
1203 
1204 	if ( !WaitState() ) {
1205 		idThread::EndMultiFrameEvent( this, &AI_WaitAction );
1206 	}
1207 }
1208 
1209 /*
1210 =====================
1211 idAI::Event_GetCombatNode
1212 =====================
1213 */
Event_GetCombatNode(void)1214 void idAI::Event_GetCombatNode( void ) {
1215 	int				i;
1216 	float			dist;
1217 	idEntity		*targetEnt;
1218 	idCombatNode	*node;
1219 	float			bestDist;
1220 	idCombatNode	*bestNode;
1221 	idActor			*enemyEnt = enemy.GetEntity();
1222 
1223 	if ( !targets.Num() ) {
1224 		// no combat nodes
1225 		idThread::ReturnEntity( NULL );
1226 		return;
1227 	}
1228 
1229 	if ( !enemyEnt || !EnemyPositionValid() ) {
1230 		// don't return a combat node if we don't have an enemy or
1231 		// if we can see he's not in the last place we saw him
1232 
1233 #ifdef _D3XP
1234 		if ( team == 0 ) {
1235 			// find the closest attack node to the player
1236 			bestNode = NULL;
1237 			const idVec3 &myPos = physicsObj.GetOrigin();
1238 			const idVec3 &playerPos = gameLocal.GetLocalPlayer()->GetPhysics()->GetOrigin();
1239 
1240 			bestDist = ( myPos - playerPos ).LengthSqr();
1241 
1242 			for( i = 0; i < targets.Num(); i++ ) {
1243 				targetEnt = targets[ i ].GetEntity();
1244 				if ( !targetEnt || !targetEnt->IsType( idCombatNode::Type ) ) {
1245 					continue;
1246 				}
1247 
1248 				node = static_cast<idCombatNode *>( targetEnt );
1249 				if ( !node->IsDisabled() ) {
1250 					idVec3 org = node->GetPhysics()->GetOrigin();
1251 					dist = ( playerPos - org ).LengthSqr();
1252 					if ( dist < bestDist ) {
1253 						bestNode = node;
1254 						bestDist = dist;
1255 					}
1256 				}
1257 			}
1258 
1259 			idThread::ReturnEntity( bestNode );
1260 			return;
1261 		}
1262 #endif
1263 
1264 		idThread::ReturnEntity( NULL );
1265 		return;
1266 	}
1267 
1268 	// find the closest attack node that can see our enemy and is closer than our enemy
1269 	bestNode = NULL;
1270 	const idVec3 &myPos = physicsObj.GetOrigin();
1271 	bestDist = ( myPos - lastVisibleEnemyPos ).LengthSqr();
1272 	for( i = 0; i < targets.Num(); i++ ) {
1273 		targetEnt = targets[ i ].GetEntity();
1274 		if ( !targetEnt || !targetEnt->IsType( idCombatNode::Type ) ) {
1275 			continue;
1276 		}
1277 
1278 		node = static_cast<idCombatNode *>( targetEnt );
1279 		if ( !node->IsDisabled() && node->EntityInView( enemyEnt, lastVisibleEnemyPos ) ) {
1280 			idVec3 org = node->GetPhysics()->GetOrigin();
1281 			dist = ( myPos - org ).LengthSqr();
1282 			if ( dist < bestDist ) {
1283 				bestNode = node;
1284 				bestDist = dist;
1285 			}
1286 		}
1287 	}
1288 
1289 	idThread::ReturnEntity( bestNode );
1290 }
1291 
1292 /*
1293 =====================
1294 idAI::Event_EnemyInCombatCone
1295 =====================
1296 */
Event_EnemyInCombatCone(idEntity * ent,int use_current_enemy_location)1297 void idAI::Event_EnemyInCombatCone( idEntity *ent, int use_current_enemy_location ) {
1298 	idCombatNode	*node;
1299 	bool			result;
1300 	idActor			*enemyEnt = enemy.GetEntity();
1301 
1302 	if ( !targets.Num() ) {
1303 		// no combat nodes
1304 		idThread::ReturnInt( false );
1305 		return;
1306 	}
1307 
1308 	if ( !enemyEnt ) {
1309 		// have to have an enemy
1310 		idThread::ReturnInt( false );
1311 		return;
1312 	}
1313 
1314 	if ( !ent || !ent->IsType( idCombatNode::Type ) ) {
1315 		// not a combat node
1316 		idThread::ReturnInt( false );
1317 		return;
1318 	}
1319 
1320 #ifdef _D3XP
1321 	//Allow the level designers define attack nodes that the enemy should never leave.
1322 	//This is different that the turrent type combat nodes because they can play an animation
1323 	if(ent->spawnArgs.GetBool("neverLeave", "0")) {
1324 		idThread::ReturnInt( true );
1325 		return;
1326 	}
1327 #endif
1328 
1329 	node = static_cast<idCombatNode *>( ent );
1330 	if ( use_current_enemy_location ) {
1331 		const idVec3 &pos = enemyEnt->GetPhysics()->GetOrigin();
1332 		result = node->EntityInView( enemyEnt, pos );
1333 	} else {
1334 		result = node->EntityInView( enemyEnt, lastVisibleEnemyPos );
1335 	}
1336 
1337 	idThread::ReturnInt( result );
1338 }
1339 
1340 /*
1341 =====================
1342 idAI::Event_WaitMove
1343 =====================
1344 */
Event_WaitMove(void)1345 void idAI::Event_WaitMove( void ) {
1346 	idThread::BeginMultiFrameEvent( this, &AI_WaitMove );
1347 
1348 	if ( MoveDone() ) {
1349 		idThread::EndMultiFrameEvent( this, &AI_WaitMove );
1350 	}
1351 }
1352 
1353 /*
1354 =====================
1355 idAI::Event_GetJumpVelocity
1356 =====================
1357 */
Event_GetJumpVelocity(const idVec3 & pos,float speed,float max_height)1358 void idAI::Event_GetJumpVelocity( const idVec3 &pos, float speed, float max_height ) {
1359 	idVec3 start;
1360 	idVec3 end;
1361 	idVec3 dir;
1362 	float dist;
1363 	bool result;
1364 	idEntity *enemyEnt = enemy.GetEntity();
1365 
1366 	if ( !enemyEnt ) {
1367 		idThread::ReturnVector( vec3_zero );
1368 		return;
1369 	}
1370 
1371 	if ( speed <= 0.0f ) {
1372 		gameLocal.Error( "Invalid speed.  speed must be > 0." );
1373 	}
1374 
1375 	start = physicsObj.GetOrigin();
1376 	end = pos;
1377 	dir = end - start;
1378 	dist = dir.Normalize();
1379 	if ( dist > 16.0f ) {
1380 		dist -= 16.0f;
1381 		end -= dir * 16.0f;
1382 	}
1383 
1384 	result = PredictTrajectory( start, end, speed, physicsObj.GetGravity(), physicsObj.GetClipModel(), MASK_MONSTERSOLID, max_height, this, enemyEnt, ai_debugMove.GetBool() ? 4000 : 0, dir );
1385 	if ( result ) {
1386 		idThread::ReturnVector( dir * speed );
1387 	} else {
1388 		idThread::ReturnVector( vec3_zero );
1389 	}
1390 }
1391 
1392 /*
1393 =====================
1394 idAI::Event_EntityInAttackCone
1395 =====================
1396 */
Event_EntityInAttackCone(idEntity * ent)1397 void idAI::Event_EntityInAttackCone( idEntity *ent ) {
1398 	float	attack_cone;
1399 	idVec3	delta;
1400 	float	yaw;
1401 	float	relYaw;
1402 
1403 	if ( !ent ) {
1404 		idThread::ReturnInt( false );
1405 		return;
1406 	}
1407 
1408 	delta = ent->GetPhysics()->GetOrigin() - GetEyePosition();
1409 
1410 	// get our gravity normal
1411 	const idVec3 &gravityDir = GetPhysics()->GetGravityNormal();
1412 
1413 	// infinite vertical vision, so project it onto our orientation plane
1414 	delta -= gravityDir * ( gravityDir * delta );
1415 
1416 	delta.Normalize();
1417 	yaw = delta.ToYaw();
1418 
1419 	attack_cone = spawnArgs.GetFloat( "attack_cone", "70" );
1420 	relYaw = idMath::AngleNormalize180( ideal_yaw - yaw );
1421 	if ( idMath::Fabs( relYaw ) < ( attack_cone * 0.5f ) ) {
1422 		idThread::ReturnInt( true );
1423 	} else {
1424 		idThread::ReturnInt( false );
1425 	}
1426 }
1427 
1428 /*
1429 =====================
1430 idAI::Event_CanSeeEntity
1431 =====================
1432 */
Event_CanSeeEntity(idEntity * ent)1433 void idAI::Event_CanSeeEntity( idEntity *ent ) {
1434 	if ( !ent ) {
1435 		idThread::ReturnInt( false );
1436 		return;
1437 	}
1438 
1439 	bool cansee = CanSee( ent, false );
1440 	idThread::ReturnInt( cansee );
1441 }
1442 
1443 /*
1444 =====================
1445 idAI::Event_SetTalkTarget
1446 =====================
1447 */
Event_SetTalkTarget(idEntity * target)1448 void idAI::Event_SetTalkTarget( idEntity *target ) {
1449 	if ( target && !target->IsType( idActor::Type ) ) {
1450 		gameLocal.Error( "Cannot set talk target to '%s'.  Not a character or player.", target->GetName() );
1451 	}
1452 	talkTarget = static_cast<idActor *>( target );
1453 	if ( target ) {
1454 		AI_TALK = true;
1455 	} else {
1456 		AI_TALK = false;
1457 	}
1458 }
1459 
1460 /*
1461 =====================
1462 idAI::Event_GetTalkTarget
1463 =====================
1464 */
Event_GetTalkTarget(void)1465 void idAI::Event_GetTalkTarget( void ) {
1466 	idThread::ReturnEntity( talkTarget.GetEntity() );
1467 }
1468 
1469 /*
1470 ================
1471 idAI::Event_SetTalkState
1472 ================
1473 */
Event_SetTalkState(int state)1474 void idAI::Event_SetTalkState( int state ) {
1475 	if ( ( state < 0 ) || ( state >= NUM_TALK_STATES ) ) {
1476 		gameLocal.Error( "Invalid talk state (%d)", state );
1477 	}
1478 
1479 	talk_state = static_cast<talkState_t>( state );
1480 }
1481 
1482 /*
1483 =====================
1484 idAI::Event_EnemyRange
1485 =====================
1486 */
Event_EnemyRange(void)1487 void idAI::Event_EnemyRange( void ) {
1488 	float dist;
1489 	idActor *enemyEnt = enemy.GetEntity();
1490 
1491 	if ( enemyEnt ) {
1492 		dist = ( enemyEnt->GetPhysics()->GetOrigin() - GetPhysics()->GetOrigin() ).Length();
1493 	} else {
1494 		// Just some really high number
1495 		dist = idMath::INFINITY;
1496 	}
1497 
1498 	idThread::ReturnFloat( dist );
1499 }
1500 
1501 /*
1502 =====================
1503 idAI::Event_EnemyRange2D
1504 =====================
1505 */
Event_EnemyRange2D(void)1506 void idAI::Event_EnemyRange2D( void ) {
1507 	float dist;
1508 	idActor *enemyEnt = enemy.GetEntity();
1509 
1510 	if ( enemyEnt ) {
1511 		dist = ( enemyEnt->GetPhysics()->GetOrigin().ToVec2() - GetPhysics()->GetOrigin().ToVec2() ).Length();
1512 	} else {
1513 		// Just some really high number
1514 		dist = idMath::INFINITY;
1515 	}
1516 
1517 	idThread::ReturnFloat( dist );
1518 }
1519 
1520 /*
1521 =====================
1522 idAI::Event_GetEnemy
1523 =====================
1524 */
Event_GetEnemy(void)1525 void idAI::Event_GetEnemy( void ) {
1526 	idThread::ReturnEntity( enemy.GetEntity() );
1527 }
1528 
1529 /*
1530 =====================
1531 idAI::Event_GetEnemyPos
1532 =====================
1533 */
Event_GetEnemyPos(void)1534 void idAI::Event_GetEnemyPos( void ) {
1535 	idThread::ReturnVector( lastVisibleEnemyPos );
1536 }
1537 
1538 /*
1539 =====================
1540 idAI::Event_GetEnemyEyePos
1541 =====================
1542 */
Event_GetEnemyEyePos(void)1543 void idAI::Event_GetEnemyEyePos( void ) {
1544 	idThread::ReturnVector( lastVisibleEnemyPos + lastVisibleEnemyEyeOffset );
1545 }
1546 
1547 /*
1548 =====================
1549 idAI::Event_PredictEnemyPos
1550 =====================
1551 */
Event_PredictEnemyPos(float time)1552 void idAI::Event_PredictEnemyPos( float time ) {
1553 	predictedPath_t path;
1554 	idActor *enemyEnt = enemy.GetEntity();
1555 
1556 	// if no enemy set
1557 	if ( !enemyEnt ) {
1558 		idThread::ReturnVector( physicsObj.GetOrigin() );
1559 		return;
1560 	}
1561 
1562 	// predict the enemy movement
1563 	idAI::PredictPath( enemyEnt, aas, lastVisibleEnemyPos, enemyEnt->GetPhysics()->GetLinearVelocity(), SEC2MS( time ), SEC2MS( time ), ( move.moveType == MOVETYPE_FLY ) ? SE_BLOCKED : ( SE_BLOCKED | SE_ENTER_LEDGE_AREA ), path );
1564 
1565 	idThread::ReturnVector( path.endPos );
1566 }
1567 
1568 /*
1569 =====================
1570 idAI::Event_CanHitEnemy
1571 =====================
1572 */
Event_CanHitEnemy(void)1573 void idAI::Event_CanHitEnemy( void ) {
1574 	trace_t	tr;
1575 	idEntity *hit;
1576 
1577 	idActor *enemyEnt = enemy.GetEntity();
1578 	if ( !AI_ENEMY_VISIBLE || !enemyEnt ) {
1579 		idThread::ReturnInt( false );
1580 		return;
1581 	}
1582 
1583 	// don't check twice per frame
1584 	if ( gameLocal.time == lastHitCheckTime ) {
1585 		idThread::ReturnInt( lastHitCheckResult );
1586 		return;
1587 	}
1588 
1589 	lastHitCheckTime = gameLocal.time;
1590 
1591 	idVec3 toPos = enemyEnt->GetEyePosition();
1592 	idVec3 eye = GetEyePosition();
1593 	idVec3 dir;
1594 
1595 	// expand the ray out as far as possible so we can detect anything behind the enemy
1596 	dir = toPos - eye;
1597 	dir.Normalize();
1598 	toPos = eye + dir * MAX_WORLD_SIZE;
1599 	gameLocal.clip.TracePoint( tr, eye, toPos, MASK_SHOT_BOUNDINGBOX, this );
1600 	hit = gameLocal.GetTraceEntity( tr );
1601 	if ( tr.fraction >= 1.0f || ( hit == enemyEnt ) ) {
1602 		lastHitCheckResult = true;
1603 	} else if ( ( tr.fraction < 1.0f ) && ( hit->IsType( idAI::Type ) ) &&
1604 		( static_cast<idAI *>( hit )->team != team ) ) {
1605 		lastHitCheckResult = true;
1606 	} else {
1607 		lastHitCheckResult = false;
1608 	}
1609 
1610 	idThread::ReturnInt( lastHitCheckResult );
1611 }
1612 
1613 /*
1614 =====================
1615 idAI::Event_CanHitEnemyFromAnim
1616 =====================
1617 */
Event_CanHitEnemyFromAnim(const char * animname)1618 void idAI::Event_CanHitEnemyFromAnim( const char *animname ) {
1619 	int		anim;
1620 	idVec3	dir;
1621 	idVec3	local_dir;
1622 	idVec3	fromPos;
1623 	idMat3	axis;
1624 	idVec3	start;
1625 	trace_t	tr;
1626 	float	distance;
1627 
1628 	idActor *enemyEnt = enemy.GetEntity();
1629 	if ( !AI_ENEMY_VISIBLE || !enemyEnt ) {
1630 		idThread::ReturnInt( false );
1631 		return;
1632 	}
1633 
1634 	anim = GetAnim( ANIMCHANNEL_LEGS, animname );
1635 	if ( !anim ) {
1636 		idThread::ReturnInt( false );
1637 		return;
1638 	}
1639 
1640 	// just do a ray test if close enough
1641 	if ( enemyEnt->GetPhysics()->GetAbsBounds().IntersectsBounds( physicsObj.GetAbsBounds().Expand( 16.0f ) ) ) {
1642 		Event_CanHitEnemy();
1643 		return;
1644 	}
1645 
1646 	// calculate the world transform of the launch position
1647 	const idVec3 &org = physicsObj.GetOrigin();
1648 	dir = lastVisibleEnemyPos - org;
1649 	physicsObj.GetGravityAxis().ProjectVector( dir, local_dir );
1650 	local_dir.z = 0.0f;
1651 	local_dir.ToVec2().Normalize();
1652 	axis = local_dir.ToMat3();
1653 	fromPos = physicsObj.GetOrigin() + missileLaunchOffset[ anim ] * axis;
1654 
1655 	if ( projectileClipModel == NULL ) {
1656 		CreateProjectileClipModel();
1657 	}
1658 
1659 	// check if the owner bounds is bigger than the projectile bounds
1660 	const idBounds &ownerBounds = physicsObj.GetAbsBounds();
1661 	const idBounds &projBounds = projectileClipModel->GetBounds();
1662 	if ( ( ( ownerBounds[1][0] - ownerBounds[0][0] ) > ( projBounds[1][0] - projBounds[0][0] ) ) &&
1663 		( ( ownerBounds[1][1] - ownerBounds[0][1] ) > ( projBounds[1][1] - projBounds[0][1] ) ) &&
1664 		( ( ownerBounds[1][2] - ownerBounds[0][2] ) > ( projBounds[1][2] - projBounds[0][2] ) ) ) {
1665 		if ( (ownerBounds - projBounds).RayIntersection( org, viewAxis[ 0 ], distance ) ) {
1666 			start = org + distance * viewAxis[ 0 ];
1667 		} else {
1668 			start = ownerBounds.GetCenter();
1669 		}
1670 	} else {
1671 		// projectile bounds bigger than the owner bounds, so just start it from the center
1672 		start = ownerBounds.GetCenter();
1673 	}
1674 
1675 	gameLocal.clip.Translation( tr, start, fromPos, projectileClipModel, mat3_identity, MASK_SHOT_RENDERMODEL, this );
1676 	fromPos = tr.endpos;
1677 
1678 	if ( GetAimDir( fromPos, enemy.GetEntity(), this, dir ) ) {
1679 		idThread::ReturnInt( true );
1680 	} else {
1681 		idThread::ReturnInt( false );
1682 	}
1683 }
1684 
1685 /*
1686 =====================
1687 idAI::Event_CanHitEnemyFromJoint
1688 =====================
1689 */
Event_CanHitEnemyFromJoint(const char * jointname)1690 void idAI::Event_CanHitEnemyFromJoint( const char *jointname ) {
1691 	trace_t	tr;
1692 	idVec3	muzzle;
1693 	idMat3	axis;
1694 	idVec3	start;
1695 	float	distance;
1696 
1697 	idActor *enemyEnt = enemy.GetEntity();
1698 	if ( !AI_ENEMY_VISIBLE || !enemyEnt ) {
1699 		idThread::ReturnInt( false );
1700 		return;
1701 	}
1702 
1703 	// don't check twice per frame
1704 	if ( gameLocal.time == lastHitCheckTime ) {
1705 		idThread::ReturnInt( lastHitCheckResult );
1706 		return;
1707 	}
1708 
1709 	lastHitCheckTime = gameLocal.time;
1710 
1711 	const idVec3 &org = physicsObj.GetOrigin();
1712 	idVec3 toPos = enemyEnt->GetEyePosition();
1713 	jointHandle_t joint = animator.GetJointHandle( jointname );
1714 	if ( joint == INVALID_JOINT ) {
1715 		gameLocal.Error( "Unknown joint '%s' on %s", jointname, GetEntityDefName() );
1716 	}
1717 	animator.GetJointTransform( joint, gameLocal.time, muzzle, axis );
1718 	muzzle = org + ( muzzle + modelOffset ) * viewAxis * physicsObj.GetGravityAxis();
1719 
1720 	if ( projectileClipModel == NULL ) {
1721 		CreateProjectileClipModel();
1722 	}
1723 
1724 	// check if the owner bounds is bigger than the projectile bounds
1725 	const idBounds &ownerBounds = physicsObj.GetAbsBounds();
1726 	const idBounds &projBounds = projectileClipModel->GetBounds();
1727 	if ( ( ( ownerBounds[1][0] - ownerBounds[0][0] ) > ( projBounds[1][0] - projBounds[0][0] ) ) &&
1728 		( ( ownerBounds[1][1] - ownerBounds[0][1] ) > ( projBounds[1][1] - projBounds[0][1] ) ) &&
1729 		( ( ownerBounds[1][2] - ownerBounds[0][2] ) > ( projBounds[1][2] - projBounds[0][2] ) ) ) {
1730 		if ( (ownerBounds - projBounds).RayIntersection( org, viewAxis[ 0 ], distance ) ) {
1731 			start = org + distance * viewAxis[ 0 ];
1732 		} else {
1733 			start = ownerBounds.GetCenter();
1734 		}
1735 	} else {
1736 		// projectile bounds bigger than the owner bounds, so just start it from the center
1737 		start = ownerBounds.GetCenter();
1738 	}
1739 
1740 	gameLocal.clip.Translation( tr, start, muzzle, projectileClipModel, mat3_identity, MASK_SHOT_BOUNDINGBOX, this );
1741 	muzzle = tr.endpos;
1742 
1743 	gameLocal.clip.Translation( tr, muzzle, toPos, projectileClipModel, mat3_identity, MASK_SHOT_BOUNDINGBOX, this );
1744 	if ( tr.fraction >= 1.0f || ( gameLocal.GetTraceEntity( tr ) == enemyEnt ) ) {
1745 		lastHitCheckResult = true;
1746 	} else {
1747 		lastHitCheckResult = false;
1748 	}
1749 
1750 	idThread::ReturnInt( lastHitCheckResult );
1751 }
1752 
1753 /*
1754 =====================
1755 idAI::Event_EnemyPositionValid
1756 =====================
1757 */
Event_EnemyPositionValid(void)1758 void idAI::Event_EnemyPositionValid( void ) {
1759 	bool result;
1760 
1761 	result = EnemyPositionValid();
1762 	idThread::ReturnInt( result );
1763 }
1764 
1765 /*
1766 =====================
1767 idAI::Event_ChargeAttack
1768 =====================
1769 */
Event_ChargeAttack(const char * damageDef)1770 void idAI::Event_ChargeAttack( const char *damageDef ) {
1771 	idActor *enemyEnt = enemy.GetEntity();
1772 
1773 	StopMove( MOVE_STATUS_DEST_NOT_FOUND );
1774 	if ( enemyEnt ) {
1775 		idVec3 enemyOrg;
1776 
1777 		if ( move.moveType == MOVETYPE_FLY ) {
1778 			// position destination so that we're in the enemy's view
1779 			enemyOrg = enemyEnt->GetEyePosition();
1780 			enemyOrg -= enemyEnt->GetPhysics()->GetGravityNormal() * fly_offset;
1781 		} else {
1782 			enemyOrg = enemyEnt->GetPhysics()->GetOrigin();
1783 		}
1784 
1785 		BeginAttack( damageDef );
1786 		DirectMoveToPosition( enemyOrg );
1787 		TurnToward( enemyOrg );
1788 	}
1789 }
1790 
1791 /*
1792 =====================
1793 idAI::Event_TestChargeAttack
1794 =====================
1795 */
Event_TestChargeAttack(void)1796 void idAI::Event_TestChargeAttack( void ) {
1797 	idActor *enemyEnt = enemy.GetEntity();
1798 	predictedPath_t path;
1799 	idVec3 end;
1800 
1801 	if ( !enemyEnt ) {
1802 		idThread::ReturnFloat( 0.0f );
1803 		return;
1804 	}
1805 
1806 	if ( move.moveType == MOVETYPE_FLY ) {
1807 		// position destination so that we're in the enemy's view
1808 		end = enemyEnt->GetEyePosition();
1809 		end -= enemyEnt->GetPhysics()->GetGravityNormal() * fly_offset;
1810 	} else {
1811 		end = enemyEnt->GetPhysics()->GetOrigin();
1812 	}
1813 
1814 	idAI::PredictPath( this, aas, physicsObj.GetOrigin(), end - physicsObj.GetOrigin(), 1000, 1000, ( move.moveType == MOVETYPE_FLY ) ? SE_BLOCKED : ( SE_ENTER_OBSTACLE | SE_BLOCKED | SE_ENTER_LEDGE_AREA ), path );
1815 
1816 	if ( ai_debugMove.GetBool() ) {
1817 		gameRenderWorld->DebugLine( colorGreen, physicsObj.GetOrigin(), end, gameLocal.msec );
1818 		gameRenderWorld->DebugBounds( path.endEvent == 0 ? colorYellow : colorRed, physicsObj.GetBounds(), end, gameLocal.msec );
1819 	}
1820 
1821 	if ( ( path.endEvent == 0 ) || ( path.blockingEntity == enemyEnt ) ) {
1822 		idVec3 delta = end - physicsObj.GetOrigin();
1823 		float time = delta.LengthFast();
1824 		idThread::ReturnFloat( time );
1825 	} else {
1826 		idThread::ReturnFloat( 0.0f );
1827 	}
1828 }
1829 
1830 /*
1831 =====================
1832 idAI::Event_TestAnimMoveTowardEnemy
1833 =====================
1834 */
Event_TestAnimMoveTowardEnemy(const char * animname)1835 void idAI::Event_TestAnimMoveTowardEnemy( const char *animname ) {
1836 	int				anim;
1837 	predictedPath_t path;
1838 	idVec3			moveVec;
1839 	float			yaw;
1840 	idVec3			delta;
1841 	idActor			*enemyEnt;
1842 
1843 	enemyEnt = enemy.GetEntity();
1844 	if ( !enemyEnt ) {
1845 		idThread::ReturnInt( false );
1846 		return;
1847 	}
1848 
1849 	anim = GetAnim( ANIMCHANNEL_LEGS, animname );
1850 	if ( !anim ) {
1851 		gameLocal.DWarning( "missing '%s' animation on '%s' (%s)", animname, name.c_str(), GetEntityDefName() );
1852 		idThread::ReturnInt( false );
1853 		return;
1854 	}
1855 
1856 	delta = enemyEnt->GetPhysics()->GetOrigin() - physicsObj.GetOrigin();
1857 	yaw = delta.ToYaw();
1858 
1859 	moveVec = animator.TotalMovementDelta( anim ) * idAngles( 0.0f, yaw, 0.0f ).ToMat3() * physicsObj.GetGravityAxis();
1860 	idAI::PredictPath( this, aas, physicsObj.GetOrigin(), moveVec, 1000, 1000, ( move.moveType == MOVETYPE_FLY ) ? SE_BLOCKED : ( SE_ENTER_OBSTACLE | SE_BLOCKED | SE_ENTER_LEDGE_AREA ), path );
1861 
1862 	if ( ai_debugMove.GetBool() ) {
1863 		gameRenderWorld->DebugLine( colorGreen, physicsObj.GetOrigin(), physicsObj.GetOrigin() + moveVec, gameLocal.msec );
1864 		gameRenderWorld->DebugBounds( path.endEvent == 0 ? colorYellow : colorRed, physicsObj.GetBounds(), physicsObj.GetOrigin() + moveVec, gameLocal.msec );
1865 	}
1866 
1867 	idThread::ReturnInt( path.endEvent == 0 );
1868 }
1869 
1870 /*
1871 =====================
1872 idAI::Event_TestAnimMove
1873 =====================
1874 */
Event_TestAnimMove(const char * animname)1875 void idAI::Event_TestAnimMove( const char *animname ) {
1876 	int				anim;
1877 	predictedPath_t path;
1878 	idVec3			moveVec;
1879 
1880 	anim = GetAnim( ANIMCHANNEL_LEGS, animname );
1881 	if ( !anim ) {
1882 		gameLocal.DWarning( "missing '%s' animation on '%s' (%s)", animname, name.c_str(), GetEntityDefName() );
1883 		idThread::ReturnInt( false );
1884 		return;
1885 	}
1886 
1887 	moveVec = animator.TotalMovementDelta( anim ) * idAngles( 0.0f, ideal_yaw, 0.0f ).ToMat3() * physicsObj.GetGravityAxis();
1888 	idAI::PredictPath( this, aas, physicsObj.GetOrigin(), moveVec, 1000, 1000, ( move.moveType == MOVETYPE_FLY ) ? SE_BLOCKED : ( SE_ENTER_OBSTACLE | SE_BLOCKED | SE_ENTER_LEDGE_AREA ), path );
1889 
1890 	if ( ai_debugMove.GetBool() ) {
1891 		gameRenderWorld->DebugLine( colorGreen, physicsObj.GetOrigin(), physicsObj.GetOrigin() + moveVec, gameLocal.msec );
1892 		gameRenderWorld->DebugBounds( path.endEvent == 0 ? colorYellow : colorRed, physicsObj.GetBounds(), physicsObj.GetOrigin() + moveVec, gameLocal.msec );
1893 	}
1894 
1895 	idThread::ReturnInt( path.endEvent == 0 );
1896 }
1897 
1898 /*
1899 =====================
1900 idAI::Event_TestMoveToPosition
1901 =====================
1902 */
Event_TestMoveToPosition(const idVec3 & position)1903 void idAI::Event_TestMoveToPosition( const idVec3 &position ) {
1904 	predictedPath_t path;
1905 
1906 	idAI::PredictPath( this, aas, physicsObj.GetOrigin(), position - physicsObj.GetOrigin(), 1000, 1000, ( move.moveType == MOVETYPE_FLY ) ? SE_BLOCKED : ( SE_ENTER_OBSTACLE | SE_BLOCKED | SE_ENTER_LEDGE_AREA ), path );
1907 
1908 	if ( ai_debugMove.GetBool() ) {
1909 		gameRenderWorld->DebugLine( colorGreen, physicsObj.GetOrigin(), position, gameLocal.msec );
1910 		gameRenderWorld->DebugBounds( colorYellow, physicsObj.GetBounds(), position, gameLocal.msec );
1911 		if ( path.endEvent ) {
1912 			gameRenderWorld->DebugBounds( colorRed, physicsObj.GetBounds(), path.endPos, gameLocal.msec );
1913 		}
1914 	}
1915 
1916 	idThread::ReturnInt( path.endEvent == 0 );
1917 }
1918 
1919 /*
1920 =====================
1921 idAI::Event_TestMeleeAttack
1922 =====================
1923 */
Event_TestMeleeAttack(void)1924 void idAI::Event_TestMeleeAttack( void ) {
1925 	bool result = TestMelee();
1926 	idThread::ReturnInt( result );
1927 }
1928 
1929 /*
1930 =====================
1931 idAI::Event_TestAnimAttack
1932 =====================
1933 */
Event_TestAnimAttack(const char * animname)1934 void idAI::Event_TestAnimAttack( const char *animname ) {
1935 	int				anim;
1936 	predictedPath_t path;
1937 
1938 	anim = GetAnim( ANIMCHANNEL_LEGS, animname );
1939 	if ( !anim ) {
1940 		gameLocal.DWarning( "missing '%s' animation on '%s' (%s)", animname, name.c_str(), GetEntityDefName() );
1941 		idThread::ReturnInt( false );
1942 		return;
1943 	}
1944 
1945 	idAI::PredictPath( this, aas, physicsObj.GetOrigin(), animator.TotalMovementDelta( anim ), 1000, 1000, ( move.moveType == MOVETYPE_FLY ) ? SE_BLOCKED : ( SE_ENTER_OBSTACLE | SE_BLOCKED | SE_ENTER_LEDGE_AREA ), path );
1946 
1947 	idThread::ReturnInt( path.blockingEntity && ( path.blockingEntity == enemy.GetEntity() ) );
1948 }
1949 
1950 /*
1951 =====================
1952 idAI::Event_Shrivel
1953 =====================
1954 */
Event_Shrivel(float shrivel_time)1955 void idAI::Event_Shrivel( float shrivel_time ) {
1956 	float t;
1957 
1958 	if ( idThread::BeginMultiFrameEvent( this, &AI_Shrivel ) ) {
1959 		if ( shrivel_time <= 0.0f ) {
1960 			idThread::EndMultiFrameEvent( this, &AI_Shrivel );
1961 			return;
1962 		}
1963 
1964 		shrivel_rate = 0.001f / shrivel_time;
1965 		shrivel_start = gameLocal.time;
1966 	}
1967 
1968 	t = ( gameLocal.time - shrivel_start ) * shrivel_rate;
1969 	if ( t > 0.25f ) {
1970 		renderEntity.noShadow = true;
1971 	}
1972 	if ( t > 1.0f ) {
1973 		t = 1.0f;
1974 		idThread::EndMultiFrameEvent( this, &AI_Shrivel );
1975 	}
1976 
1977 	renderEntity.shaderParms[ SHADERPARM_MD5_SKINSCALE ] = 1.0f - t * 0.5f;
1978 	UpdateVisuals();
1979 }
1980 
1981 /*
1982 =====================
1983 idAI::Event_PreBurn
1984 =====================
1985 */
Event_PreBurn(void)1986 void idAI::Event_PreBurn( void ) {
1987 #ifdef _D3XP
1988 	// No grabbing after the burn has started!
1989 	noGrab = true;
1990 #endif
1991 
1992 	// for now this just turns shadows off
1993 	renderEntity.noShadow = true;
1994 }
1995 
1996 /*
1997 =====================
1998 idAI::Event_Burn
1999 =====================
2000 */
Event_Burn(void)2001 void idAI::Event_Burn( void ) {
2002 	renderEntity.shaderParms[ SHADERPARM_TIME_OF_DEATH ] = gameLocal.time * 0.001f;
2003 	SpawnParticles( "smoke_burnParticleSystem" );
2004 	UpdateVisuals();
2005 }
2006 
2007 /*
2008 =====================
2009 idAI::Event_ClearBurn
2010 =====================
2011 */
Event_ClearBurn(void)2012 void idAI::Event_ClearBurn( void ) {
2013 	renderEntity.noShadow = spawnArgs.GetBool( "noshadows" );
2014 	renderEntity.shaderParms[ SHADERPARM_TIME_OF_DEATH ] = 0.0f;
2015 	UpdateVisuals();
2016 }
2017 
2018 /*
2019 =====================
2020 idAI::Event_SetSmokeVisibility
2021 =====================
2022 */
Event_SetSmokeVisibility(int num,int on)2023 void idAI::Event_SetSmokeVisibility( int num, int on ) {
2024 	int i;
2025 	int time;
2026 
2027 	if ( num >= particles.Num() ) {
2028 		gameLocal.Warning( "Particle #%d out of range (%d particles) on entity '%s'", num, particles.Num(), name.c_str() );
2029 		return;
2030 	}
2031 
2032 	if ( on != 0 ) {
2033 		time = gameLocal.time;
2034 		BecomeActive( TH_UPDATEPARTICLES );
2035 	} else {
2036 		time = 0;
2037 	}
2038 
2039 	if ( num >= 0 ) {
2040 		particles[ num ].time = time;
2041 	} else {
2042 		for ( i = 0; i < particles.Num(); i++ ) {
2043 			particles[ i ].time = time;
2044 		}
2045 	}
2046 
2047 	UpdateVisuals();
2048 }
2049 
2050 /*
2051 =====================
2052 idAI::Event_NumSmokeEmitters
2053 =====================
2054 */
Event_NumSmokeEmitters(void)2055 void idAI::Event_NumSmokeEmitters( void ) {
2056 	idThread::ReturnInt( particles.Num() );
2057 }
2058 
2059 /*
2060 =====================
2061 idAI::Event_StopThinking
2062 =====================
2063 */
Event_StopThinking(void)2064 void idAI::Event_StopThinking( void ) {
2065 	BecomeInactive( TH_THINK );
2066 	idThread *thread = idThread::CurrentThread();
2067 	if ( thread ) {
2068 		thread->DoneProcessing();
2069 	}
2070 }
2071 
2072 /*
2073 =====================
2074 idAI::Event_GetTurnDelta
2075 =====================
2076 */
Event_GetTurnDelta(void)2077 void idAI::Event_GetTurnDelta( void ) {
2078 	float amount;
2079 
2080 	if ( turnRate ) {
2081 		amount = idMath::AngleNormalize180( ideal_yaw - current_yaw );
2082 		idThread::ReturnFloat( amount );
2083 	} else {
2084 		idThread::ReturnFloat( 0.0f );
2085 	}
2086 }
2087 
2088 /*
2089 =====================
2090 idAI::Event_GetMoveType
2091 =====================
2092 */
Event_GetMoveType(void)2093 void idAI::Event_GetMoveType( void ) {
2094 	idThread::ReturnInt( move.moveType );
2095 }
2096 
2097 /*
2098 =====================
2099 idAI::Event_SetMoveTypes
2100 =====================
2101 */
Event_SetMoveType(int moveType)2102 void idAI::Event_SetMoveType( int moveType ) {
2103 	if ( ( moveType < 0 ) || ( moveType >= NUM_MOVETYPES ) ) {
2104 		gameLocal.Error( "Invalid movetype %d", moveType );
2105 	}
2106 
2107 	move.moveType = static_cast<moveType_t>( moveType );
2108 	if ( move.moveType == MOVETYPE_FLY ) {
2109 		travelFlags = TFL_WALK|TFL_AIR|TFL_FLY;
2110 	} else {
2111 		travelFlags = TFL_WALK|TFL_AIR;
2112 	}
2113 }
2114 
2115 /*
2116 =====================
2117 idAI::Event_SaveMove
2118 =====================
2119 */
Event_SaveMove(void)2120 void idAI::Event_SaveMove( void ) {
2121 	savedMove = move;
2122 }
2123 
2124 /*
2125 =====================
2126 idAI::Event_RestoreMove
2127 =====================
2128 */
Event_RestoreMove(void)2129 void idAI::Event_RestoreMove( void ) {
2130 	idVec3 goalPos;
2131 	idVec3 dest;
2132 
2133 	switch( savedMove.moveCommand ) {
2134 	case MOVE_NONE :
2135 		StopMove( savedMove.moveStatus );
2136 		break;
2137 
2138 	case MOVE_FACE_ENEMY :
2139 		FaceEnemy();
2140 		break;
2141 
2142 	case MOVE_FACE_ENTITY :
2143 		FaceEntity( savedMove.goalEntity.GetEntity() );
2144 		break;
2145 
2146 	case MOVE_TO_ENEMY :
2147 		MoveToEnemy();
2148 		break;
2149 
2150 	case MOVE_TO_ENEMYHEIGHT :
2151 		MoveToEnemyHeight();
2152 		break;
2153 
2154 	case MOVE_TO_ENTITY :
2155 		MoveToEntity( savedMove.goalEntity.GetEntity() );
2156 		break;
2157 
2158 	case MOVE_OUT_OF_RANGE :
2159 		MoveOutOfRange( savedMove.goalEntity.GetEntity(), savedMove.range );
2160 		break;
2161 
2162 	case MOVE_TO_ATTACK_POSITION :
2163 		MoveToAttackPosition( savedMove.goalEntity.GetEntity(), savedMove.anim );
2164 		break;
2165 
2166 	case MOVE_TO_COVER :
2167 		MoveToCover( savedMove.goalEntity.GetEntity(), lastVisibleEnemyPos );
2168 		break;
2169 
2170 	case MOVE_TO_POSITION :
2171 		MoveToPosition( savedMove.moveDest );
2172 		break;
2173 
2174 	case MOVE_TO_POSITION_DIRECT :
2175 		DirectMoveToPosition( savedMove.moveDest );
2176 		break;
2177 
2178 	case MOVE_SLIDE_TO_POSITION :
2179 		SlideToPosition( savedMove.moveDest, savedMove.duration );
2180 		break;
2181 
2182 	case MOVE_WANDER :
2183 		WanderAround();
2184 		break;
2185 	}
2186 
2187 	if ( GetMovePos( goalPos ) ) {
2188 		CheckObstacleAvoidance( goalPos, dest );
2189 	}
2190 }
2191 
2192 /*
2193 =====================
2194 idAI::Event_AllowMovement
2195 =====================
2196 */
Event_AllowMovement(float flag)2197 void idAI::Event_AllowMovement( float flag ) {
2198 	allowMove = ( flag != 0.0f );
2199 }
2200 
2201 /*
2202 =====================
2203 idAI::Event_JumpFrame
2204 =====================
2205 */
Event_JumpFrame(void)2206 void idAI::Event_JumpFrame( void ) {
2207 	AI_JUMP = true;
2208 }
2209 
2210 /*
2211 =====================
2212 idAI::Event_EnableClip
2213 =====================
2214 */
Event_EnableClip(void)2215 void idAI::Event_EnableClip( void ) {
2216 	physicsObj.SetClipMask( MASK_MONSTERSOLID );
2217 	disableGravity = false;
2218 }
2219 
2220 /*
2221 =====================
2222 idAI::Event_DisableClip
2223 =====================
2224 */
Event_DisableClip(void)2225 void idAI::Event_DisableClip( void ) {
2226 	physicsObj.SetClipMask( 0 );
2227 	disableGravity = true;
2228 }
2229 
2230 /*
2231 =====================
2232 idAI::Event_EnableGravity
2233 =====================
2234 */
Event_EnableGravity(void)2235 void idAI::Event_EnableGravity( void ) {
2236 	disableGravity = false;
2237 }
2238 
2239 /*
2240 =====================
2241 idAI::Event_DisableGravity
2242 =====================
2243 */
Event_DisableGravity(void)2244 void idAI::Event_DisableGravity( void ) {
2245 	disableGravity = true;
2246 }
2247 
2248 /*
2249 =====================
2250 idAI::Event_EnableAFPush
2251 =====================
2252 */
Event_EnableAFPush(void)2253 void idAI::Event_EnableAFPush( void ) {
2254 	af_push_moveables = true;
2255 }
2256 
2257 /*
2258 =====================
2259 idAI::Event_DisableAFPush
2260 =====================
2261 */
Event_DisableAFPush(void)2262 void idAI::Event_DisableAFPush( void ) {
2263 	af_push_moveables = false;
2264 }
2265 
2266 /*
2267 =====================
2268 idAI::Event_SetFlySpeed
2269 =====================
2270 */
Event_SetFlySpeed(float speed)2271 void idAI::Event_SetFlySpeed( float speed ) {
2272 	if ( move.speed == fly_speed ) {
2273 		move.speed = speed;
2274 	}
2275 	fly_speed = speed;
2276 }
2277 
2278 /*
2279 ================
2280 idAI::Event_SetFlyOffset
2281 ================
2282 */
Event_SetFlyOffset(int offset)2283 void idAI::Event_SetFlyOffset( int offset ) {
2284 	fly_offset = offset;
2285 }
2286 
2287 /*
2288 ================
2289 idAI::Event_ClearFlyOffset
2290 ================
2291 */
Event_ClearFlyOffset(void)2292 void idAI::Event_ClearFlyOffset( void ) {
2293 	spawnArgs.GetInt( "fly_offset",	"0", fly_offset );
2294 }
2295 
2296 /*
2297 =====================
2298 idAI::Event_GetClosestHiddenTarget
2299 =====================
2300 */
Event_GetClosestHiddenTarget(const char * type)2301 void idAI::Event_GetClosestHiddenTarget( const char *type ) {
2302 	int	i;
2303 	idEntity *ent;
2304 	idEntity *bestEnt;
2305 	float time;
2306 	float bestTime;
2307 	const idVec3 &org = physicsObj.GetOrigin();
2308 	idActor *enemyEnt = enemy.GetEntity();
2309 
2310 	if ( !enemyEnt ) {
2311 		// no enemy to hide from
2312 		idThread::ReturnEntity( NULL );
2313 		return;
2314 	}
2315 
2316 	if ( targets.Num() == 1 ) {
2317 		ent = targets[ 0 ].GetEntity();
2318 		if ( ent && idStr::Cmp( ent->GetEntityDefName(), type ) == 0 ) {
2319 			if ( !EntityCanSeePos( enemyEnt, lastVisibleEnemyPos, ent->GetPhysics()->GetOrigin() ) ) {
2320 				idThread::ReturnEntity( ent );
2321 				return;
2322 			}
2323 		}
2324 		idThread::ReturnEntity( NULL );
2325 		return;
2326 	}
2327 
2328 	bestEnt = NULL;
2329 	bestTime = idMath::INFINITY;
2330 	for( i = 0; i < targets.Num(); i++ ) {
2331 		ent = targets[ i ].GetEntity();
2332 		if ( ent && idStr::Cmp( ent->GetEntityDefName(), type ) == 0 ) {
2333 			const idVec3 &destOrg = ent->GetPhysics()->GetOrigin();
2334 			time = TravelDistance( org, destOrg );
2335 			if ( ( time >= 0.0f ) && ( time < bestTime ) ) {
2336 				if ( !EntityCanSeePos( enemyEnt, lastVisibleEnemyPos, destOrg ) ) {
2337 					bestEnt = ent;
2338 					bestTime = time;
2339 				}
2340 			}
2341 		}
2342 	}
2343 	idThread::ReturnEntity( bestEnt );
2344 }
2345 
2346 /*
2347 =====================
2348 idAI::Event_GetRandomTarget
2349 =====================
2350 */
Event_GetRandomTarget(const char * type)2351 void idAI::Event_GetRandomTarget( const char *type ) {
2352 	int	i;
2353 	int	num;
2354 	int which;
2355 	idEntity *ent;
2356 	idEntity *ents[ MAX_GENTITIES ];
2357 
2358 	num = 0;
2359 	for( i = 0; i < targets.Num(); i++ ) {
2360 		ent = targets[ i ].GetEntity();
2361 		if ( ent && idStr::Cmp( ent->GetEntityDefName(), type ) == 0 ) {
2362 			ents[ num++ ] = ent;
2363 			if ( num >= MAX_GENTITIES ) {
2364 				break;
2365 			}
2366 		}
2367 	}
2368 
2369 	if ( !num ) {
2370 		idThread::ReturnEntity( NULL );
2371 		return;
2372 	}
2373 
2374 	which = gameLocal.random.RandomInt( num );
2375 	idThread::ReturnEntity( ents[ which ] );
2376 }
2377 
2378 /*
2379 ================
2380 idAI::Event_TravelDistanceToPoint
2381 ================
2382 */
Event_TravelDistanceToPoint(const idVec3 & pos)2383 void idAI::Event_TravelDistanceToPoint( const idVec3 &pos ) {
2384 	float time;
2385 
2386 	time = TravelDistance( physicsObj.GetOrigin(), pos );
2387 	idThread::ReturnFloat( time );
2388 }
2389 
2390 /*
2391 ================
2392 idAI::Event_TravelDistanceToEntity
2393 ================
2394 */
Event_TravelDistanceToEntity(idEntity * ent)2395 void idAI::Event_TravelDistanceToEntity( idEntity *ent ) {
2396 	float time;
2397 
2398 	time = TravelDistance( physicsObj.GetOrigin(), ent->GetPhysics()->GetOrigin() );
2399 	idThread::ReturnFloat( time );
2400 }
2401 
2402 /*
2403 ================
2404 idAI::Event_TravelDistanceBetweenPoints
2405 ================
2406 */
Event_TravelDistanceBetweenPoints(const idVec3 & source,const idVec3 & dest)2407 void idAI::Event_TravelDistanceBetweenPoints( const idVec3 &source, const idVec3 &dest ) {
2408 	float time;
2409 
2410 	time = TravelDistance( source, dest );
2411 	idThread::ReturnFloat( time );
2412 }
2413 
2414 /*
2415 ================
2416 idAI::Event_TravelDistanceBetweenEntities
2417 ================
2418 */
Event_TravelDistanceBetweenEntities(idEntity * source,idEntity * dest)2419 void idAI::Event_TravelDistanceBetweenEntities( idEntity *source, idEntity *dest ) {
2420 	float time;
2421 
2422 	assert( source );
2423 	assert( dest );
2424 	time = TravelDistance( source->GetPhysics()->GetOrigin(), dest->GetPhysics()->GetOrigin() );
2425 	idThread::ReturnFloat( time );
2426 }
2427 
2428 /*
2429 =====================
2430 idAI::Event_LookAtEntity
2431 =====================
2432 */
Event_LookAtEntity(idEntity * ent,float duration)2433 void idAI::Event_LookAtEntity( idEntity *ent, float duration ) {
2434 	if ( ent == this ) {
2435 		ent = NULL;
2436 	}
2437 
2438 	if ( ( ent != focusEntity.GetEntity() ) || ( focusTime < gameLocal.time ) ) {
2439 		focusEntity	= ent;
2440 		alignHeadTime = gameLocal.time;
2441 		forceAlignHeadTime = gameLocal.time + SEC2MS( 1 );
2442 		blink_time = 0;
2443 	}
2444 
2445 	focusTime = gameLocal.time + SEC2MS( duration );
2446 }
2447 
2448 /*
2449 =====================
2450 idAI::Event_LookAtEnemy
2451 =====================
2452 */
Event_LookAtEnemy(float duration)2453 void idAI::Event_LookAtEnemy( float duration ) {
2454 	idActor *enemyEnt;
2455 
2456 	enemyEnt = enemy.GetEntity();
2457 	if ( ( enemyEnt != focusEntity.GetEntity() ) || ( focusTime < gameLocal.time ) ) {
2458 		focusEntity	= enemyEnt;
2459 		alignHeadTime = gameLocal.time;
2460 		forceAlignHeadTime = gameLocal.time + SEC2MS( 1 );
2461 		blink_time = 0;
2462 	}
2463 
2464 	focusTime = gameLocal.time + SEC2MS( duration );
2465 }
2466 
2467 /*
2468 ===============
2469 idAI::Event_SetJointMod
2470 ===============
2471 */
Event_SetJointMod(int allow)2472 void idAI::Event_SetJointMod( int allow ) {
2473 	allowJointMod = ( allow != 0 );
2474 }
2475 
2476 /*
2477 ================
2478 idAI::Event_ThrowMoveable
2479 ================
2480 */
Event_ThrowMoveable(void)2481 void idAI::Event_ThrowMoveable( void ) {
2482 	idEntity *ent;
2483 	idEntity *moveable = NULL;
2484 
2485 	for ( ent = GetNextTeamEntity(); ent != NULL; ent = ent->GetNextTeamEntity() ) {
2486 		if ( ent->GetBindMaster() == this && ent->IsType( idMoveable::Type ) ) {
2487 			moveable = ent;
2488 			break;
2489 		}
2490 	}
2491 	if ( moveable ) {
2492 		moveable->Unbind();
2493 		moveable->PostEventMS( &EV_SetOwner, 200, 0 );
2494 	}
2495 }
2496 
2497 /*
2498 ================
2499 idAI::Event_ThrowAF
2500 ================
2501 */
Event_ThrowAF(void)2502 void idAI::Event_ThrowAF( void ) {
2503 	idEntity *ent;
2504 	idEntity *af = NULL;
2505 
2506 	for ( ent = GetNextTeamEntity(); ent != NULL; ent = ent->GetNextTeamEntity() ) {
2507 		if ( ent->GetBindMaster() == this && ent->IsType( idAFEntity_Base::Type ) ) {
2508 			af = ent;
2509 			break;
2510 		}
2511 	}
2512 	if ( af ) {
2513 		af->Unbind();
2514 		af->PostEventMS( &EV_SetOwner, 200, 0 );
2515 	}
2516 }
2517 
2518 /*
2519 ================
2520 idAI::Event_SetAngles
2521 ================
2522 */
Event_SetAngles(idAngles const & ang)2523 void idAI::Event_SetAngles( idAngles const &ang ) {
2524 	current_yaw = ang.yaw;
2525 	viewAxis = idAngles( 0, current_yaw, 0 ).ToMat3();
2526 }
2527 
2528 /*
2529 ================
2530 idAI::Event_GetAngles
2531 ================
2532 */
Event_GetAngles(void)2533 void idAI::Event_GetAngles( void ) {
2534 	idThread::ReturnVector( idVec3( 0.0f, current_yaw, 0.0f ) );
2535 }
2536 
2537 /*
2538 ================
2539 idAI::Event_RealKill
2540 ================
2541 */
Event_RealKill(void)2542 void idAI::Event_RealKill( void ) {
2543 	health = 0;
2544 
2545 	if ( af.IsLoaded() ) {
2546 		// clear impacts
2547 		af.Rest();
2548 
2549 		// physics is turned off by calling af.Rest()
2550 		BecomeActive( TH_PHYSICS );
2551 	}
2552 
2553 	Killed( this, this, 0, vec3_zero, INVALID_JOINT );
2554 }
2555 
2556 /*
2557 ================
2558 idAI::Event_Kill
2559 ================
2560 */
Event_Kill(void)2561 void idAI::Event_Kill( void ) {
2562 	PostEventMS( &AI_RealKill, 0 );
2563 }
2564 
2565 /*
2566 ================
2567 idAI::Event_WakeOnFlashlight
2568 ================
2569 */
Event_WakeOnFlashlight(int enable)2570 void idAI::Event_WakeOnFlashlight( int enable ) {
2571 	wakeOnFlashlight = ( enable != 0 );
2572 }
2573 
2574 /*
2575 ================
2576 idAI::Event_LocateEnemy
2577 ================
2578 */
Event_LocateEnemy(void)2579 void idAI::Event_LocateEnemy( void ) {
2580 	idActor *enemyEnt;
2581 	int areaNum;
2582 
2583 	enemyEnt = enemy.GetEntity();
2584 	if ( !enemyEnt ) {
2585 		return;
2586 	}
2587 
2588 	enemyEnt->GetAASLocation( aas, lastReachableEnemyPos, areaNum );
2589 	SetEnemyPosition();
2590 	UpdateEnemyPosition();
2591 }
2592 
2593 /*
2594 ================
2595 idAI::Event_KickObstacles
2596 ================
2597 */
Event_KickObstacles(idEntity * kickEnt,float force)2598 void idAI::Event_KickObstacles( idEntity *kickEnt, float force ) {
2599 	idVec3 dir;
2600 	idEntity *obEnt;
2601 
2602 	if ( kickEnt ) {
2603 		obEnt = kickEnt;
2604 	} else {
2605 		obEnt = move.obstacle.GetEntity();
2606 	}
2607 
2608 	if ( obEnt ) {
2609 		dir = obEnt->GetPhysics()->GetOrigin() - physicsObj.GetOrigin();
2610 		dir.Normalize();
2611 	} else {
2612 		dir = viewAxis[ 0 ];
2613 	}
2614 	KickObstacles( dir, force, obEnt );
2615 }
2616 
2617 /*
2618 ================
2619 idAI::Event_GetObstacle
2620 ================
2621 */
Event_GetObstacle(void)2622 void idAI::Event_GetObstacle( void ) {
2623 	idThread::ReturnEntity( move.obstacle.GetEntity() );
2624 }
2625 
2626 /*
2627 ================
2628 idAI::Event_PushPointIntoAAS
2629 ================
2630 */
Event_PushPointIntoAAS(const idVec3 & pos)2631 void idAI::Event_PushPointIntoAAS( const idVec3 &pos ) {
2632 	int		areaNum;
2633 	idVec3	newPos;
2634 
2635 	areaNum = PointReachableAreaNum( pos );
2636 	if ( areaNum ) {
2637 		newPos = pos;
2638 		aas->PushPointIntoAreaNum( areaNum, newPos );
2639 		idThread::ReturnVector( newPos );
2640 	} else {
2641 		idThread::ReturnVector( pos );
2642 	}
2643 }
2644 
2645 
2646 /*
2647 ================
2648 idAI::Event_GetTurnRate
2649 ================
2650 */
Event_GetTurnRate(void)2651 void idAI::Event_GetTurnRate( void ) {
2652 	idThread::ReturnFloat( turnRate );
2653 }
2654 
2655 /*
2656 ================
2657 idAI::Event_SetTurnRate
2658 ================
2659 */
Event_SetTurnRate(float rate)2660 void idAI::Event_SetTurnRate( float rate ) {
2661 	turnRate = rate;
2662 }
2663 
2664 /*
2665 ================
2666 idAI::Event_AnimTurn
2667 ================
2668 */
Event_AnimTurn(float angles)2669 void idAI::Event_AnimTurn( float angles ) {
2670 	turnVel = 0.0f;
2671 	anim_turn_angles = angles;
2672 	if ( angles ) {
2673 		anim_turn_yaw = current_yaw;
2674 		anim_turn_amount = idMath::Fabs( idMath::AngleNormalize180( current_yaw - ideal_yaw ) );
2675 		if ( anim_turn_amount > anim_turn_angles ) {
2676 			anim_turn_amount = anim_turn_angles;
2677 		}
2678 	} else {
2679 		anim_turn_amount = 0.0f;
2680 		animator.CurrentAnim( ANIMCHANNEL_LEGS )->SetSyncedAnimWeight( 0, 1.0f );
2681 		animator.CurrentAnim( ANIMCHANNEL_LEGS )->SetSyncedAnimWeight( 1, 0.0f );
2682 		animator.CurrentAnim( ANIMCHANNEL_TORSO )->SetSyncedAnimWeight( 0, 1.0f );
2683 		animator.CurrentAnim( ANIMCHANNEL_TORSO )->SetSyncedAnimWeight( 1, 0.0f );
2684 	}
2685 }
2686 
2687 /*
2688 ================
2689 idAI::Event_AllowHiddenMovement
2690 ================
2691 */
Event_AllowHiddenMovement(int enable)2692 void idAI::Event_AllowHiddenMovement( int enable ) {
2693 	allowHiddenMovement = ( enable != 0 );
2694 }
2695 
2696 /*
2697 ================
2698 idAI::Event_TriggerParticles
2699 ================
2700 */
Event_TriggerParticles(const char * jointName)2701 void idAI::Event_TriggerParticles( const char *jointName ) {
2702 	TriggerParticles( jointName );
2703 }
2704 
2705 /*
2706 =====================
2707 idAI::Event_FindActorsInBounds
2708 =====================
2709 */
Event_FindActorsInBounds(const idVec3 & mins,const idVec3 & maxs)2710 void idAI::Event_FindActorsInBounds( const idVec3 &mins, const idVec3 &maxs ) {
2711 	idEntity *	ent;
2712 	idEntity *	entityList[ MAX_GENTITIES ];
2713 	int			numListedEntities;
2714 	int			i;
2715 
2716 	numListedEntities = gameLocal.clip.EntitiesTouchingBounds( idBounds( mins, maxs ), CONTENTS_BODY, entityList, MAX_GENTITIES );
2717 	for( i = 0; i < numListedEntities; i++ ) {
2718 		ent = entityList[ i ];
2719 		if ( ent != this && !ent->IsHidden() && ( ent->health > 0 ) && ent->IsType( idActor::Type ) ) {
2720 			idThread::ReturnEntity( ent );
2721 			return;
2722 		}
2723 	}
2724 
2725 	idThread::ReturnEntity( NULL );
2726 }
2727 
2728 /*
2729 ================
2730 idAI::Event_CanReachPosition
2731 ================
2732 */
Event_CanReachPosition(const idVec3 & pos)2733 void idAI::Event_CanReachPosition( const idVec3 &pos ) {
2734 	aasPath_t	path;
2735 	int			toAreaNum;
2736 	int			areaNum;
2737 
2738 	toAreaNum = PointReachableAreaNum( pos );
2739 	areaNum	= PointReachableAreaNum( physicsObj.GetOrigin() );
2740 	if ( !toAreaNum || !PathToGoal( path, areaNum, physicsObj.GetOrigin(), toAreaNum, pos ) ) {
2741 		idThread::ReturnInt( false );
2742 	} else {
2743 		idThread::ReturnInt( true );
2744 	}
2745 }
2746 
2747 /*
2748 ================
2749 idAI::Event_CanReachEntity
2750 ================
2751 */
Event_CanReachEntity(idEntity * ent)2752 void idAI::Event_CanReachEntity( idEntity *ent ) {
2753 	aasPath_t	path;
2754 	int			toAreaNum;
2755 	int			areaNum;
2756 	idVec3		pos;
2757 
2758 	if ( !ent ) {
2759 		idThread::ReturnInt( false );
2760 		return;
2761 	}
2762 
2763 	if ( move.moveType != MOVETYPE_FLY ) {
2764 		if ( !ent->GetFloorPos( 64.0f, pos ) ) {
2765 			idThread::ReturnInt( false );
2766 			return;
2767 		}
2768 		if ( ent->IsType( idActor::Type ) && static_cast<idActor *>( ent )->OnLadder() ) {
2769 			idThread::ReturnInt( false );
2770 			return;
2771 		}
2772 	} else {
2773 		pos = ent->GetPhysics()->GetOrigin();
2774 	}
2775 
2776 	toAreaNum = PointReachableAreaNum( pos );
2777 	if ( !toAreaNum ) {
2778 		idThread::ReturnInt( false );
2779 		return;
2780 	}
2781 
2782 	const idVec3 &org = physicsObj.GetOrigin();
2783 	areaNum	= PointReachableAreaNum( org );
2784 	if ( !toAreaNum || !PathToGoal( path, areaNum, org, toAreaNum, pos ) ) {
2785 		idThread::ReturnInt( false );
2786 	} else {
2787 		idThread::ReturnInt( true );
2788 	}
2789 }
2790 
2791 /*
2792 ================
2793 idAI::Event_CanReachEnemy
2794 ================
2795 */
Event_CanReachEnemy(void)2796 void idAI::Event_CanReachEnemy( void ) {
2797 	aasPath_t	path;
2798 	int			toAreaNum;
2799 	int			areaNum;
2800 	idVec3		pos;
2801 	idActor		*enemyEnt;
2802 
2803 	enemyEnt = enemy.GetEntity();
2804 	if ( !enemyEnt ) {
2805 		idThread::ReturnInt( false );
2806 		return;
2807 	}
2808 
2809 	if ( move.moveType != MOVETYPE_FLY ) {
2810 		if ( enemyEnt->OnLadder() ) {
2811 			idThread::ReturnInt( false );
2812 			return;
2813 		}
2814 		enemyEnt->GetAASLocation( aas, pos, toAreaNum );
2815 	}  else {
2816 		pos = enemyEnt->GetPhysics()->GetOrigin();
2817 		toAreaNum = PointReachableAreaNum( pos );
2818 	}
2819 
2820 	if ( !toAreaNum ) {
2821 		idThread::ReturnInt( false );
2822 		return;
2823 	}
2824 
2825 	const idVec3 &org = physicsObj.GetOrigin();
2826 	areaNum	= PointReachableAreaNum( org );
2827 	if ( !PathToGoal( path, areaNum, org, toAreaNum, pos ) ) {
2828 		idThread::ReturnInt( false );
2829 	} else {
2830 		idThread::ReturnInt( true );
2831 	}
2832 }
2833 
2834 /*
2835 ================
2836 idAI::Event_GetReachableEntityPosition
2837 ================
2838 */
Event_GetReachableEntityPosition(idEntity * ent)2839 void idAI::Event_GetReachableEntityPosition( idEntity *ent ) {
2840 	int		toAreaNum;
2841 	idVec3	pos;
2842 
2843 	if ( move.moveType != MOVETYPE_FLY ) {
2844 		if ( !ent->GetFloorPos( 64.0f, pos ) ) {
2845 			// NOTE: not a good way to return 'false'
2846 			return idThread::ReturnVector( vec3_zero );
2847 		}
2848 		if ( ent->IsType( idActor::Type ) && static_cast<idActor *>( ent )->OnLadder() ) {
2849 			// NOTE: not a good way to return 'false'
2850 			return idThread::ReturnVector( vec3_zero );
2851 		}
2852 	} else {
2853 		pos = ent->GetPhysics()->GetOrigin();
2854 	}
2855 
2856 	if ( aas ) {
2857 		toAreaNum = PointReachableAreaNum( pos );
2858 		aas->PushPointIntoAreaNum( toAreaNum, pos );
2859 	}
2860 
2861 	idThread::ReturnVector( pos );
2862 }
2863 
2864 #ifdef _D3XP
2865 /*
2866 ================
2867 idAI::Event_MoveToPositionDirect
2868 ================
2869 */
Event_MoveToPositionDirect(const idVec3 & pos)2870 void idAI::Event_MoveToPositionDirect( const idVec3 &pos ) {
2871 	StopMove( MOVE_STATUS_DONE );
2872 	DirectMoveToPosition( pos );
2873 }
2874 
2875 /*
2876 ================
2877 idAI::Event_AvoidObstacles
2878 ================
2879 */
Event_AvoidObstacles(int ignore)2880 void idAI::Event_AvoidObstacles( int ignore) {
2881 	ignore_obstacles = (ignore == 1) ? false : true;
2882 }
2883 
2884 /*
2885 ================
2886 idAI::Event_TriggerFX
2887 ================
2888 */
Event_TriggerFX(const char * joint,const char * fx)2889 void idAI::Event_TriggerFX( const char* joint, const char* fx ) {
2890 	TriggerFX(joint, fx);
2891 }
2892 
Event_StartEmitter(const char * name,const char * joint,const char * particle)2893 void idAI::Event_StartEmitter( const char* name, const char* joint, const char* particle ) {
2894 	idEntity *ent = StartEmitter(name, joint, particle);
2895 	idThread::ReturnEntity(ent);
2896 }
2897 
Event_GetEmitter(const char * name)2898 void idAI::Event_GetEmitter( const char* name ) {
2899 	idThread::ReturnEntity(GetEmitter(name));
2900 }
2901 
Event_StopEmitter(const char * name)2902 void idAI::Event_StopEmitter( const char* name ) {
2903 	StopEmitter(name);
2904 }
2905 
2906 #endif
2907