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