doom3-gpl
Doom 3 GPL source release
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Pages
AI.cpp
Go to the documentation of this file.
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 "../../idlib/precompiled.h"
30 #pragma hdrstop
31 
32 #include "../Game_local.h"
33 
34 static const char *moveCommandString[ NUM_MOVE_COMMANDS ] = {
35  "MOVE_NONE",
36  "MOVE_FACE_ENEMY",
37  "MOVE_FACE_ENTITY",
38  "MOVE_TO_ENEMY",
39  "MOVE_TO_ENEMYHEIGHT",
40  "MOVE_TO_ENTITY",
41  "MOVE_OUT_OF_RANGE",
42  "MOVE_TO_ATTACK_POSITION",
43  "MOVE_TO_COVER",
44  "MOVE_TO_POSITION",
45  "MOVE_TO_POSITION_DIRECT",
46  "MOVE_SLIDE_TO_POSITION",
47  "MOVE_WANDER"
48 };
49 
50 /*
51 =====================
52 idMoveState::idMoveState
53 =====================
54 */
59  moveDest.Zero();
60  moveDir.Set( 1.0f, 0.0f, 0.0f );
61  goalEntity = NULL;
63  toAreaNum = 0;
64  startTime = 0;
65  duration = 0;
66  speed = 0.0f;
67  range = 0.0f;
68  wanderYaw = 0;
69  nextWanderTime = 0;
70  blockTime = 0;
71  obstacle = NULL;
73  lastMoveTime = 0;
74  anim = 0;
75 }
76 
77 /*
78 =====================
79 idMoveState::Save
80 =====================
81 */
82 void idMoveState::Save( idSaveGame *savefile ) const {
83  savefile->WriteInt( (int)moveType );
84  savefile->WriteInt( (int)moveCommand );
85  savefile->WriteInt( (int)moveStatus );
86  savefile->WriteVec3( moveDest );
87  savefile->WriteVec3( moveDir );
88  goalEntity.Save( savefile );
89  savefile->WriteVec3( goalEntityOrigin );
90  savefile->WriteInt( toAreaNum );
91  savefile->WriteInt( startTime );
92  savefile->WriteInt( duration );
93  savefile->WriteFloat( speed );
94  savefile->WriteFloat( range );
95  savefile->WriteFloat( wanderYaw );
96  savefile->WriteInt( nextWanderTime );
97  savefile->WriteInt( blockTime );
98  obstacle.Save( savefile );
99  savefile->WriteVec3( lastMoveOrigin );
100  savefile->WriteInt( lastMoveTime );
101  savefile->WriteInt( anim );
102 }
103 
104 /*
105 =====================
106 idMoveState::Restore
107 =====================
108 */
110  savefile->ReadInt( (int &)moveType );
111  savefile->ReadInt( (int &)moveCommand );
112  savefile->ReadInt( (int &)moveStatus );
113  savefile->ReadVec3( moveDest );
114  savefile->ReadVec3( moveDir );
115  goalEntity.Restore( savefile );
116  savefile->ReadVec3( goalEntityOrigin );
117  savefile->ReadInt( toAreaNum );
118  savefile->ReadInt( startTime );
119  savefile->ReadInt( duration );
120  savefile->ReadFloat( speed );
121  savefile->ReadFloat( range );
122  savefile->ReadFloat( wanderYaw );
123  savefile->ReadInt( nextWanderTime );
124  savefile->ReadInt( blockTime );
125  obstacle.Restore( savefile );
126  savefile->ReadVec3( lastMoveOrigin );
127  savefile->ReadInt( lastMoveTime );
128  savefile->ReadInt( anim );
129 }
130 
131 /*
132 ============
133 idAASFindCover::idAASFindCover
134 ============
135 */
136 idAASFindCover::idAASFindCover( const idVec3 &hideFromPos ) {
137  int numPVSAreas;
138  idBounds bounds( hideFromPos - idVec3( 16, 16, 0 ), hideFromPos + idVec3( 16, 16, 64 ) );
139 
140  // setup PVS
141  numPVSAreas = gameLocal.pvs.GetPVSAreas( bounds, PVSAreas, idEntity::MAX_PVS_AREAS );
142  hidePVS = gameLocal.pvs.SetupCurrentPVS( PVSAreas, numPVSAreas );
143 }
144 
145 /*
146 ============
147 idAASFindCover::~idAASFindCover
148 ============
149 */
152 }
153 
154 /*
155 ============
156 idAASFindCover::TestArea
157 ============
158 */
159 bool idAASFindCover::TestArea( const idAAS *aas, int areaNum ) {
160  idVec3 areaCenter;
161  int numPVSAreas;
163 
164  areaCenter = aas->AreaCenter( areaNum );
165  areaCenter[ 2 ] += 1.0f;
166 
167  numPVSAreas = gameLocal.pvs.GetPVSAreas( idBounds( areaCenter ).Expand( 16.0f ), PVSAreas, idEntity::MAX_PVS_AREAS );
168  if ( !gameLocal.pvs.InCurrentPVS( hidePVS, PVSAreas, numPVSAreas ) ) {
169  return true;
170  }
171 
172  return false;
173 }
174 
175 /*
176 ============
177 idAASFindAreaOutOfRange::idAASFindAreaOutOfRange
178 ============
179 */
180 idAASFindAreaOutOfRange::idAASFindAreaOutOfRange( const idVec3 &targetPos, float maxDist ) {
181  this->targetPos = targetPos;
182  this->maxDistSqr = maxDist * maxDist;
183 }
184 
185 /*
186 ============
187 idAASFindAreaOutOfRange::TestArea
188 ============
189 */
190 bool idAASFindAreaOutOfRange::TestArea( const idAAS *aas, int areaNum ) {
191  const idVec3 &areaCenter = aas->AreaCenter( areaNum );
192  trace_t trace;
193  float dist;
194 
195  dist = ( targetPos.ToVec2() - areaCenter.ToVec2() ).LengthSqr();
196 
197  if ( ( maxDistSqr > 0.0f ) && ( dist < maxDistSqr ) ) {
198  return false;
199  }
200 
201  gameLocal.clip.TracePoint( trace, targetPos, areaCenter + idVec3( 0.0f, 0.0f, 1.0f ), MASK_OPAQUE, NULL );
202  if ( trace.fraction < 1.0f ) {
203  return false;
204  }
205 
206  return true;
207 }
208 
209 /*
210 ============
211 idAASFindAttackPosition::idAASFindAttackPosition
212 ============
213 */
214 idAASFindAttackPosition::idAASFindAttackPosition( const idAI *self, const idMat3 &gravityAxis, idEntity *target, const idVec3 &targetPos, const idVec3 &fireOffset ) {
215  int numPVSAreas;
216 
217  this->target = target;
218  this->targetPos = targetPos;
219  this->fireOffset = fireOffset;
220  this->self = self;
221  this->gravityAxis = gravityAxis;
222 
223  excludeBounds = idBounds( idVec3( -64.0, -64.0f, -8.0f ), idVec3( 64.0, 64.0f, 64.0f ) );
225 
226  // setup PVS
227  idBounds bounds( targetPos - idVec3( 16, 16, 0 ), targetPos + idVec3( 16, 16, 64 ) );
228  numPVSAreas = gameLocal.pvs.GetPVSAreas( bounds, PVSAreas, idEntity::MAX_PVS_AREAS );
229  targetPVS = gameLocal.pvs.SetupCurrentPVS( PVSAreas, numPVSAreas );
230 }
231 
232 /*
233 ============
234 idAASFindAttackPosition::~idAASFindAttackPosition
235 ============
236 */
239 }
240 
241 /*
242 ============
243 idAASFindAttackPosition::TestArea
244 ============
245 */
246 bool idAASFindAttackPosition::TestArea( const idAAS *aas, int areaNum ) {
247  idVec3 dir;
248  idVec3 local_dir;
249  idVec3 fromPos;
250  idMat3 axis;
251  idVec3 areaCenter;
252  int numPVSAreas;
254 
255  areaCenter = aas->AreaCenter( areaNum );
256  areaCenter[ 2 ] += 1.0f;
257 
258  if ( excludeBounds.ContainsPoint( areaCenter ) ) {
259  // too close to where we already are
260  return false;
261  }
262 
263  numPVSAreas = gameLocal.pvs.GetPVSAreas( idBounds( areaCenter ).Expand( 16.0f ), PVSAreas, idEntity::MAX_PVS_AREAS );
264  if ( !gameLocal.pvs.InCurrentPVS( targetPVS, PVSAreas, numPVSAreas ) ) {
265  return false;
266  }
267 
268  // calculate the world transform of the launch position
269  dir = targetPos - areaCenter;
270  gravityAxis.ProjectVector( dir, local_dir );
271  local_dir.z = 0.0f;
272  local_dir.ToVec2().Normalize();
273  axis = local_dir.ToMat3();
274  fromPos = areaCenter + fireOffset * axis;
275 
276  return self->GetAimDir( fromPos, target, self, dir );
277 }
278 
279 /*
280 =====================
281 idAI::idAI
282 =====================
283 */
285  aas = NULL;
287 
288  kickForce = 2048.0f;
289  ignore_obstacles = false;
290  blockedRadius = 0.0f;
291  blockedMoveTime = 750;
292  blockedAttackTime = 750;
293  turnRate = 360.0f;
294  turnVel = 0.0f;
295  anim_turn_yaw = 0.0f;
296  anim_turn_amount = 0.0f;
297  anim_turn_angles = 0.0f;
298  fly_offset = 0;
299  fly_seek_scale = 1.0f;
300  fly_roll_scale = 0.0f;
301  fly_roll_max = 0.0f;
302  fly_roll = 0.0f;
303  fly_pitch_scale = 0.0f;
304  fly_pitch_max = 0.0f;
305  fly_pitch = 0.0f;
306  allowMove = false;
307  allowHiddenMovement = false;
308  fly_speed = 0.0f;
309  fly_bob_strength = 0.0f;
310  fly_bob_vert = 0.0f;
311  fly_bob_horz = 0.0f;
312  lastHitCheckResult = false;
313  lastHitCheckTime = 0;
314  lastAttackTime = 0;
315  melee_range = 0.0f;
318  projectile = NULL;
320  projectileRadius = 0.0f;
323  projectileSpeed = 0.0f;
324  chat_snd = NULL;
325  chat_min = 0;
326  chat_max = 0;
327  chat_time = 0;
329  talkTarget = NULL;
330 
331  particles.Clear();
332  restartParticles = true;
333  useBoneAxis = false;
334 
335  wakeOnFlashlight = false;
336  memset( &worldMuzzleFlash, 0, sizeof ( worldMuzzleFlash ) );
338 
339  enemy = NULL;
344  shrivel_rate = 0.0f;
345  shrivel_start = 0;
346  fl.neverDormant = false; // AI's can go dormant
347  current_yaw = 0.0f;
348  ideal_yaw = 0.0f;
349 
350 #ifdef _D3XP
351  spawnClearMoveables = false;
352  harvestEnt = NULL;
353 #endif
354 
355  num_cinematics = 0;
356  current_cinematic = 0;
357 
358  allowEyeFocus = true;
359  allowPain = true;
360  allowJointMod = true;
361  focusEntity = NULL;
362  focusTime = 0;
363  alignHeadTime = 0;
364  forceAlignHeadTime = 0;
365 
367  eyeAng.Zero();
368  lookAng.Zero();
369  destLookAng.Zero();
370  lookMin.Zero();
371  lookMax.Zero();
372 
373  eyeMin.Zero();
374  eyeMax.Zero();
375  muzzleFlashEnd = 0;
376  flashTime = 0;
378 
382 
383  eyeVerticalOffset = 0.0f;
384  eyeHorizontalOffset = 0.0f;
385  eyeFocusRate = 0.0f;
386  headFocusRate = 0.0f;
387  focusAlignTime = 0;
388 }
389 
390 /*
391 =====================
392 idAI::~idAI
393 =====================
394 */
396  delete projectileClipModel;
398  scriptObject.Free();
399  if ( worldMuzzleFlashHandle != -1 ) {
402  }
403 
404 #ifdef _D3XP
405  if ( harvestEnt.GetEntity() ) {
406  harvestEnt.GetEntity()->PostEventMS( &EV_Remove, 0 );
407  }
408 #endif
409 }
410 
411 /*
412 =====================
413 idAI::Save
414 =====================
415 */
416 void idAI::Save( idSaveGame *savefile ) const {
417  int i;
418 
419  savefile->WriteInt( travelFlags );
420  move.Save( savefile );
421  savedMove.Save( savefile );
422  savefile->WriteFloat( kickForce );
423  savefile->WriteBool( ignore_obstacles );
424  savefile->WriteFloat( blockedRadius );
425  savefile->WriteInt( blockedMoveTime );
426  savefile->WriteInt( blockedAttackTime );
427 
428  savefile->WriteFloat( ideal_yaw );
429  savefile->WriteFloat( current_yaw );
430  savefile->WriteFloat( turnRate );
431  savefile->WriteFloat( turnVel );
432  savefile->WriteFloat( anim_turn_yaw );
433  savefile->WriteFloat( anim_turn_amount );
434  savefile->WriteFloat( anim_turn_angles );
435 
436  savefile->WriteStaticObject( physicsObj );
437 
438  savefile->WriteFloat( fly_speed );
439  savefile->WriteFloat( fly_bob_strength );
440  savefile->WriteFloat( fly_bob_vert );
441  savefile->WriteFloat( fly_bob_horz );
442  savefile->WriteInt( fly_offset );
443  savefile->WriteFloat( fly_seek_scale );
444  savefile->WriteFloat( fly_roll_scale );
445  savefile->WriteFloat( fly_roll_max );
446  savefile->WriteFloat( fly_roll );
447  savefile->WriteFloat( fly_pitch_scale );
448  savefile->WriteFloat( fly_pitch_max );
449  savefile->WriteFloat( fly_pitch );
450 
451  savefile->WriteBool( allowMove );
452  savefile->WriteBool( allowHiddenMovement );
453  savefile->WriteBool( disableGravity );
454  savefile->WriteBool( af_push_moveables );
455 
456  savefile->WriteBool( lastHitCheckResult );
457  savefile->WriteInt( lastHitCheckTime );
458  savefile->WriteInt( lastAttackTime );
459  savefile->WriteFloat( melee_range );
461 
462  savefile->WriteInt( missileLaunchOffset.Num() );
463  for( i = 0; i < missileLaunchOffset.Num(); i++ ) {
464  savefile->WriteVec3( missileLaunchOffset[ i ] );
465  }
466 
467  idStr projectileName;
468  spawnArgs.GetString( "def_projectile", "", projectileName );
469  savefile->WriteString( projectileName );
470  savefile->WriteFloat( projectileRadius );
471  savefile->WriteFloat( projectileSpeed );
472  savefile->WriteVec3( projectileVelocity );
473  savefile->WriteVec3( projectileGravity );
474  projectile.Save( savefile );
475  savefile->WriteString( attack );
476 
477  savefile->WriteSoundShader( chat_snd );
478  savefile->WriteInt( chat_min );
479  savefile->WriteInt( chat_max );
480  savefile->WriteInt( chat_time );
481  savefile->WriteInt( talk_state );
482  talkTarget.Save( savefile );
483 
484  savefile->WriteInt( num_cinematics );
485  savefile->WriteInt( current_cinematic );
486 
487  savefile->WriteBool( allowJointMod );
488  focusEntity.Save( savefile );
489  savefile->WriteVec3( currentFocusPos );
490  savefile->WriteInt( focusTime );
491  savefile->WriteInt( alignHeadTime );
492  savefile->WriteInt( forceAlignHeadTime );
493  savefile->WriteAngles( eyeAng );
494  savefile->WriteAngles( lookAng );
495  savefile->WriteAngles( destLookAng );
496  savefile->WriteAngles( lookMin );
497  savefile->WriteAngles( lookMax );
498 
499  savefile->WriteInt( lookJoints.Num() );
500  for( i = 0; i < lookJoints.Num(); i++ ) {
501  savefile->WriteJoint( lookJoints[ i ] );
502  savefile->WriteAngles( lookJointAngles[ i ] );
503  }
504 
505  savefile->WriteFloat( shrivel_rate );
506  savefile->WriteInt( shrivel_start );
507 
508  savefile->WriteInt( particles.Num() );
509  for ( i = 0; i < particles.Num(); i++ ) {
510  savefile->WriteParticle( particles[i].particle );
511  savefile->WriteInt( particles[i].time );
512  savefile->WriteJoint( particles[i].joint );
513  }
514  savefile->WriteBool( restartParticles );
515  savefile->WriteBool( useBoneAxis );
516 
517  enemy.Save( savefile );
518  savefile->WriteVec3( lastVisibleEnemyPos );
521  savefile->WriteVec3( lastReachableEnemyPos );
522  savefile->WriteBool( wakeOnFlashlight );
523 
524  savefile->WriteAngles( eyeMin );
525  savefile->WriteAngles( eyeMax );
526 
527  savefile->WriteFloat( eyeVerticalOffset );
528  savefile->WriteFloat( eyeHorizontalOffset );
529  savefile->WriteFloat( eyeFocusRate );
530  savefile->WriteFloat( headFocusRate );
531  savefile->WriteInt( focusAlignTime );
532 
533  savefile->WriteJoint( flashJointWorld );
534  savefile->WriteInt( muzzleFlashEnd );
535 
536  savefile->WriteJoint( focusJoint );
537  savefile->WriteJoint( orientationJoint );
538  savefile->WriteJoint( flyTiltJoint );
539 
540  savefile->WriteBool( GetPhysics() == static_cast<const idPhysics *>(&physicsObj) );
541 
542 #ifdef _D3XP
543  savefile->WriteInt(funcEmitters.Num());
544  for(int i = 0; i < funcEmitters.Num(); i++) {
545  funcEmitter_t* emitter = funcEmitters.GetIndex(i);
546  savefile->WriteString(emitter->name);
547  savefile->WriteJoint(emitter->joint);
548  savefile->WriteObject(emitter->particle);
549  }
550 
551  harvestEnt.Save( savefile);
552 #endif
553 }
554 
555 /*
556 =====================
557 idAI::Restore
558 =====================
559 */
560 void idAI::Restore( idRestoreGame *savefile ) {
561  bool restorePhysics;
562  int i;
563  int num;
564  idBounds bounds;
565 
566  savefile->ReadInt( travelFlags );
567  move.Restore( savefile );
568  savedMove.Restore( savefile );
569  savefile->ReadFloat( kickForce );
570  savefile->ReadBool( ignore_obstacles );
571  savefile->ReadFloat( blockedRadius );
572  savefile->ReadInt( blockedMoveTime );
573  savefile->ReadInt( blockedAttackTime );
574 
575  savefile->ReadFloat( ideal_yaw );
576  savefile->ReadFloat( current_yaw );
577  savefile->ReadFloat( turnRate );
578  savefile->ReadFloat( turnVel );
579  savefile->ReadFloat( anim_turn_yaw );
580  savefile->ReadFloat( anim_turn_amount );
581  savefile->ReadFloat( anim_turn_angles );
582 
583  savefile->ReadStaticObject( physicsObj );
584 
585  savefile->ReadFloat( fly_speed );
586  savefile->ReadFloat( fly_bob_strength );
587  savefile->ReadFloat( fly_bob_vert );
588  savefile->ReadFloat( fly_bob_horz );
589  savefile->ReadInt( fly_offset );
590  savefile->ReadFloat( fly_seek_scale );
591  savefile->ReadFloat( fly_roll_scale );
592  savefile->ReadFloat( fly_roll_max );
593  savefile->ReadFloat( fly_roll );
594  savefile->ReadFloat( fly_pitch_scale );
595  savefile->ReadFloat( fly_pitch_max );
596  savefile->ReadFloat( fly_pitch );
597 
598  savefile->ReadBool( allowMove );
599  savefile->ReadBool( allowHiddenMovement );
600  savefile->ReadBool( disableGravity );
601  savefile->ReadBool( af_push_moveables );
602 
603  savefile->ReadBool( lastHitCheckResult );
604  savefile->ReadInt( lastHitCheckTime );
605  savefile->ReadInt( lastAttackTime );
606  savefile->ReadFloat( melee_range );
608 
609  savefile->ReadInt( num );
612  for( i = 0; i < num; i++ ) {
613  savefile->ReadVec3( missileLaunchOffset[ i ] );
614  }
615 
616  idStr projectileName;
617  savefile->ReadString( projectileName );
618  if ( projectileName.Length() ) {
619  projectileDef = gameLocal.FindEntityDefDict( projectileName );
620  } else {
622  }
623  savefile->ReadFloat( projectileRadius );
624  savefile->ReadFloat( projectileSpeed );
625  savefile->ReadVec3( projectileVelocity );
626  savefile->ReadVec3( projectileGravity );
627  projectile.Restore( savefile );
628  savefile->ReadString( attack );
629 
630  savefile->ReadSoundShader( chat_snd );
631  savefile->ReadInt( chat_min );
632  savefile->ReadInt( chat_max );
633  savefile->ReadInt( chat_time );
634  savefile->ReadInt( i );
635  talk_state = static_cast<talkState_t>( i );
636  talkTarget.Restore( savefile );
637 
638  savefile->ReadInt( num_cinematics );
639  savefile->ReadInt( current_cinematic );
640 
641  savefile->ReadBool( allowJointMod );
642  focusEntity.Restore( savefile );
643  savefile->ReadVec3( currentFocusPos );
644  savefile->ReadInt( focusTime );
645  savefile->ReadInt( alignHeadTime );
646  savefile->ReadInt( forceAlignHeadTime );
647  savefile->ReadAngles( eyeAng );
648  savefile->ReadAngles( lookAng );
649  savefile->ReadAngles( destLookAng );
650  savefile->ReadAngles( lookMin );
651  savefile->ReadAngles( lookMax );
652 
653  savefile->ReadInt( num );
655  lookJoints.SetNum( num );
657  lookJointAngles.SetNum( num );
658  for( i = 0; i < num; i++ ) {
659  savefile->ReadJoint( lookJoints[ i ] );
660  savefile->ReadAngles( lookJointAngles[ i ] );
661  }
662 
663  savefile->ReadFloat( shrivel_rate );
664  savefile->ReadInt( shrivel_start );
665 
666  savefile->ReadInt( num );
667  particles.SetNum( num );
668  for ( i = 0; i < particles.Num(); i++ ) {
669  savefile->ReadParticle( particles[i].particle );
670  savefile->ReadInt( particles[i].time );
671  savefile->ReadJoint( particles[i].joint );
672  }
673  savefile->ReadBool( restartParticles );
674  savefile->ReadBool( useBoneAxis );
675 
676  enemy.Restore( savefile );
677  savefile->ReadVec3( lastVisibleEnemyPos );
678  savefile->ReadVec3( lastVisibleEnemyEyeOffset );
680  savefile->ReadVec3( lastReachableEnemyPos );
681 
682  savefile->ReadBool( wakeOnFlashlight );
683 
684  savefile->ReadAngles( eyeMin );
685  savefile->ReadAngles( eyeMax );
686 
687  savefile->ReadFloat( eyeVerticalOffset );
688  savefile->ReadFloat( eyeHorizontalOffset );
689  savefile->ReadFloat( eyeFocusRate );
690  savefile->ReadFloat( headFocusRate );
691  savefile->ReadInt( focusAlignTime );
692 
693  savefile->ReadJoint( flashJointWorld );
694  savefile->ReadInt( muzzleFlashEnd );
695 
696  savefile->ReadJoint( focusJoint );
697  savefile->ReadJoint( orientationJoint );
698  savefile->ReadJoint( flyTiltJoint );
699 
700  savefile->ReadBool( restorePhysics );
701 
702  // Set the AAS if the character has the correct gravity vector
703  idVec3 gravity = spawnArgs.GetVector( "gravityDir", "0 0 -1" );
704  gravity *= g_gravity.GetFloat();
705  if ( gravity == gameLocal.GetGravity() ) {
706  SetAAS();
707  }
708 
709  SetCombatModel();
710  LinkCombat();
711 
712  InitMuzzleFlash();
713 
714  // Link the script variables back to the scriptobject
716 
717  if ( restorePhysics ) {
719  }
720 
721 #ifdef _D3XP
722 
723  //Clean up the emitters
724  for(int i = 0; i < funcEmitters.Num(); i++) {
725  funcEmitter_t* emitter = funcEmitters.GetIndex(i);
726  if(emitter->particle) {
727  //Destroy the emitters
728  emitter->particle->PostEventMS(&EV_Remove, 0 );
729  }
730  }
731  funcEmitters.Clear();
732 
733  int emitterCount;
734  savefile->ReadInt( emitterCount );
735  for(int i = 0; i < emitterCount; i++) {
736  funcEmitter_t newEmitter;
737  memset(&newEmitter, 0, sizeof(newEmitter));
738 
739  idStr name;
740  savefile->ReadString( name );
741 
742  strcpy( newEmitter.name, name.c_str() );
743 
744  savefile->ReadJoint( newEmitter.joint );
745  savefile->ReadObject(reinterpret_cast<idClass *&>(newEmitter.particle));
746 
747  funcEmitters.Set(newEmitter.name, newEmitter);
748  }
749 
750  harvestEnt.Restore(savefile);
751  //if(harvestEnt.GetEntity()) {
752  // harvestEnt.GetEntity()->SetParent(this);
753  //}
754 
755 #endif
756 }
757 
758 /*
759 =====================
760 idAI::Spawn
761 =====================
762 */
763 void idAI::Spawn( void ) {
764  const char *jointname;
765  const idKeyValue *kv;
766  idStr jointName;
767  idAngles jointScale;
768  jointHandle_t joint;
769  idVec3 local_dir;
770  bool talks;
771 
772  if ( !g_monsters.GetBool() ) {
773  PostEventMS( &EV_Remove, 0 );
774  return;
775  }
776 
777  spawnArgs.GetInt( "team", "1", team );
778  spawnArgs.GetInt( "rank", "0", rank );
779  spawnArgs.GetInt( "fly_offset", "0", fly_offset );
780  spawnArgs.GetFloat( "fly_speed", "100", fly_speed );
781  spawnArgs.GetFloat( "fly_bob_strength", "50", fly_bob_strength );
782  spawnArgs.GetFloat( "fly_bob_vert", "2", fly_bob_horz );
783  spawnArgs.GetFloat( "fly_bob_horz", "2.7", fly_bob_vert );
784  spawnArgs.GetFloat( "fly_seek_scale", "4", fly_seek_scale );
785  spawnArgs.GetFloat( "fly_roll_scale", "90", fly_roll_scale );
786  spawnArgs.GetFloat( "fly_roll_max", "60", fly_roll_max );
787  spawnArgs.GetFloat( "fly_pitch_scale", "45", fly_pitch_scale );
788  spawnArgs.GetFloat( "fly_pitch_max", "30", fly_pitch_max );
789 
790  spawnArgs.GetFloat( "melee_range", "64", melee_range );
791  spawnArgs.GetFloat( "projectile_height_to_distance_ratio", "1", projectile_height_to_distance_ratio );
792 
793  spawnArgs.GetFloat( "turn_rate", "360", turnRate );
794 
795  spawnArgs.GetBool( "talks", "0", talks );
796  if ( spawnArgs.GetString( "npc_name", NULL ) != NULL ) {
797  if ( talks ) {
799  } else {
801  }
802  } else {
804  }
805 
806  spawnArgs.GetBool( "animate_z", "0", disableGravity );
807  spawnArgs.GetBool( "af_push_moveables", "0", af_push_moveables );
808  spawnArgs.GetFloat( "kick_force", "4096", kickForce );
809  spawnArgs.GetBool( "ignore_obstacles", "0", ignore_obstacles );
810  spawnArgs.GetFloat( "blockedRadius", "-1", blockedRadius );
811  spawnArgs.GetInt( "blockedMoveTime", "750", blockedMoveTime );
812  spawnArgs.GetInt( "blockedAttackTime", "750", blockedAttackTime );
813 
814  spawnArgs.GetInt( "num_cinematics", "0", num_cinematics );
815  current_cinematic = 0;
816 
818 
819  fl.takedamage = !spawnArgs.GetBool( "noDamage" );
820  enemy = NULL;
821  allowMove = true;
822  allowHiddenMovement = false;
823 
825 
826  // create combat collision hull for exact collision detection
827  SetCombatModel();
828 
829  lookMin = spawnArgs.GetAngles( "look_min", "-80 -75 0" );
830  lookMax = spawnArgs.GetAngles( "look_max", "80 75 0" );
831 
834  kv = spawnArgs.MatchPrefix( "look_joint", NULL );
835  while( kv ) {
836  jointName = kv->GetKey();
837  jointName.StripLeadingOnce( "look_joint " );
838  joint = animator.GetJointHandle( jointName );
839  if ( joint == INVALID_JOINT ) {
840  gameLocal.Warning( "Unknown look_joint '%s' on entity %s", jointName.c_str(), name.c_str() );
841  } else {
842  jointScale = spawnArgs.GetAngles( kv->GetKey(), "0 0 0" );
843  jointScale.roll = 0.0f;
844 
845  // if no scale on any component, then don't bother adding it. this may be done to
846  // zero out rotation from an inherited entitydef.
847  if ( jointScale != ang_zero ) {
848  lookJoints.Append( joint );
849  lookJointAngles.Append( jointScale );
850  }
851  }
852  kv = spawnArgs.MatchPrefix( "look_joint", kv );
853  }
854 
855  // calculate joint positions on attack frames so we can do proper "can hit" tests
857 
858  eyeMin = spawnArgs.GetAngles( "eye_turn_min", "-10 -30 0" );
859  eyeMax = spawnArgs.GetAngles( "eye_turn_max", "10 30 0" );
860  eyeVerticalOffset = spawnArgs.GetFloat( "eye_verticle_offset", "5" );
861  eyeHorizontalOffset = spawnArgs.GetFloat( "eye_horizontal_offset", "-8" );
862  eyeFocusRate = spawnArgs.GetFloat( "eye_focus_rate", "0.5" );
863  headFocusRate = spawnArgs.GetFloat( "head_focus_rate", "0.1" );
864  focusAlignTime = SEC2MS( spawnArgs.GetFloat( "focus_align_time", "1" ) );
865 
867 
868  if ( head.GetEntity() ) {
869  idAnimator *headAnimator = head.GetEntity()->GetAnimator();
870 
871  jointname = spawnArgs.GetString( "bone_focus" );
872  if ( *jointname ) {
873  focusJoint = headAnimator->GetJointHandle( jointname );
874  if ( focusJoint == INVALID_JOINT ) {
875  gameLocal.Warning( "Joint '%s' not found on head on '%s'", jointname, name.c_str() );
876  }
877  }
878  } else {
879  jointname = spawnArgs.GetString( "bone_focus" );
880  if ( *jointname ) {
881  focusJoint = animator.GetJointHandle( jointname );
882  if ( focusJoint == INVALID_JOINT ) {
883  gameLocal.Warning( "Joint '%s' not found on '%s'", jointname, name.c_str() );
884  }
885  }
886  }
887 
888  jointname = spawnArgs.GetString( "bone_orientation" );
889  if ( *jointname ) {
891  if ( orientationJoint == INVALID_JOINT ) {
892  gameLocal.Warning( "Joint '%s' not found on '%s'", jointname, name.c_str() );
893  }
894  }
895 
896  jointname = spawnArgs.GetString( "bone_flytilt" );
897  if ( *jointname ) {
898  flyTiltJoint = animator.GetJointHandle( jointname );
899  if ( flyTiltJoint == INVALID_JOINT ) {
900  gameLocal.Warning( "Joint '%s' not found on '%s'", jointname, name.c_str() );
901  }
902  }
903 
904  InitMuzzleFlash();
905 
906  physicsObj.SetSelf( this );
907  physicsObj.SetClipModel( new idClipModel( GetPhysics()->GetClipModel() ), 1.0f );
908  physicsObj.SetMass( spawnArgs.GetFloat( "mass", "100" ) );
909 
910  if ( spawnArgs.GetBool( "big_monster" ) ) {
911  physicsObj.SetContents( 0 );
913  } else {
914  if ( use_combat_bbox ) {
916  } else {
918  }
920  }
921 
922  // move up to make sure the monster is at least an epsilon above the floor
923  physicsObj.SetOrigin( GetPhysics()->GetOrigin() + idVec3( 0, 0, CM_CLIP_EPSILON ) );
924 
925  if ( num_cinematics ) {
927  } else {
928  idVec3 gravity = spawnArgs.GetVector( "gravityDir", "0 0 -1" );
929  gravity *= g_gravity.GetFloat();
930  physicsObj.SetGravity( gravity );
931  }
932 
934 
935  physicsObj.GetGravityAxis().ProjectVector( viewAxis[ 0 ], local_dir );
936  current_yaw = local_dir.ToYaw();
938 
939  move.blockTime = 0;
940 
941  SetAAS();
942 
943  projectile = NULL;
946  idStr projectileName;
947  if ( spawnArgs.GetString( "def_projectile", "", projectileName ) && projectileName.Length() ) {
948  projectileDef = gameLocal.FindEntityDefDict( projectileName );
954  delete projectile.GetEntity();
955  projectile = NULL;
956  }
957 
958  particles.Clear();
959  restartParticles = true;
960  useBoneAxis = spawnArgs.GetBool( "useBoneAxis" );
961  SpawnParticles( "smokeParticleSystem" );
962 
963  if ( num_cinematics || spawnArgs.GetBool( "hide" ) || spawnArgs.GetBool( "teleport" ) || spawnArgs.GetBool( "trigger_anim" ) ) {
964  fl.takedamage = false;
965  physicsObj.SetContents( 0 );
967  Hide();
968  } else {
969  // play a looping ambient sound if we have one
970  StartSound( "snd_ambient", SND_CHANNEL_AMBIENT, 0, false, NULL );
971  }
972 
973  if ( health <= 0 ) {
974  gameLocal.Warning( "entity '%s' doesn't have health set", name.c_str() );
975  health = 1;
976  }
977 
978  // set up monster chatter
979  SetChatSound();
980 
982 
983  if ( af_push_moveables ) {
984  af.SetupPose( this, gameLocal.time );
985  af.GetPhysics()->EnableClip();
986  }
987 
988  // init the move variables
990 
991 
992 #ifdef _D3XP
993  spawnArgs.GetBool( "spawnClearMoveables", "0", spawnClearMoveables );
994 #endif
995 }
996 
997 
998 #ifdef _D3XP
999 void idAI::Gib( const idVec3 &dir, const char *damageDefName ) {
1000  if(harvestEnt.GetEntity()) {
1001  //Let the harvest ent know that we gibbed
1002  harvestEnt.GetEntity()->Gib();
1003  }
1004  idActor::Gib(dir, damageDefName);
1005 }
1006 #endif
1007 
1008 /*
1009 ===================
1010 idAI::InitMuzzleFlash
1011 ===================
1012 */
1014  const char *shader;
1015  idVec3 flashColor;
1016 
1017  spawnArgs.GetString( "mtr_flashShader", "muzzleflash", &shader );
1018  spawnArgs.GetVector( "flashColor", "0 0 0", flashColor );
1019  float flashRadius = spawnArgs.GetFloat( "flashRadius" );
1020  flashTime = SEC2MS( spawnArgs.GetFloat( "flashTime", "0.25" ) );
1021 
1022  memset( &worldMuzzleFlash, 0, sizeof ( worldMuzzleFlash ) );
1023 
1025  worldMuzzleFlash.shader = declManager->FindMaterial( shader, false );
1026  worldMuzzleFlash.shaderParms[ SHADERPARM_RED ] = flashColor[0];
1027  worldMuzzleFlash.shaderParms[ SHADERPARM_GREEN ] = flashColor[1];
1028  worldMuzzleFlash.shaderParms[ SHADERPARM_BLUE ] = flashColor[2];
1031  worldMuzzleFlash.lightRadius[0] = flashRadius;
1032  worldMuzzleFlash.lightRadius[1] = flashRadius;
1033  worldMuzzleFlash.lightRadius[2] = flashRadius;
1034 
1036 }
1037 
1038 /*
1039 ===================
1040 idAI::List_f
1041 ===================
1042 */
1043 void idAI::List_f( const idCmdArgs &args ) {
1044  int e;
1045  idAI *check;
1046  int count;
1047  const char *statename;
1048 
1049  count = 0;
1050 
1051  gameLocal.Printf( "%-4s %-20s %s\n", " Num", "EntityDef", "Name" );
1052  gameLocal.Printf( "------------------------------------------------\n" );
1053  for( e = 0; e < MAX_GENTITIES; e++ ) {
1054  check = static_cast<idAI *>(gameLocal.entities[ e ]);
1055  if ( !check || !check->IsType( idAI::Type ) ) {
1056  continue;
1057  }
1058 
1059  if ( check->state ) {
1060  statename = check->state->Name();
1061  } else {
1062  statename = "NULL state";
1063  }
1064 
1065  gameLocal.Printf( "%4i: %-20s %-20s %s move: %d\n", e, check->GetEntityDefName(), check->name.c_str(), statename, check->allowMove );
1066  count++;
1067  }
1068 
1069  gameLocal.Printf( "...%d monsters\n", count );
1070 }
1071 
1072 /*
1073 ================
1074 idAI::DormantBegin
1075 
1076 called when entity becomes dormant
1077 ================
1078 */
1079 void idAI::DormantBegin( void ) {
1080  // since dormant happens on a timer, we wont get to update particles to
1081  // hidden through the think loop, but we need to hide them though.
1082  if ( particles.Num() ) {
1083  for ( int i = 0; i < particles.Num(); i++ ) {
1084  particles[i].time = 0;
1085  }
1086  }
1087 
1088  if ( enemyNode.InList() ) {
1089  // remove ourselves from the enemy's enemylist
1090  enemyNode.Remove();
1091  }
1093 }
1094 
1095 /*
1096 ================
1097 idAI::DormantEnd
1098 
1099 called when entity wakes from being dormant
1100 ================
1101 */
1102 void idAI::DormantEnd( void ) {
1103  if ( enemy.GetEntity() && !enemyNode.InList() ) {
1104  // let our enemy know we're back on the trail
1106  }
1107 
1108  if ( particles.Num() ) {
1109  for ( int i = 0; i < particles.Num(); i++ ) {
1110  particles[i].time = gameLocal.time;
1111  }
1112  }
1113 
1115 }
1116 
1117 /*
1118 =====================
1119 idAI::Think
1120 =====================
1121 */
1122 void idAI::Think( void ) {
1123  // if we are completely closed off from the player, don't do anything at all
1124  if ( CheckDormant() ) {
1125  return;
1126  }
1127 
1128  if ( thinkFlags & TH_THINK ) {
1129  // clear out the enemy when he dies or is hidden
1130  idActor *enemyEnt = enemy.GetEntity();
1131  if ( enemyEnt ) {
1132  if ( enemyEnt->health <= 0 ) {
1133  EnemyDead();
1134  }
1135  }
1136 
1140  viewAxis = idAngles( 0, current_yaw, 0 ).ToMat3();
1141 
1142  if ( num_cinematics ) {
1143  if ( !IsHidden() && torsoAnim.AnimDone( 0 ) ) {
1144  PlayCinematic();
1145  }
1146  RunPhysics();
1147  } else if ( !allowHiddenMovement && IsHidden() ) {
1148  // hidden monsters
1149  UpdateAIScript();
1150  } else {
1151  // clear the ik before we do anything else so the skeleton doesn't get updated twice
1153 
1154  switch( move.moveType ) {
1155  case MOVETYPE_DEAD :
1156  // dead monsters
1157  UpdateAIScript();
1158  DeadMove();
1159  break;
1160 
1161  case MOVETYPE_FLY :
1162  // flying monsters
1164  UpdateAIScript();
1165  FlyMove();
1166  PlayChatter();
1167  CheckBlink();
1168  break;
1169 
1170  case MOVETYPE_STATIC :
1171  // static monsters
1173  UpdateAIScript();
1174  StaticMove();
1175  PlayChatter();
1176  CheckBlink();
1177  break;
1178 
1179  case MOVETYPE_ANIM :
1180  // animation based movement
1182  UpdateAIScript();
1183  AnimMove();
1184  PlayChatter();
1185  CheckBlink();
1186  break;
1187 
1188  case MOVETYPE_SLIDE :
1189  // velocity based movement
1191  UpdateAIScript();
1192  SlideMove();
1193  PlayChatter();
1194  CheckBlink();
1195  break;
1196  }
1197  }
1198 
1199  // clear pain flag so that we recieve any damage between now and the next time we run the script
1200  AI_PAIN = false;
1201  AI_SPECIAL_DAMAGE = 0;
1202  AI_PUSHED = false;
1203  } else if ( thinkFlags & TH_PHYSICS ) {
1204  RunPhysics();
1205  }
1206 
1207  if ( af_push_moveables ) {
1208  PushWithAF();
1209  }
1210 
1211  if ( fl.hidden && allowHiddenMovement ) {
1212  // UpdateAnimation won't call frame commands when hidden, so call them here when we allow hidden movement
1214  }
1215 /* this still draws in retail builds.. not sure why.. don't care at this point.
1216  if ( !aas && developer.GetBool() && !fl.hidden && !num_cinematics ) {
1217  gameRenderWorld->DrawText( "No AAS", physicsObj.GetAbsBounds().GetCenter(), 0.1f, colorWhite, gameLocal.GetLocalPlayer()->viewAngles.ToMat3(), 1, gameLocal.msec );
1218  }
1219 */
1220 
1222  UpdateAnimation();
1223  UpdateParticles();
1224  Present();
1226  LinkCombat();
1227 
1228 #ifdef _D3XP
1229  if(ai_showHealth.GetBool()) {
1230  idVec3 aboveHead(0,0,20);
1231  gameRenderWorld->DrawText( va( "%d", ( int )health), this->GetEyePosition()+aboveHead, 0.5f, colorWhite, gameLocal.GetLocalPlayer()->viewAngles.ToMat3() );
1232  }
1233 #endif
1234 }
1235 
1236 /***********************************************************************
1237 
1238  AI script state management
1239 
1240 ***********************************************************************/
1241 
1242 /*
1243 =====================
1244 idAI::LinkScriptVariables
1245 =====================
1246 */
1248  AI_TALK.LinkTo( scriptObject, "AI_TALK" );
1249  AI_DAMAGE.LinkTo( scriptObject, "AI_DAMAGE" );
1250  AI_PAIN.LinkTo( scriptObject, "AI_PAIN" );
1251  AI_SPECIAL_DAMAGE.LinkTo( scriptObject, "AI_SPECIAL_DAMAGE" );
1252  AI_DEAD.LinkTo( scriptObject, "AI_DEAD" );
1253  AI_ENEMY_VISIBLE.LinkTo( scriptObject, "AI_ENEMY_VISIBLE" );
1254  AI_ENEMY_IN_FOV.LinkTo( scriptObject, "AI_ENEMY_IN_FOV" );
1255  AI_ENEMY_DEAD.LinkTo( scriptObject, "AI_ENEMY_DEAD" );
1256  AI_MOVE_DONE.LinkTo( scriptObject, "AI_MOVE_DONE" );
1257  AI_ONGROUND.LinkTo( scriptObject, "AI_ONGROUND" );
1258  AI_ACTIVATED.LinkTo( scriptObject, "AI_ACTIVATED" );
1259  AI_FORWARD.LinkTo( scriptObject, "AI_FORWARD" );
1260  AI_JUMP.LinkTo( scriptObject, "AI_JUMP" );
1261  AI_BLOCKED.LinkTo( scriptObject, "AI_BLOCKED" );
1262  AI_DEST_UNREACHABLE.LinkTo( scriptObject, "AI_DEST_UNREACHABLE" );
1263  AI_HIT_ENEMY.LinkTo( scriptObject, "AI_HIT_ENEMY" );
1264  AI_OBSTACLE_IN_PATH.LinkTo( scriptObject, "AI_OBSTACLE_IN_PATH" );
1265  AI_PUSHED.LinkTo( scriptObject, "AI_PUSHED" );
1266 }
1267 
1268 /*
1269 =====================
1270 idAI::UpdateAIScript
1271 =====================
1272 */
1273 void idAI::UpdateAIScript( void ) {
1274  UpdateScript();
1275 
1276  // clear the hit enemy flag so we catch the next time we hit someone
1277  AI_HIT_ENEMY = false;
1278 
1279  if ( allowHiddenMovement || !IsHidden() ) {
1280  // update the animstate if we're not hidden
1281  UpdateAnimState();
1282  }
1283 }
1284 
1285 /***********************************************************************
1286 
1287  navigation
1288 
1289 ***********************************************************************/
1290 
1291 /*
1292 ============
1293 idAI::KickObstacles
1294 ============
1295 */
1296 void idAI::KickObstacles( const idVec3 &dir, float force, idEntity *alwaysKick ) {
1297  int i, numListedClipModels;
1298  idBounds clipBounds;
1299  idEntity *obEnt;
1300  idClipModel *clipModel;
1301  idClipModel *clipModelList[ MAX_GENTITIES ];
1302  int clipmask;
1303  idVec3 org;
1304  idVec3 forceVec;
1305  idVec3 delta;
1306  idVec2 perpendicular;
1307 
1308  org = physicsObj.GetOrigin();
1309 
1310  // find all possible obstacles
1311  clipBounds = physicsObj.GetAbsBounds();
1312  clipBounds.TranslateSelf( dir * 32.0f );
1313  clipBounds.ExpandSelf( 8.0f );
1314  clipBounds.AddPoint( org );
1315  clipmask = physicsObj.GetClipMask();
1316  numListedClipModels = gameLocal.clip.ClipModelsTouchingBounds( clipBounds, clipmask, clipModelList, MAX_GENTITIES );
1317  for ( i = 0; i < numListedClipModels; i++ ) {
1318  clipModel = clipModelList[i];
1319  obEnt = clipModel->GetEntity();
1320  if ( obEnt == alwaysKick ) {
1321  // we'll kick this one outside the loop
1322  continue;
1323  }
1324 
1325  if ( !clipModel->IsTraceModel() ) {
1326  continue;
1327  }
1328 
1329  if ( obEnt->IsType( idMoveable::Type ) && obEnt->GetPhysics()->IsPushable() ) {
1330  delta = obEnt->GetPhysics()->GetOrigin() - org;
1331  delta.NormalizeFast();
1332  perpendicular.x = -delta.y;
1333  perpendicular.y = delta.x;
1334  delta.z += 0.5f;
1335  delta.ToVec2() += perpendicular * gameLocal.random.CRandomFloat() * 0.5f;
1336  forceVec = delta * force * obEnt->GetPhysics()->GetMass();
1337  obEnt->ApplyImpulse( this, 0, obEnt->GetPhysics()->GetOrigin(), forceVec );
1338  }
1339  }
1340 
1341  if ( alwaysKick ) {
1342  delta = alwaysKick->GetPhysics()->GetOrigin() - org;
1343  delta.NormalizeFast();
1344  perpendicular.x = -delta.y;
1345  perpendicular.y = delta.x;
1346  delta.z += 0.5f;
1347  delta.ToVec2() += perpendicular * gameLocal.random.CRandomFloat() * 0.5f;
1348  forceVec = delta * force * alwaysKick->GetPhysics()->GetMass();
1349  alwaysKick->ApplyImpulse( this, 0, alwaysKick->GetPhysics()->GetOrigin(), forceVec );
1350  }
1351 }
1352 
1353 /*
1354 ============
1355 ValidForBounds
1356 ============
1357 */
1358 bool ValidForBounds( const idAASSettings *settings, const idBounds &bounds ) {
1359  int i;
1360 
1361  for ( i = 0; i < 3; i++ ) {
1362  if ( bounds[0][i] < settings->boundingBoxes[0][0][i] ) {
1363  return false;
1364  }
1365  if ( bounds[1][i] > settings->boundingBoxes[0][1][i] ) {
1366  return false;
1367  }
1368  }
1369  return true;
1370 }
1371 
1372 /*
1373 =====================
1374 idAI::SetAAS
1375 =====================
1376 */
1377 void idAI::SetAAS( void ) {
1378  idStr use_aas;
1379 
1380  spawnArgs.GetString( "use_aas", NULL, use_aas );
1381  aas = gameLocal.GetAAS( use_aas );
1382  if ( aas ) {
1383  const idAASSettings *settings = aas->GetSettings();
1384  if ( settings ) {
1385  if ( !ValidForBounds( settings, physicsObj.GetBounds() ) ) {
1386  gameLocal.Error( "%s cannot use use_aas %s\n", name.c_str(), use_aas.c_str() );
1387  }
1388  float height = settings->maxStepHeight;
1389  physicsObj.SetMaxStepHeight( height );
1390  return;
1391  } else {
1392  aas = NULL;
1393  }
1394  }
1395  gameLocal.Printf( "WARNING: %s has no AAS file\n", name.c_str() );
1396 }
1397 
1398 /*
1399 =====================
1400 idAI::DrawRoute
1401 =====================
1402 */
1403 void idAI::DrawRoute( void ) const {
1405  if ( move.moveType == MOVETYPE_FLY ) {
1407  } else {
1409  }
1410  }
1411 }
1412 
1413 /*
1414 =====================
1415 idAI::ReachedPos
1416 =====================
1417 */
1418 bool idAI::ReachedPos( const idVec3 &pos, const moveCommand_t moveCommand ) const {
1419  if ( move.moveType == MOVETYPE_SLIDE ) {
1420  idBounds bnds( idVec3( -4, -4.0f, -8.0f ), idVec3( 4.0f, 4.0f, 64.0f ) );
1421  bnds.TranslateSelf( physicsObj.GetOrigin() );
1422  if ( bnds.ContainsPoint( pos ) ) {
1423  return true;
1424  }
1425  } else {
1426  if ( ( moveCommand == MOVE_TO_ENEMY ) || ( moveCommand == MOVE_TO_ENTITY ) ) {
1427  if ( physicsObj.GetAbsBounds().IntersectsBounds( idBounds( pos ).Expand( 8.0f ) ) ) {
1428  return true;
1429  }
1430  } else {
1431  idBounds bnds( idVec3( -16.0, -16.0f, -8.0f ), idVec3( 16.0, 16.0f, 64.0f ) );
1432  bnds.TranslateSelf( physicsObj.GetOrigin() );
1433  if ( bnds.ContainsPoint( pos ) ) {
1434  return true;
1435  }
1436  }
1437  }
1438  return false;
1439 }
1440 
1441 /*
1442 =====================
1443 idAI::PointReachableAreaNum
1444 =====================
1445 */
1446 int idAI::PointReachableAreaNum( const idVec3 &pos, const float boundsScale ) const {
1447  int areaNum;
1448  idVec3 size;
1449  idBounds bounds;
1450 
1451  if ( !aas ) {
1452  return 0;
1453  }
1454 
1455  size = aas->GetSettings()->boundingBoxes[0][1] * boundsScale;
1456  bounds[0] = -size;
1457  size.z = 32.0f;
1458  bounds[1] = size;
1459 
1460  if ( move.moveType == MOVETYPE_FLY ) {
1461  areaNum = aas->PointReachableAreaNum( pos, bounds, AREA_REACHABLE_WALK | AREA_REACHABLE_FLY );
1462  } else {
1463  areaNum = aas->PointReachableAreaNum( pos, bounds, AREA_REACHABLE_WALK );
1464  }
1465 
1466  return areaNum;
1467 }
1468 
1469 /*
1470 =====================
1471 idAI::PathToGoal
1472 =====================
1473 */
1474 bool idAI::PathToGoal( aasPath_t &path, int areaNum, const idVec3 &origin, int goalAreaNum, const idVec3 &goalOrigin ) const {
1475  idVec3 org;
1476  idVec3 goal;
1477 
1478  if ( !aas ) {
1479  return false;
1480  }
1481 
1482  org = origin;
1483  aas->PushPointIntoAreaNum( areaNum, org );
1484  if ( !areaNum ) {
1485  return false;
1486  }
1487 
1488  goal = goalOrigin;
1489  aas->PushPointIntoAreaNum( goalAreaNum, goal );
1490  if ( !goalAreaNum ) {
1491  return false;
1492  }
1493 
1494  if ( move.moveType == MOVETYPE_FLY ) {
1495  return aas->FlyPathToGoal( path, areaNum, org, goalAreaNum, goal, travelFlags );
1496  } else {
1497  return aas->WalkPathToGoal( path, areaNum, org, goalAreaNum, goal, travelFlags );
1498  }
1499 }
1500 
1501 /*
1502 =====================
1503 idAI::TravelDistance
1504 
1505 Returns the approximate travel distance from one position to the goal, or if no AAS, the straight line distance.
1506 
1507 This is feakin' slow, so it's not good to do it too many times per frame. It also is slower the further you
1508 are from the goal, so try to break the goals up into shorter distances.
1509 =====================
1510 */
1511 float idAI::TravelDistance( const idVec3 &start, const idVec3 &end ) const {
1512  int fromArea;
1513  int toArea;
1514  float dist;
1515  idVec2 delta;
1516  aasPath_t path;
1517 
1518  if ( !aas ) {
1519  // no aas, so just take the straight line distance
1520  delta = end.ToVec2() - start.ToVec2();
1521  dist = delta.LengthFast();
1522 
1523  if ( ai_debugMove.GetBool() ) {
1524  gameRenderWorld->DebugLine( colorBlue, start, end, gameLocal.msec, false );
1525  gameRenderWorld->DrawText( va( "%d", ( int )dist ), ( start + end ) * 0.5f, 0.1f, colorWhite, gameLocal.GetLocalPlayer()->viewAngles.ToMat3() );
1526  }
1527 
1528  return dist;
1529  }
1530 
1531  fromArea = PointReachableAreaNum( start );
1532  toArea = PointReachableAreaNum( end );
1533 
1534  if ( !fromArea || !toArea ) {
1535  // can't seem to get there
1536  return -1;
1537  }
1538 
1539  if ( fromArea == toArea ) {
1540  // same area, so just take the straight line distance
1541  delta = end.ToVec2() - start.ToVec2();
1542  dist = delta.LengthFast();
1543 
1544  if ( ai_debugMove.GetBool() ) {
1545  gameRenderWorld->DebugLine( colorBlue, start, end, gameLocal.msec, false );
1546  gameRenderWorld->DrawText( va( "%d", ( int )dist ), ( start + end ) * 0.5f, 0.1f, colorWhite, gameLocal.GetLocalPlayer()->viewAngles.ToMat3() );
1547  }
1548 
1549  return dist;
1550  }
1551 
1552  idReachability *reach;
1553  int travelTime;
1554  if ( !aas->RouteToGoalArea( fromArea, start, toArea, travelFlags, travelTime, &reach ) ) {
1555  return -1;
1556  }
1557 
1558  if ( ai_debugMove.GetBool() ) {
1559  if ( move.moveType == MOVETYPE_FLY ) {
1560  aas->ShowFlyPath( start, toArea, end );
1561  } else {
1562  aas->ShowWalkPath( start, toArea, end );
1563  }
1564  }
1565 
1566  return travelTime;
1567 }
1568 
1569 /*
1570 =====================
1571 idAI::StopMove
1572 =====================
1573 */
1575  AI_MOVE_DONE = true;
1576  AI_FORWARD = false;
1578  move.moveStatus = status;
1579  move.toAreaNum = 0;
1580  move.goalEntity = NULL;
1582  AI_DEST_UNREACHABLE = false;
1583  AI_OBSTACLE_IN_PATH = false;
1584  AI_BLOCKED = false;
1586  move.duration = 0;
1587  move.range = 0.0f;
1588  move.speed = 0.0f;
1589  move.anim = 0;
1590  move.moveDir.Zero();
1593 }
1594 
1595 /*
1596 =====================
1597 idAI::FaceEnemy
1598 
1599 Continually face the enemy's last known position. MoveDone is always true in this case.
1600 =====================
1601 */
1602 bool idAI::FaceEnemy( void ) {
1603  idActor *enemyEnt = enemy.GetEntity();
1604  if ( !enemyEnt ) {
1606  return false;
1607  }
1608 
1610  move.goalEntity = enemyEnt;
1615  move.speed = 0.0f;
1616  AI_MOVE_DONE = true;
1617  AI_FORWARD = false;
1618  AI_DEST_UNREACHABLE = false;
1619 
1620  return true;
1621 }
1622 
1623 /*
1624 =====================
1625 idAI::FaceEntity
1626 
1627 Continually face the entity position. MoveDone is always true in this case.
1628 =====================
1629 */
1631  if ( !ent ) {
1633  return false;
1634  }
1635 
1636  idVec3 entityOrg = ent->GetPhysics()->GetOrigin();
1637  TurnToward( entityOrg );
1638  move.goalEntity = ent;
1643  move.speed = 0.0f;
1644  AI_MOVE_DONE = true;
1645  AI_FORWARD = false;
1646  AI_DEST_UNREACHABLE = false;
1647 
1648  return true;
1649 }
1650 
1651 /*
1652 =====================
1653 idAI::DirectMoveToPosition
1654 =====================
1655 */
1657  if ( ReachedPos( pos, move.moveCommand ) ) {
1659  return true;
1660  }
1661 
1662  move.moveDest = pos;
1663  move.goalEntity = NULL;
1667  move.speed = fly_speed;
1668  AI_MOVE_DONE = false;
1669  AI_DEST_UNREACHABLE = false;
1670  AI_FORWARD = true;
1671 
1672  if ( move.moveType == MOVETYPE_FLY ) {
1673  idVec3 dir = pos - physicsObj.GetOrigin();
1674  dir.Normalize();
1675  dir *= fly_speed;
1677  }
1678 
1679  return true;
1680 }
1681 
1682 /*
1683 =====================
1684 idAI::MoveToEnemyHeight
1685 =====================
1686 */
1688  idActor *enemyEnt = enemy.GetEntity();
1689 
1690  if ( !enemyEnt || ( move.moveType != MOVETYPE_FLY ) ) {
1692  return false;
1693  }
1694 
1696  move.goalEntity = enemyEnt;
1700  move.speed = 0.0f;
1701  AI_MOVE_DONE = false;
1702  AI_DEST_UNREACHABLE = false;
1703  AI_FORWARD = false;
1704 
1705  return true;
1706 }
1707 
1708 /*
1709 =====================
1710 idAI::MoveToEnemy
1711 =====================
1712 */
1713 bool idAI::MoveToEnemy( void ) {
1714  int areaNum;
1715  aasPath_t path;
1716  idActor *enemyEnt = enemy.GetEntity();
1717 
1718  if ( !enemyEnt ) {
1720  return false;
1721  }
1722 
1726  AI_DEST_UNREACHABLE = true;
1727  return false;
1728  }
1730  return true;
1731  }
1732 
1734 
1735  move.toAreaNum = 0;
1736  if ( aas ) {
1739 
1741  if ( !PathToGoal( path, areaNum, physicsObj.GetOrigin(), move.toAreaNum, pos ) ) {
1742  AI_DEST_UNREACHABLE = true;
1743  return false;
1744  }
1745  }
1746 
1747  if ( !move.toAreaNum ) {
1748  // if only trying to update the enemy position
1749  if ( move.moveCommand == MOVE_TO_ENEMY ) {
1750  if ( !aas ) {
1751  // keep the move destination up to date for wandering
1752  move.moveDest = pos;
1753  }
1754  return false;
1755  }
1756 
1757  if ( !NewWanderDir( pos ) ) {
1759  AI_DEST_UNREACHABLE = true;
1760  return false;
1761  }
1762  }
1763 
1764  if ( move.moveCommand != MOVE_TO_ENEMY ) {
1767  }
1768 
1769  move.moveDest = pos;
1770  move.goalEntity = enemyEnt;
1771  move.speed = fly_speed;
1773  AI_MOVE_DONE = false;
1774  AI_DEST_UNREACHABLE = false;
1775  AI_FORWARD = true;
1776 
1777  return true;
1778 }
1779 
1780 /*
1781 =====================
1782 idAI::MoveToEntity
1783 =====================
1784 */
1786  int areaNum;
1787  aasPath_t path;
1788  idVec3 pos;
1789 
1790  if ( !ent ) {
1792  return false;
1793  }
1794 
1795  pos = ent->GetPhysics()->GetOrigin();
1796  if ( ( move.moveType != MOVETYPE_FLY ) && ( ( move.moveCommand != MOVE_TO_ENTITY ) || ( move.goalEntityOrigin != pos ) ) ) {
1797  ent->GetFloorPos( 64.0f, pos );
1798  }
1799 
1800  if ( ReachedPos( pos, MOVE_TO_ENTITY ) ) {
1802  return true;
1803  }
1804 
1805  move.toAreaNum = 0;
1806  if ( aas ) {
1809 
1811  if ( !PathToGoal( path, areaNum, physicsObj.GetOrigin(), move.toAreaNum, pos ) ) {
1812  AI_DEST_UNREACHABLE = true;
1813  return false;
1814  }
1815  }
1816 
1817  if ( !move.toAreaNum ) {
1818  // if only trying to update the entity position
1819  if ( move.moveCommand == MOVE_TO_ENTITY ) {
1820  if ( !aas ) {
1821  // keep the move destination up to date for wandering
1822  move.moveDest = pos;
1823  }
1824  return false;
1825  }
1826 
1827  if ( !NewWanderDir( pos ) ) {
1829  AI_DEST_UNREACHABLE = true;
1830  return false;
1831  }
1832  }
1833 
1834  if ( ( move.moveCommand != MOVE_TO_ENTITY ) || ( move.goalEntity.GetEntity() != ent ) ) {
1836  move.goalEntity = ent;
1838  }
1839 
1840  move.moveDest = pos;
1843  move.speed = fly_speed;
1844  AI_MOVE_DONE = false;
1845  AI_DEST_UNREACHABLE = false;
1846  AI_FORWARD = true;
1847 
1848  return true;
1849 }
1850 
1851 /*
1852 =====================
1853 idAI::MoveOutOfRange
1854 =====================
1855 */
1856 bool idAI::MoveOutOfRange( idEntity *ent, float range ) {
1857  int areaNum;
1858  aasObstacle_t obstacle;
1859  aasGoal_t goal;
1860  idBounds bounds;
1861  idVec3 pos;
1862 
1863  if ( !aas || !ent ) {
1865  AI_DEST_UNREACHABLE = true;
1866  return false;
1867  }
1868 
1869  const idVec3 &org = physicsObj.GetOrigin();
1870  areaNum = PointReachableAreaNum( org );
1871 
1872  // consider the entity the monster is getting close to as an obstacle
1873  obstacle.absBounds = ent->GetPhysics()->GetAbsBounds();
1874 
1875  if ( ent == enemy.GetEntity() ) {
1876  pos = lastVisibleEnemyPos;
1877  } else {
1878  pos = ent->GetPhysics()->GetOrigin();
1879  }
1880 
1881  idAASFindAreaOutOfRange findGoal( pos, range );
1882  if ( !aas->FindNearestGoal( goal, areaNum, org, pos, travelFlags, &obstacle, 1, findGoal ) ) {
1884  AI_DEST_UNREACHABLE = true;
1885  return false;
1886  }
1887 
1888  if ( ReachedPos( goal.origin, move.moveCommand ) ) {
1890  return true;
1891  }
1892 
1893  move.moveDest = goal.origin;
1894  move.toAreaNum = goal.areaNum;
1895  move.goalEntity = ent;
1898  move.range = range;
1899  move.speed = fly_speed;
1901  AI_MOVE_DONE = false;
1902  AI_DEST_UNREACHABLE = false;
1903  AI_FORWARD = true;
1904 
1905  return true;
1906 }
1907 
1908 /*
1909 =====================
1910 idAI::MoveToAttackPosition
1911 =====================
1912 */
1913 bool idAI::MoveToAttackPosition( idEntity *ent, int attack_anim ) {
1914  int areaNum;
1915  aasObstacle_t obstacle;
1916  aasGoal_t goal;
1917  idBounds bounds;
1918  idVec3 pos;
1919 
1920  if ( !aas || !ent ) {
1922  AI_DEST_UNREACHABLE = true;
1923  return false;
1924  }
1925 
1926  const idVec3 &org = physicsObj.GetOrigin();
1927  areaNum = PointReachableAreaNum( org );
1928 
1929  // consider the entity the monster is getting close to as an obstacle
1930  obstacle.absBounds = ent->GetPhysics()->GetAbsBounds();
1931 
1932  if ( ent == enemy.GetEntity() ) {
1933  pos = lastVisibleEnemyPos;
1934  } else {
1935  pos = ent->GetPhysics()->GetOrigin();
1936  }
1937 
1938  idAASFindAttackPosition findGoal( this, physicsObj.GetGravityAxis(), ent, pos, missileLaunchOffset[ attack_anim ] );
1939  if ( !aas->FindNearestGoal( goal, areaNum, org, pos, travelFlags, &obstacle, 1, findGoal ) ) {
1941  AI_DEST_UNREACHABLE = true;
1942  return false;
1943  }
1944 
1945  move.moveDest = goal.origin;
1946  move.toAreaNum = goal.areaNum;
1947  move.goalEntity = ent;
1950  move.speed = fly_speed;
1952  move.anim = attack_anim;
1953  AI_MOVE_DONE = false;
1954  AI_DEST_UNREACHABLE = false;
1955  AI_FORWARD = true;
1956 
1957  return true;
1958 }
1959 
1960 /*
1961 =====================
1962 idAI::MoveToPosition
1963 =====================
1964 */
1965 bool idAI::MoveToPosition( const idVec3 &pos ) {
1966  idVec3 org;
1967  int areaNum;
1968  aasPath_t path;
1969 
1970  if ( ReachedPos( pos, move.moveCommand ) ) {
1972  return true;
1973  }
1974 
1975  org = pos;
1976  move.toAreaNum = 0;
1977  if ( aas ) {
1980 
1982  if ( !PathToGoal( path, areaNum, physicsObj.GetOrigin(), move.toAreaNum, org ) ) {
1984  AI_DEST_UNREACHABLE = true;
1985  return false;
1986  }
1987  }
1988 
1989  if ( !move.toAreaNum && !NewWanderDir( org ) ) {
1991  AI_DEST_UNREACHABLE = true;
1992  return false;
1993  }
1994 
1995  move.moveDest = org;
1996  move.goalEntity = NULL;
2000  move.speed = fly_speed;
2001  AI_MOVE_DONE = false;
2002  AI_DEST_UNREACHABLE = false;
2003  AI_FORWARD = true;
2004 
2005  return true;
2006 }
2007 
2008 /*
2009 =====================
2010 idAI::MoveToCover
2011 =====================
2012 */
2013 bool idAI::MoveToCover( idEntity *entity, const idVec3 &hideFromPos ) {
2014  int areaNum;
2015  aasObstacle_t obstacle;
2016  aasGoal_t hideGoal;
2017  idBounds bounds;
2018 
2019  if ( !aas || !entity ) {
2021  AI_DEST_UNREACHABLE = true;
2022  return false;
2023  }
2024 
2025  const idVec3 &org = physicsObj.GetOrigin();
2026  areaNum = PointReachableAreaNum( org );
2027 
2028  // consider the entity the monster tries to hide from as an obstacle
2029  obstacle.absBounds = entity->GetPhysics()->GetAbsBounds();
2030 
2031  idAASFindCover findCover( hideFromPos );
2032  if ( !aas->FindNearestGoal( hideGoal, areaNum, org, hideFromPos, travelFlags, &obstacle, 1, findCover ) ) {
2034  AI_DEST_UNREACHABLE = true;
2035  return false;
2036  }
2037 
2038  if ( ReachedPos( hideGoal.origin, move.moveCommand ) ) {
2040  return true;
2041  }
2042 
2043  move.moveDest = hideGoal.origin;
2044  move.toAreaNum = hideGoal.areaNum;
2045  move.goalEntity = entity;
2049  move.speed = fly_speed;
2050  AI_MOVE_DONE = false;
2051  AI_DEST_UNREACHABLE = false;
2052  AI_FORWARD = true;
2053 
2054  return true;
2055 }
2056 
2057 /*
2058 =====================
2059 idAI::SlideToPosition
2060 =====================
2061 */
2062 bool idAI::SlideToPosition( const idVec3 &pos, float time ) {
2064 
2065  move.moveDest = pos;
2066  move.goalEntity = NULL;
2071  AI_MOVE_DONE = false;
2072  AI_DEST_UNREACHABLE = false;
2073  AI_FORWARD = false;
2074 
2075  if ( move.duration > 0 ) {
2076  move.moveDir = ( pos - physicsObj.GetOrigin() ) / MS2SEC( move.duration );
2077  if ( move.moveType != MOVETYPE_FLY ) {
2078  move.moveDir.z = 0.0f;
2079  }
2081  }
2082 
2083  return true;
2084 }
2085 
2086 /*
2087 =====================
2088 idAI::WanderAround
2089 =====================
2090 */
2091 bool idAI::WanderAround( void ) {
2093 
2095  if ( !NewWanderDir( move.moveDest ) ) {
2097  AI_DEST_UNREACHABLE = true;
2098  return false;
2099  }
2100 
2104  move.speed = fly_speed;
2105  AI_MOVE_DONE = false;
2106  AI_FORWARD = true;
2107 
2108  return true;
2109 }
2110 
2111 /*
2112 =====================
2113 idAI::MoveDone
2114 =====================
2115 */
2116 bool idAI::MoveDone( void ) const {
2117  return ( move.moveCommand == MOVE_NONE );
2118 }
2119 
2120 /*
2121 ================
2122 idAI::StepDirection
2123 ================
2124 */
2125 bool idAI::StepDirection( float dir ) {
2127  idVec3 org;
2128 
2129  move.wanderYaw = dir;
2131 
2132  org = physicsObj.GetOrigin();
2133 
2134  idAI::PredictPath( this, aas, org, move.moveDir * 48.0f, 1000, 1000, ( move.moveType == MOVETYPE_FLY ) ? SE_BLOCKED : ( SE_ENTER_OBSTACLE | SE_BLOCKED | SE_ENTER_LEDGE_AREA ), path );
2135 
2136  if ( path.blockingEntity && ( ( move.moveCommand == MOVE_TO_ENEMY ) || ( move.moveCommand == MOVE_TO_ENTITY ) ) && ( path.blockingEntity == move.goalEntity.GetEntity() ) ) {
2137  // don't report being blocked if we ran into our goal entity
2138  return true;
2139  }
2140 
2141  if ( ( move.moveType == MOVETYPE_FLY ) && ( path.endEvent == SE_BLOCKED ) ) {
2142  float z;
2143 
2144  move.moveDir = path.endVelocity * 1.0f / 48.0f;
2145 
2146  // trace down to the floor and see if we can go forward
2147  idAI::PredictPath( this, aas, org, idVec3( 0.0f, 0.0f, -1024.0f ), 1000, 1000, SE_BLOCKED, path );
2148 
2149  idVec3 floorPos = path.endPos;
2150  idAI::PredictPath( this, aas, floorPos, move.moveDir * 48.0f, 1000, 1000, SE_BLOCKED, path );
2151  if ( !path.endEvent ) {
2152  move.moveDir.z = -1.0f;
2153  return true;
2154  }
2155 
2156  // trace up to see if we can go over something and go forward
2157  idAI::PredictPath( this, aas, org, idVec3( 0.0f, 0.0f, 256.0f ), 1000, 1000, SE_BLOCKED, path );
2158 
2159  idVec3 ceilingPos = path.endPos;
2160 
2161  for( z = org.z; z <= ceilingPos.z + 64.0f; z += 64.0f ) {
2162  idVec3 start;
2163  if ( z <= ceilingPos.z ) {
2164  start.x = org.x;
2165  start.y = org.y;
2166  start.z = z;
2167  } else {
2168  start = ceilingPos;
2169  }
2170  idAI::PredictPath( this, aas, start, move.moveDir * 48.0f, 1000, 1000, SE_BLOCKED, path );
2171  if ( !path.endEvent ) {
2172  move.moveDir.z = 1.0f;
2173  return true;
2174  }
2175  }
2176  return false;
2177  }
2178 
2179  return ( path.endEvent == 0 );
2180 }
2181 
2182 /*
2183 ================
2184 idAI::NewWanderDir
2185 ================
2186 */
2187 bool idAI::NewWanderDir( const idVec3 &dest ) {
2188  float deltax, deltay;
2189  float d[ 3 ];
2190  float tdir, olddir, turnaround;
2191 
2193 
2194  olddir = idMath::AngleNormalize360( ( int )( current_yaw / 45 ) * 45 );
2195  turnaround = idMath::AngleNormalize360( olddir - 180 );
2196 
2197  idVec3 org = physicsObj.GetOrigin();
2198  deltax = dest.x - org.x;
2199  deltay = dest.y - org.y;
2200  if ( deltax > 10 ) {
2201  d[ 1 ]= 0;
2202  } else if ( deltax < -10 ) {
2203  d[ 1 ] = 180;
2204  } else {
2205  d[ 1 ] = DI_NODIR;
2206  }
2207 
2208  if ( deltay < -10 ) {
2209  d[ 2 ] = 270;
2210  } else if ( deltay > 10 ) {
2211  d[ 2 ] = 90;
2212  } else {
2213  d[ 2 ] = DI_NODIR;
2214  }
2215 
2216  // try direct route
2217  if ( d[ 1 ] != DI_NODIR && d[ 2 ] != DI_NODIR ) {
2218  if ( d[ 1 ] == 0 ) {
2219  tdir = d[ 2 ] == 90 ? 45 : 315;
2220  } else {
2221  tdir = d[ 2 ] == 90 ? 135 : 215;
2222  }
2223 
2224  if ( tdir != turnaround && StepDirection( tdir ) ) {
2225  return true;
2226  }
2227  }
2228 
2229  // try other directions
2230  if ( ( gameLocal.random.RandomInt() & 1 ) || abs( deltay ) > abs( deltax ) ) {
2231  tdir = d[ 1 ];
2232  d[ 1 ] = d[ 2 ];
2233  d[ 2 ] = tdir;
2234  }
2235 
2236  if ( d[ 1 ] != DI_NODIR && d[ 1 ] != turnaround && StepDirection( d[1] ) ) {
2237  return true;
2238  }
2239 
2240  if ( d[ 2 ] != DI_NODIR && d[ 2 ] != turnaround && StepDirection( d[ 2 ] ) ) {
2241  return true;
2242  }
2243 
2244  // there is no direct path to the player, so pick another direction
2245  if ( olddir != DI_NODIR && StepDirection( olddir ) ) {
2246  return true;
2247  }
2248 
2249  // randomly determine direction of search
2250  if ( gameLocal.random.RandomInt() & 1 ) {
2251  for( tdir = 0; tdir <= 315; tdir += 45 ) {
2252  if ( tdir != turnaround && StepDirection( tdir ) ) {
2253  return true;
2254  }
2255  }
2256  } else {
2257  for ( tdir = 315; tdir >= 0; tdir -= 45 ) {
2258  if ( tdir != turnaround && StepDirection( tdir ) ) {
2259  return true;
2260  }
2261  }
2262  }
2263 
2264  if ( turnaround != DI_NODIR && StepDirection( turnaround ) ) {
2265  return true;
2266  }
2267 
2268  // can't move
2270  return false;
2271 }
2272 
2273 /*
2274 =====================
2275 idAI::GetMovePos
2276 =====================
2277 */
2278 bool idAI::GetMovePos( idVec3 &seekPos ) {
2279  int areaNum;
2280  aasPath_t path;
2281  bool result;
2282  idVec3 org;
2283 
2284  org = physicsObj.GetOrigin();
2285  seekPos = org;
2286 
2287  switch( move.moveCommand ) {
2288  case MOVE_NONE :
2289  seekPos = move.moveDest;
2290  return false;
2291  break;
2292 
2293  case MOVE_FACE_ENEMY :
2294  case MOVE_FACE_ENTITY :
2295  seekPos = move.moveDest;
2296  return false;
2297  break;
2298 
2300  seekPos = move.moveDest;
2301  if ( ReachedPos( move.moveDest, move.moveCommand ) ) {
2303  }
2304  return false;
2305  break;
2306 
2307  case MOVE_SLIDE_TO_POSITION :
2308  seekPos = org;
2309  return false;
2310  break;
2311  }
2312 
2313  if ( move.moveCommand == MOVE_TO_ENTITY ) {
2315  }
2316 
2318  result = false;
2319  if ( gameLocal.time > move.blockTime ) {
2320  if ( move.moveCommand == MOVE_WANDER ) {
2321  move.moveDest = org + viewAxis[ 0 ] * physicsObj.GetGravityAxis() * 256.0f;
2322  } else {
2323  if ( ReachedPos( move.moveDest, move.moveCommand ) ) {
2325  seekPos = org;
2326  return false;
2327  }
2328  }
2329 
2330  if ( aas && move.toAreaNum ) {
2331  areaNum = PointReachableAreaNum( org );
2332  if ( PathToGoal( path, areaNum, org, move.toAreaNum, move.moveDest ) ) {
2333  seekPos = path.moveGoal;
2334  result = true;
2335  move.nextWanderTime = 0;
2336  } else {
2337  AI_DEST_UNREACHABLE = true;
2338  }
2339  }
2340  }
2341 
2342  if ( !result ) {
2343  // wander around
2345  result = NewWanderDir( move.moveDest );
2346  if ( !result ) {
2348  AI_DEST_UNREACHABLE = true;
2349  seekPos = org;
2350  return false;
2351  }
2352  } else {
2353  result = true;
2354  }
2355 
2356  seekPos = org + move.moveDir * 2048.0f;
2357  if ( ai_debugMove.GetBool() ) {
2358  gameRenderWorld->DebugLine( colorYellow, org, seekPos, gameLocal.msec, true );
2359  }
2360  } else {
2361  AI_DEST_UNREACHABLE = false;
2362  }
2363 
2364  if ( result && ( ai_debugMove.GetBool() ) ) {
2366  }
2367 
2368  return result;
2369 }
2370 
2371 /*
2372 =====================
2373 idAI::EntityCanSeePos
2374 =====================
2375 */
2376 bool idAI::EntityCanSeePos( idActor *actor, const idVec3 &actorOrigin, const idVec3 &pos ) {
2377  idVec3 eye, point;
2378  trace_t results;
2379  pvsHandle_t handle;
2380 
2381  handle = gameLocal.pvs.SetupCurrentPVS( actor->GetPVSAreas(), actor->GetNumPVSAreas() );
2382 
2383  if ( !gameLocal.pvs.InCurrentPVS( handle, GetPVSAreas(), GetNumPVSAreas() ) ) {
2384  gameLocal.pvs.FreeCurrentPVS( handle );
2385  return false;
2386  }
2387 
2388  gameLocal.pvs.FreeCurrentPVS( handle );
2389 
2390  eye = actorOrigin + actor->EyeOffset();
2391 
2392  point = pos;
2393  point[2] += 1.0f;
2394 
2396 
2397  gameLocal.clip.TracePoint( results, eye, point, MASK_SOLID, actor );
2398  if ( results.fraction >= 1.0f || ( gameLocal.GetTraceEntity( results ) == this ) ) {
2400  return true;
2401  }
2402 
2403  const idBounds &bounds = physicsObj.GetBounds();
2404  point[2] += bounds[1][2] - bounds[0][2];
2405 
2406  gameLocal.clip.TracePoint( results, eye, point, MASK_SOLID, actor );
2408  if ( results.fraction >= 1.0f || ( gameLocal.GetTraceEntity( results ) == this ) ) {
2409  return true;
2410  }
2411  return false;
2412 }
2413 
2414 /*
2415 =====================
2416 idAI::BlockedFailSafe
2417 =====================
2418 */
2420  if ( !ai_blockedFailSafe.GetBool() || blockedRadius < 0.0f ) {
2421  return;
2422  }
2423  if ( !physicsObj.OnGround() || enemy.GetEntity() == NULL ||
2424  ( physicsObj.GetOrigin() - move.lastMoveOrigin ).LengthSqr() > Square( blockedRadius ) ) {
2427  }
2430  AI_BLOCKED = true;
2432  }
2433  }
2434 }
2435 
2436 /***********************************************************************
2437 
2438  turning
2439 
2440 ***********************************************************************/
2441 
2442 /*
2443 =====================
2444 idAI::Turn
2445 =====================
2446 */
2447 void idAI::Turn( void ) {
2448  float diff;
2449  float diff2;
2450  float turnAmount;
2451  animFlags_t animflags;
2452 
2453  if ( !turnRate ) {
2454  return;
2455  }
2456 
2457  // check if the animator has marker this anim as non-turning
2458  if ( !legsAnim.Disabled() && !legsAnim.AnimDone( 0 ) ) {
2459  animflags = legsAnim.GetAnimFlags();
2460  } else {
2461  animflags = torsoAnim.GetAnimFlags();
2462  }
2463  if ( animflags.ai_no_turn ) {
2464  return;
2465  }
2466 
2467  if ( anim_turn_angles && animflags.anim_turn ) {
2468  idMat3 rotateAxis;
2469 
2470  // set the blend between no turn and full turn
2471  float frac = anim_turn_amount / anim_turn_angles;
2476 
2477  // get the total rotation from the start of the anim
2478  animator.GetDeltaRotation( 0, gameLocal.time, rotateAxis );
2479  current_yaw = idMath::AngleNormalize180( anim_turn_yaw + rotateAxis[ 0 ].ToYaw() );
2480  } else {
2482  turnVel += AI_TURN_SCALE * diff * MS2SEC( gameLocal.msec );
2483  if ( turnVel > turnRate ) {
2484  turnVel = turnRate;
2485  } else if ( turnVel < -turnRate ) {
2486  turnVel = -turnRate;
2487  }
2488  turnAmount = turnVel * MS2SEC( gameLocal.msec );
2489  if ( ( diff >= 0.0f ) && ( turnAmount >= diff ) ) {
2490  turnVel = diff / MS2SEC( gameLocal.msec );
2491  turnAmount = diff;
2492  } else if ( ( diff <= 0.0f ) && ( turnAmount <= diff ) ) {
2493  turnVel = diff / MS2SEC( gameLocal.msec );
2494  turnAmount = diff;
2495  }
2496  current_yaw += turnAmount;
2499  if ( idMath::Fabs( diff2 ) < 0.1f ) {
2501  }
2502  }
2503 
2504  viewAxis = idAngles( 0, current_yaw, 0 ).ToMat3();
2505 
2506  if ( ai_debugMove.GetBool() ) {
2507  const idVec3 &org = physicsObj.GetOrigin();
2508  gameRenderWorld->DebugLine( colorRed, org, org + idAngles( 0, ideal_yaw, 0 ).ToForward() * 64, gameLocal.msec );
2509  gameRenderWorld->DebugLine( colorGreen, org, org + idAngles( 0, current_yaw, 0 ).ToForward() * 48, gameLocal.msec );
2510  gameRenderWorld->DebugLine( colorYellow, org, org + idAngles( 0, current_yaw + turnVel, 0 ).ToForward() * 32, gameLocal.msec );
2511  }
2512 }
2513 
2514 /*
2515 =====================
2516 idAI::FacingIdeal
2517 =====================
2518 */
2519 bool idAI::FacingIdeal( void ) {
2520  float diff;
2521 
2522  if ( !turnRate ) {
2523  return true;
2524  }
2525 
2527  if ( idMath::Fabs( diff ) < 0.01f ) {
2528  // force it to be exact
2530  return true;
2531  }
2532 
2533  return false;
2534 }
2535 
2536 /*
2537 =====================
2538 idAI::TurnToward
2539 =====================
2540 */
2541 bool idAI::TurnToward( float yaw ) {
2543  bool result = FacingIdeal();
2544  return result;
2545 }
2546 
2547 /*
2548 =====================
2549 idAI::TurnToward
2550 =====================
2551 */
2552 bool idAI::TurnToward( const idVec3 &pos ) {
2553  idVec3 dir;
2554  idVec3 local_dir;
2555  float lengthSqr;
2556 
2557  dir = pos - physicsObj.GetOrigin();
2558  physicsObj.GetGravityAxis().ProjectVector( dir, local_dir );
2559  local_dir.z = 0.0f;
2560  lengthSqr = local_dir.LengthSqr();
2561  if ( lengthSqr > Square( 2.0f ) || ( lengthSqr > Square( 0.1f ) && enemy.GetEntity() == NULL ) ) {
2562  ideal_yaw = idMath::AngleNormalize180( local_dir.ToYaw() );
2563  }
2564 
2565  bool result = FacingIdeal();
2566  return result;
2567 }
2568 
2569 /***********************************************************************
2570 
2571  Movement
2572 
2573 ***********************************************************************/
2574 
2575 /*
2576 ================
2577 idAI::ApplyImpulse
2578 ================
2579 */
2580 void idAI::ApplyImpulse( idEntity *ent, int id, const idVec3 &point, const idVec3 &impulse ) {
2581  // FIXME: Jim take a look at this and see if this is a reasonable thing to do
2582  // instead of a spawnArg flag.. Sabaoth is the only slide monster ( and should be the only one for D3 )
2583  // and we don't want him taking physics impulses as it can knock him off the path
2585  idActor::ApplyImpulse( ent, id, point, impulse );
2586  }
2587 }
2588 
2589 /*
2590 =====================
2591 idAI::GetMoveDelta
2592 =====================
2593 */
2594 void idAI::GetMoveDelta( const idMat3 &oldaxis, const idMat3 &axis, idVec3 &delta ) {
2595  idVec3 oldModelOrigin;
2596  idVec3 modelOrigin;
2597 
2599  delta = axis * delta;
2600 
2601  if ( modelOffset != vec3_zero ) {
2602  // the pivot of the monster's model is around its origin, and not around the bounding
2603  // box's origin, so we have to compensate for this when the model is offset so that
2604  // the monster still appears to rotate around it's origin.
2605  oldModelOrigin = modelOffset * oldaxis;
2606  modelOrigin = modelOffset * axis;
2607  delta += oldModelOrigin - modelOrigin;
2608  }
2609 
2610  delta *= physicsObj.GetGravityAxis();
2611 }
2612 
2613 /*
2614 =====================
2615 idAI::CheckObstacleAvoidance
2616 =====================
2617 */
2618 void idAI::CheckObstacleAvoidance( const idVec3 &goalPos, idVec3 &newPos ) {
2619  idEntity *obstacle;
2621  idVec3 dir;
2622  float dist;
2623  bool foundPath;
2624 
2625  if ( ignore_obstacles ) {
2626  newPos = goalPos;
2627  move.obstacle = NULL;
2628  return;
2629  }
2630 
2631  const idVec3 &origin = physicsObj.GetOrigin();
2632 
2633  obstacle = NULL;
2634  AI_OBSTACLE_IN_PATH = false;
2635  foundPath = FindPathAroundObstacles( &physicsObj, aas, enemy.GetEntity(), origin, goalPos, path );
2637  gameRenderWorld->DebugLine( colorBlue, goalPos + idVec3( 1.0f, 1.0f, 0.0f ), goalPos + idVec3( 1.0f, 1.0f, 64.0f ), gameLocal.msec );
2638  gameRenderWorld->DebugLine( foundPath ? colorYellow : colorRed, path.seekPos, path.seekPos + idVec3( 0.0f, 0.0f, 64.0f ), gameLocal.msec );
2639  }
2640 
2641  if ( !foundPath ) {
2642  // couldn't get around obstacles
2643  if ( path.firstObstacle ) {
2644  AI_OBSTACLE_IN_PATH = true;
2646  obstacle = path.firstObstacle;
2647  }
2648  } else if ( path.startPosObstacle ) {
2649  AI_OBSTACLE_IN_PATH = true;
2651  obstacle = path.startPosObstacle;
2652  }
2653  } else {
2654  // Blocked by wall
2656  }
2657 #if 0
2658  } else if ( path.startPosObstacle ) {
2659  // check if we're past where the our origin was pushed out of the obstacle
2660  dir = goalPos - origin;
2661  dir.Normalize();
2662  dist = ( path.seekPos - origin ) * dir;
2663  if ( dist < 1.0f ) {
2664  AI_OBSTACLE_IN_PATH = true;
2665  obstacle = path.startPosObstacle;
2666  }
2667 #endif
2668  } else if ( path.seekPosObstacle ) {
2669  // if the AI is very close to the path.seekPos already and path.seekPosObstacle != NULL
2670  // then we want to push the path.seekPosObstacle entity out of the way
2671  AI_OBSTACLE_IN_PATH = true;
2672 
2673  // check if we're past where the goalPos was pushed out of the obstacle
2674  dir = goalPos - origin;
2675  dir.Normalize();
2676  dist = ( path.seekPos - origin ) * dir;
2677  if ( dist < 1.0f ) {
2678  obstacle = path.seekPosObstacle;
2679  }
2680  }
2681 
2682  // if we had an obstacle, set our move status based on the type, and kick it out of the way if it's a moveable
2683  if ( obstacle ) {
2684  if ( obstacle->IsType( idActor::Type ) ) {
2685  // monsters aren't kickable
2686  if ( obstacle == enemy.GetEntity() ) {
2688  } else {
2690  }
2691  } else {
2692  // try kicking the object out of the way
2694  }
2695  newPos = obstacle->GetPhysics()->GetOrigin();
2696  //newPos = path.seekPos;
2697  move.obstacle = obstacle;
2698  } else {
2699  newPos = path.seekPos;
2700  move.obstacle = NULL;
2701  }
2702 }
2703 
2704 /*
2705 =====================
2706 idAI::DeadMove
2707 =====================
2708 */
2709 void idAI::DeadMove( void ) {
2710  idVec3 delta;
2711  monsterMoveResult_t moveResult;
2712 
2713  idVec3 org = physicsObj.GetOrigin();
2714 
2715  GetMoveDelta( viewAxis, viewAxis, delta );
2716  physicsObj.SetDelta( delta );
2717 
2718  RunPhysics();
2719 
2720  moveResult = physicsObj.GetMoveResult();
2722 }
2723 
2724 /*
2725 =====================
2726 idAI::AnimMove
2727 =====================
2728 */
2729 void idAI::AnimMove( void ) {
2730  idVec3 goalPos;
2731  idVec3 delta;
2732  idVec3 goalDelta;
2733  float goalDist;
2734  monsterMoveResult_t moveResult;
2735  idVec3 newDest;
2736 
2737  idVec3 oldorigin = physicsObj.GetOrigin();
2738  idMat3 oldaxis = viewAxis;
2739 
2740  AI_BLOCKED = false;
2741 
2745  }
2746 
2747  move.obstacle = NULL;
2748  if ( ( move.moveCommand == MOVE_FACE_ENEMY ) && enemy.GetEntity() ) {
2750  goalPos = oldorigin;
2751  } else if ( ( move.moveCommand == MOVE_FACE_ENTITY ) && move.goalEntity.GetEntity() ) {
2753  goalPos = oldorigin;
2754  } else if ( GetMovePos( goalPos ) ) {
2755  if ( move.moveCommand != MOVE_WANDER ) {
2756  CheckObstacleAvoidance( goalPos, newDest );
2757  TurnToward( newDest );
2758  } else {
2759  TurnToward( goalPos );
2760  }
2761  }
2762 
2763  Turn();
2764 
2766  if ( gameLocal.time < move.startTime + move.duration ) {
2768  delta = goalPos - oldorigin;
2769  delta.z = 0.0f;
2770  } else {
2771  delta = move.moveDest - oldorigin;
2772  delta.z = 0.0f;
2774  }
2775  } else if ( allowMove ) {
2776  GetMoveDelta( oldaxis, viewAxis, delta );
2777  } else {
2778  delta.Zero();
2779  }
2780 
2781  if ( move.moveCommand == MOVE_TO_POSITION ) {
2782  goalDelta = move.moveDest - oldorigin;
2783  goalDist = goalDelta.LengthFast();
2784  if ( goalDist < delta.LengthFast() ) {
2785  delta = goalDelta;
2786  }
2787  }
2788 
2789 #ifdef _D3XP
2790  physicsObj.UseFlyMove( false );
2791 #endif
2792  physicsObj.SetDelta( delta );
2794 
2795  RunPhysics();
2796 
2797  if ( ai_debugMove.GetBool() ) {
2798  gameRenderWorld->DebugLine( colorCyan, oldorigin, physicsObj.GetOrigin(), 5000 );
2799  }
2800 
2801  moveResult = physicsObj.GetMoveResult();
2802  if ( !af_push_moveables && attack.Length() && TestMelee() ) {
2804  } else {
2805  idEntity *blockEnt = physicsObj.GetSlideMoveEntity();
2806  if ( blockEnt && blockEnt->IsType( idMoveable::Type ) && blockEnt->GetPhysics()->IsPushable() ) {
2807  KickObstacles( viewAxis[ 0 ], kickForce, blockEnt );
2808  }
2809  }
2810 
2811  BlockedFailSafe();
2812 
2814 
2815  idVec3 org = physicsObj.GetOrigin();
2816  if ( oldorigin != org ) {
2817  TouchTriggers();
2818  }
2819 
2820  if ( ai_debugMove.GetBool() ) {
2823  gameRenderWorld->DebugLine( colorYellow, org + EyeOffset(), org + EyeOffset() + viewAxis[ 0 ] * physicsObj.GetGravityAxis() * 16.0f, gameLocal.msec, true );
2824  DrawRoute();
2825  }
2826 }
2827 
2828 /*
2829 =====================
2830 Seek
2831 =====================
2832 */
2833 idVec3 Seek( idVec3 &vel, const idVec3 &org, const idVec3 &goal, float prediction ) {
2834  idVec3 predictedPos;
2835  idVec3 goalDelta;
2836  idVec3 seekVel;
2837 
2838  // predict our position
2839  predictedPos = org + vel * prediction;
2840  goalDelta = goal - predictedPos;
2841  seekVel = goalDelta * MS2SEC( gameLocal.msec );
2842 
2843  return seekVel;
2844 }
2845 
2846 /*
2847 =====================
2848 idAI::SlideMove
2849 =====================
2850 */
2851 void idAI::SlideMove( void ) {
2852  idVec3 goalPos;
2853  idVec3 delta;
2854  idVec3 goalDelta;
2855  float goalDist;
2856  monsterMoveResult_t moveResult;
2857  idVec3 newDest;
2858 
2859  idVec3 oldorigin = physicsObj.GetOrigin();
2860  idMat3 oldaxis = viewAxis;
2861 
2862  AI_BLOCKED = false;
2863 
2867  }
2868 
2869  move.obstacle = NULL;
2870  if ( ( move.moveCommand == MOVE_FACE_ENEMY ) && enemy.GetEntity() ) {
2872  goalPos = move.moveDest;
2873  } else if ( ( move.moveCommand == MOVE_FACE_ENTITY ) && move.goalEntity.GetEntity() ) {
2875  goalPos = move.moveDest;
2876  } else if ( GetMovePos( goalPos ) ) {
2877  CheckObstacleAvoidance( goalPos, newDest );
2878  TurnToward( newDest );
2879  goalPos = newDest;
2880  }
2881 
2883  if ( gameLocal.time < move.startTime + move.duration ) {
2885  } else {
2886  goalPos = move.moveDest;
2888  }
2889  }
2890 
2891  if ( move.moveCommand == MOVE_TO_POSITION ) {
2892  goalDelta = move.moveDest - oldorigin;
2893  goalDist = goalDelta.LengthFast();
2894  if ( goalDist < delta.LengthFast() ) {
2895  delta = goalDelta;
2896  }
2897  }
2898 
2900  float z = vel.z;
2901  idVec3 predictedPos = oldorigin + vel * AI_SEEK_PREDICTION;
2902 
2903  // seek the goal position
2904  goalDelta = goalPos - predictedPos;
2905  vel -= vel * AI_FLY_DAMPENING * MS2SEC( gameLocal.msec );
2906  vel += goalDelta * MS2SEC( gameLocal.msec );
2907 
2908  // cap our speed
2909  vel.Truncate( fly_speed );
2910  vel.z = z;
2912  physicsObj.UseVelocityMove( true );
2913  RunPhysics();
2914 
2915  if ( ( move.moveCommand == MOVE_FACE_ENEMY ) && enemy.GetEntity() ) {
2917  } else if ( ( move.moveCommand == MOVE_FACE_ENTITY ) && move.goalEntity.GetEntity() ) {
2919  } else if ( move.moveCommand != MOVE_NONE ) {
2920  if ( vel.ToVec2().LengthSqr() > 0.1f ) {
2921  TurnToward( vel.ToYaw() );
2922  }
2923  }
2924  Turn();
2925 
2926  if ( ai_debugMove.GetBool() ) {
2927  gameRenderWorld->DebugLine( colorCyan, oldorigin, physicsObj.GetOrigin(), 5000 );
2928  }
2929 
2930  moveResult = physicsObj.GetMoveResult();
2931  if ( !af_push_moveables && attack.Length() && TestMelee() ) {
2933  } else {
2934  idEntity *blockEnt = physicsObj.GetSlideMoveEntity();
2935  if ( blockEnt && blockEnt->IsType( idMoveable::Type ) && blockEnt->GetPhysics()->IsPushable() ) {
2936  KickObstacles( viewAxis[ 0 ], kickForce, blockEnt );
2937  }
2938  }
2939 
2940  BlockedFailSafe();
2941 
2943 
2944  idVec3 org = physicsObj.GetOrigin();
2945  if ( oldorigin != org ) {
2946  TouchTriggers();
2947  }
2948 
2949  if ( ai_debugMove.GetBool() ) {
2952  gameRenderWorld->DebugLine( colorYellow, org + EyeOffset(), org + EyeOffset() + viewAxis[ 0 ] * physicsObj.GetGravityAxis() * 16.0f, gameLocal.msec, true );
2953  DrawRoute();
2954  }
2955 }
2956 
2957 /*
2958 =====================
2959 idAI::AdjustFlyingAngles
2960 =====================
2961 */
2963  idVec3 vel;
2964  float speed;
2965  float roll;
2966  float pitch;
2967 
2968  vel = physicsObj.GetLinearVelocity();
2969 
2970  speed = vel.Length();
2971  if ( speed < 5.0f ) {
2972  roll = 0.0f;
2973  pitch = 0.0f;
2974  } else {
2975  roll = vel * viewAxis[ 1 ] * -fly_roll_scale / fly_speed;
2976  if ( roll > fly_roll_max ) {
2977  roll = fly_roll_max;
2978  } else if ( roll < -fly_roll_max ) {
2979  roll = -fly_roll_max;
2980  }
2981 
2982  pitch = vel * viewAxis[ 2 ] * -fly_pitch_scale / fly_speed;
2983  if ( pitch > fly_pitch_max ) {
2984  pitch = fly_pitch_max;
2985  } else if ( pitch < -fly_pitch_max ) {
2986  pitch = -fly_pitch_max;
2987  }
2988  }
2989 
2990  fly_roll = fly_roll * 0.95f + roll * 0.05f;
2991  fly_pitch = fly_pitch * 0.95f + pitch * 0.05f;
2992 
2993  if ( flyTiltJoint != INVALID_JOINT ) {
2995  } else {
2997  }
2998 }
2999 
3000 /*
3001 =====================
3002 idAI::AddFlyBob
3003 =====================
3004 */
3005 void idAI::AddFlyBob( idVec3 &vel ) {
3006  idVec3 fly_bob_add;
3007  float t;
3008 
3009  if ( fly_bob_strength ) {
3010  t = MS2SEC( gameLocal.time + entityNumber * 497 );
3011  fly_bob_add = ( viewAxis[ 1 ] * idMath::Sin16( t * fly_bob_horz ) + viewAxis[ 2 ] * idMath::Sin16( t * fly_bob_vert ) ) * fly_bob_strength;
3012  vel += fly_bob_add * MS2SEC( gameLocal.msec );
3013  if ( ai_debugMove.GetBool() ) {
3014  const idVec3 &origin = physicsObj.GetOrigin();
3015  gameRenderWorld->DebugArrow( colorOrange, origin, origin + fly_bob_add, 0 );
3016  }
3017  }
3018 }
3019 
3020 /*
3021 =====================
3022 idAI::AdjustFlyHeight
3023 =====================
3024 */
3025 void idAI::AdjustFlyHeight( idVec3 &vel, const idVec3 &goalPos ) {
3026  const idVec3 &origin = physicsObj.GetOrigin();
3028  idVec3 end;
3029  idVec3 dest;
3030  trace_t trace;
3031  idActor *enemyEnt;
3032  bool goLower;
3033 
3034  // make sure we're not flying too high to get through doors
3035  goLower = false;
3036  if ( origin.z > goalPos.z ) {
3037  dest = goalPos;
3038  dest.z = origin.z + 128.0f;
3039  idAI::PredictPath( this, aas, goalPos, dest - origin, 1000, 1000, SE_BLOCKED, path );
3040  if ( path.endPos.z < origin.z ) {
3041  idVec3 addVel = Seek( vel, origin, path.endPos, AI_SEEK_PREDICTION );
3042  vel.z += addVel.z;
3043  goLower = true;
3044  }
3045 
3046  if ( ai_debugMove.GetBool() ) {
3048  }
3049  }
3050 
3051  if ( !goLower ) {
3052  // make sure we don't fly too low
3053  end = origin;
3054 
3055  enemyEnt = enemy.GetEntity();
3056  if ( enemyEnt ) {
3058  } else {
3059  // just use the default eye height for the player
3060  end.z = goalPos.z + DEFAULT_FLY_OFFSET + fly_offset;
3061  }
3062 
3064  vel += Seek( vel, origin, trace.endpos, AI_SEEK_PREDICTION );
3065  }
3066 }
3067 
3068 /*
3069 =====================
3070 idAI::FlySeekGoal
3071 =====================
3072 */
3073 void idAI::FlySeekGoal( idVec3 &vel, idVec3 &goalPos ) {
3074  idVec3 seekVel;
3075 
3076  // seek the goal position
3077  seekVel = Seek( vel, physicsObj.GetOrigin(), goalPos, AI_SEEK_PREDICTION );
3078  seekVel *= fly_seek_scale;
3079  vel += seekVel;
3080 }
3081 
3082 /*
3083 =====================
3084 idAI::AdjustFlySpeed
3085 =====================
3086 */
3088  float speed;
3089 
3090  // apply dampening
3091  vel -= vel * AI_FLY_DAMPENING * MS2SEC( gameLocal.msec );
3092 
3093  // gradually speed up/slow down to desired speed
3094  speed = vel.Normalize();
3095  speed += ( move.speed - speed ) * MS2SEC( gameLocal.msec );
3096  if ( speed < 0.0f ) {
3097  speed = 0.0f;
3098  } else if ( move.speed && ( speed > move.speed ) ) {
3099  speed = move.speed;
3100  }
3101 
3102  vel *= speed;
3103 }
3104 
3105 /*
3106 =====================
3107 idAI::FlyTurn
3108 =====================
3109 */
3110 void idAI::FlyTurn( void ) {
3111  if ( move.moveCommand == MOVE_FACE_ENEMY ) {
3113  } else if ( ( move.moveCommand == MOVE_FACE_ENTITY ) && move.goalEntity.GetEntity() ) {
3115  } else if ( move.speed > 0.0f ) {
3116  const idVec3 &vel = physicsObj.GetLinearVelocity();
3117  if ( vel.ToVec2().LengthSqr() > 0.1f ) {
3118  TurnToward( vel.ToYaw() );
3119  }
3120  }
3121  Turn();
3122 }
3123 
3124 /*
3125 =====================
3126 idAI::FlyMove
3127 =====================
3128 */
3129 void idAI::FlyMove( void ) {
3130  idVec3 goalPos;
3131  idVec3 oldorigin;
3132  idVec3 newDest;
3133 
3134  AI_BLOCKED = false;
3137  }
3138 
3139  if ( ai_debugMove.GetBool() ) {
3140  gameLocal.Printf( "%d: %s: %s, vel = %.2f, sp = %.2f, maxsp = %.2f\n", gameLocal.time, name.c_str(), moveCommandString[ move.moveCommand ], physicsObj.GetLinearVelocity().Length(), move.speed, fly_speed );
3141  }
3142 
3145 
3146  if ( GetMovePos( goalPos ) ) {
3147  CheckObstacleAvoidance( goalPos, newDest );
3148  goalPos = newDest;
3149  }
3150 
3151  if ( move.speed ) {
3152  FlySeekGoal( vel, goalPos );
3153  }
3154 
3155  // add in bobbing
3156  AddFlyBob( vel );
3157 
3158  if ( enemy.GetEntity() && ( move.moveCommand != MOVE_TO_POSITION ) ) {
3159  AdjustFlyHeight( vel, goalPos );
3160  }
3161 
3162  AdjustFlySpeed( vel );
3163 
3165  }
3166 
3167  // turn
3168  FlyTurn();
3169 
3170  // run the physics for this frame
3171  oldorigin = physicsObj.GetOrigin();
3172  physicsObj.UseFlyMove( true );
3173  physicsObj.UseVelocityMove( false );
3176  RunPhysics();
3177 
3179  if ( !af_push_moveables && attack.Length() && TestMelee() ) {
3181  } else {
3182  idEntity *blockEnt = physicsObj.GetSlideMoveEntity();
3183  if ( blockEnt && blockEnt->IsType( idMoveable::Type ) && blockEnt->GetPhysics()->IsPushable() ) {
3184  KickObstacles( viewAxis[ 0 ], kickForce, blockEnt );
3185  } else if ( moveResult == MM_BLOCKED ) {
3186  move.blockTime = gameLocal.time + 500;
3187  AI_BLOCKED = true;
3188  }
3189  }
3190 
3191  idVec3 org = physicsObj.GetOrigin();
3192  if ( oldorigin != org ) {
3193  TouchTriggers();
3194  }
3195 
3196  if ( ai_debugMove.GetBool() ) {
3197  gameRenderWorld->DebugLine( colorCyan, oldorigin, physicsObj.GetOrigin(), 4000 );
3201  gameRenderWorld->DebugLine( colorBlue, org, goalPos, gameLocal.msec, true );
3202  gameRenderWorld->DebugLine( colorYellow, org + EyeOffset(), org + EyeOffset() + viewAxis[ 0 ] * physicsObj.GetGravityAxis() * 16.0f, gameLocal.msec, true );
3203  DrawRoute();
3204  }
3205 }
3206 
3207 /*
3208 =====================
3209 idAI::StaticMove
3210 =====================
3211 */
3212 void idAI::StaticMove( void ) {
3213  idActor *enemyEnt = enemy.GetEntity();
3214 
3215  if ( AI_DEAD ) {
3216  return;
3217  }
3218 
3219  if ( ( move.moveCommand == MOVE_FACE_ENEMY ) && enemyEnt ) {
3221  } else if ( ( move.moveCommand == MOVE_FACE_ENTITY ) && move.goalEntity.GetEntity() ) {
3223  } else if ( move.moveCommand != MOVE_NONE ) {
3225  }
3226  Turn();
3227 
3228  physicsObj.ForceDeltaMove( true ); // disable gravity
3229  RunPhysics();
3230 
3231  AI_ONGROUND = false;
3232 
3233  if ( !af_push_moveables && attack.Length() && TestMelee() ) {
3234  DirectDamage( attack, enemyEnt );
3235  }
3236 
3237  if ( ai_debugMove.GetBool() ) {
3238  const idVec3 &org = physicsObj.GetOrigin();
3241  gameRenderWorld->DebugLine( colorYellow, org + EyeOffset(), org + EyeOffset() + viewAxis[ 0 ] * physicsObj.GetGravityAxis() * 16.0f, gameLocal.msec, true );
3242  }
3243 }
3244 
3245 /***********************************************************************
3246 
3247  Damage
3248 
3249 ***********************************************************************/
3250 
3251 /*
3252 =====================
3253 idAI::ReactionTo
3254 =====================
3255 */
3256 int idAI::ReactionTo( const idEntity *ent ) {
3257 
3258  if ( ent->fl.hidden ) {
3259  // ignore hidden entities
3260  return ATTACK_IGNORE;
3261  }
3262 
3263  if ( !ent->IsType( idActor::Type ) ) {
3264  return ATTACK_IGNORE;
3265  }
3266 
3267  const idActor *actor = static_cast<const idActor *>( ent );
3268  if ( actor->IsType( idPlayer::Type ) && static_cast<const idPlayer *>(actor)->noclip ) {
3269  // ignore players in noclip mode
3270  return ATTACK_IGNORE;
3271  }
3272 
3273  // actors on different teams will always fight each other
3274  if ( actor->team != team ) {
3275  if ( actor->fl.notarget ) {
3276  // don't attack on sight when attacker is notargeted
3278  }
3280  }
3281 
3282  // monsters will fight when attacked by lower ranked monsters. rank 0 never fights back.
3283  if ( rank && ( actor->rank < rank ) ) {
3284  return ATTACK_ON_DAMAGE;
3285  }
3286 
3287  // don't fight back
3288  return ATTACK_IGNORE;
3289 }
3290 
3291 
3292 /*
3293 =====================
3294 idAI::Pain
3295 =====================
3296 */
3297 bool idAI::Pain( idEntity *inflictor, idEntity *attacker, int damage, const idVec3 &dir, int location ) {
3298  idActor *actor;
3299 
3300  AI_PAIN = idActor::Pain( inflictor, attacker, damage, dir, location );
3301  AI_DAMAGE = true;
3302 
3303  // force a blink
3304  blink_time = 0;
3305 
3306  // ignore damage from self
3307  if ( attacker != this ) {
3308  if ( inflictor ) {
3309  AI_SPECIAL_DAMAGE = inflictor->spawnArgs.GetInt( "special_damage" );
3310  } else {
3311  AI_SPECIAL_DAMAGE = 0;
3312  }
3313 
3314  if ( enemy.GetEntity() != attacker && attacker->IsType( idActor::Type ) ) {
3315  actor = ( idActor * )attacker;
3316  if ( ReactionTo( actor ) & ATTACK_ON_DAMAGE ) {
3317  gameLocal.AlertAI( actor );
3318  SetEnemy( actor );
3319  }
3320  }
3321  }
3322 
3323  return ( AI_PAIN != 0 );
3324 }
3325 
3326 
3327 /*
3328 =====================
3329 idAI::SpawnParticles
3330 =====================
3331 */
3332 void idAI::SpawnParticles( const char *keyName ) {
3333  const idKeyValue *kv = spawnArgs.MatchPrefix( keyName, NULL );
3334  while ( kv ) {
3335  particleEmitter_t pe;
3336 
3337  idStr particleName = kv->GetValue();
3338 
3339  if ( particleName.Length() ) {
3340 
3341  idStr jointName = kv->GetValue();
3342  int dash = jointName.Find('-');
3343  if ( dash > 0 ) {
3344  particleName = particleName.Left( dash );
3345  jointName = jointName.Right( jointName.Length() - dash - 1 );
3346  }
3347 
3348  SpawnParticlesOnJoint( pe, particleName, jointName );
3349  particles.Append( pe );
3350  }
3351 
3352  kv = spawnArgs.MatchPrefix( keyName, kv );
3353  }
3354 }
3355 
3356 /*
3357 =====================
3358 idAI::SpawnParticlesOnJoint
3359 =====================
3360 */
3361 const idDeclParticle *idAI::SpawnParticlesOnJoint( particleEmitter_t &pe, const char *particleName, const char *jointName ) {
3362  idVec3 origin;
3363  idMat3 axis;
3364 
3365  if ( *particleName == '\0' ) {
3366  memset( &pe, 0, sizeof( pe ) );
3367  return pe.particle;
3368  }
3369 
3370  pe.joint = animator.GetJointHandle( jointName );
3371  if ( pe.joint == INVALID_JOINT ) {
3372  gameLocal.Warning( "Unknown particleJoint '%s' on '%s'", jointName, name.c_str() );
3373  pe.time = 0;
3374  pe.particle = NULL;
3375  } else {
3376  animator.GetJointTransform( pe.joint, gameLocal.time, origin, axis );
3377  origin = renderEntity.origin + origin * renderEntity.axis;
3378 
3380  if ( !gameLocal.time ) {
3381  // particles with time of 0 don't show, so set the time differently on the first frame
3382  pe.time = 1;
3383  } else {
3384  pe.time = gameLocal.time;
3385  }
3386  pe.particle = static_cast<const idDeclParticle *>( declManager->FindType( DECL_PARTICLE, particleName ) );
3387  gameLocal.smokeParticles->EmitSmoke( pe.particle, pe.time, gameLocal.random.CRandomFloat(), origin, axis, timeGroup /*_D3XP*/ );
3388  }
3389 
3390  return pe.particle;
3391 }
3392 
3393 /*
3394 =====================
3395 idAI::Killed
3396 =====================
3397 */
3398 void idAI::Killed( idEntity *inflictor, idEntity *attacker, int damage, const idVec3 &dir, int location ) {
3399  idAngles ang;
3400  const char *modelDeath;
3401 
3402  // make sure the monster is activated
3403  EndAttack();
3404 
3405  if ( g_debugDamage.GetBool() ) {
3406  gameLocal.Printf( "Damage: joint: '%s', zone '%s'\n", animator.GetJointName( ( jointHandle_t )location ),
3407  GetDamageGroup( location ) );
3408  }
3409 
3410  if ( inflictor ) {
3411  AI_SPECIAL_DAMAGE = inflictor->spawnArgs.GetInt( "special_damage" );
3412  } else {
3413  AI_SPECIAL_DAMAGE = 0;
3414  }
3415 
3416  if ( AI_DEAD ) {
3417  AI_PAIN = true;
3418  AI_DAMAGE = true;
3419  return;
3420  }
3421 
3422  // stop all voice sounds
3423  StopSound( SND_CHANNEL_VOICE, false );
3424  if ( head.GetEntity() ) {
3427  }
3428 
3429  disableGravity = false;
3431  af_push_moveables = false;
3432 
3433  physicsObj.UseFlyMove( false );
3434  physicsObj.ForceDeltaMove( false );
3435 
3436  // end our looping ambient sound
3437  StopSound( SND_CHANNEL_AMBIENT, false );
3438 
3439  if ( attacker && attacker->IsType( idActor::Type ) ) {
3440  gameLocal.AlertAI( ( idActor * )attacker );
3441  }
3442 
3443  // activate targets
3444  ActivateTargets( attacker );
3445 
3447  RemoveProjectile();
3449 
3450  ClearEnemy();
3451  AI_DEAD = true;
3452 
3453  // make monster nonsolid
3454  physicsObj.SetContents( 0 );
3456 
3457  Unbind();
3458 
3459  if ( StartRagdoll() ) {
3460  StartSound( "snd_death", SND_CHANNEL_VOICE, 0, false, NULL );
3461  }
3462 
3463  if ( spawnArgs.GetString( "model_death", "", &modelDeath ) ) {
3464  // lost soul is only case that does not use a ragdoll and has a model_death so get the death sound in here
3465  StartSound( "snd_death", SND_CHANNEL_VOICE, 0, false, NULL );
3467  SetModel( modelDeath );
3471 #ifdef _D3XP
3472  // No grabbing if "model_death"
3473  noGrab = true;
3474 #endif
3475  }
3476 
3477  restartParticles = false;
3478 
3479  state = GetScriptFunction( "state_Killed" );
3480  SetState( state );
3481  SetWaitState( "" );
3482 
3483  const idKeyValue *kv = spawnArgs.MatchPrefix( "def_drops", NULL );
3484  while( kv ) {
3485  idDict args;
3486 
3487  args.Set( "classname", kv->GetValue() );
3488  args.Set( "origin", physicsObj.GetOrigin().ToString() );
3489  gameLocal.SpawnEntityDef( args );
3490  kv = spawnArgs.MatchPrefix( "def_drops", kv );
3491  }
3492 
3493 #ifndef _D3XP
3494  if ( ( attacker && attacker->IsType( idPlayer::Type ) ) && ( inflictor && !inflictor->IsType( idSoulCubeMissile::Type ) ) ) {
3495  static_cast< idPlayer* >( attacker )->AddAIKill();
3496  }
3497 #endif
3498 
3499 #ifdef _D3XP
3500  if(spawnArgs.GetBool("harvest_on_death")) {
3501  const idDict *harvestDef = gameLocal.FindEntityDefDict( spawnArgs.GetString("def_harvest_type"), false );
3502  if ( harvestDef ) {
3503  idEntity *temp;
3504  gameLocal.SpawnEntityDef( *harvestDef, &temp, false );
3505  harvestEnt = static_cast<idHarvestable *>(temp);
3506 
3507  }
3508 
3509  if(harvestEnt.GetEntity()) {
3510  //Let the harvest entity set itself up
3511  harvestEnt.GetEntity()->Init(this);
3512  harvestEnt.GetEntity()->BecomeActive( TH_THINK );
3513  }
3514  }
3515 #endif
3516 }
3517 
3518 /***********************************************************************
3519 
3520  Targeting/Combat
3521 
3522 ***********************************************************************/
3523 
3524 /*
3525 =====================
3526 idAI::PlayCinematic
3527 =====================
3528 */
3529 void idAI::PlayCinematic( void ) {
3530  const char *animname;
3531 
3532  if ( current_cinematic >= num_cinematics ) {
3533  if ( g_debugCinematic.GetBool() ) {
3534  gameLocal.Printf( "%d: '%s' stop\n", gameLocal.framenum, GetName() );
3535  }
3536  if ( !spawnArgs.GetBool( "cinematic_no_hide" ) ) {
3537  Hide();
3538  }
3539  current_cinematic = 0;
3541  fl.neverDormant = false;
3542  return;
3543  }
3544 
3545  Show();
3547 
3548  allowJointMod = false;
3549  allowEyeFocus = false;
3550 
3551  spawnArgs.GetString( va( "anim%d", current_cinematic ), NULL, &animname );
3552  if ( !animname ) {
3553  gameLocal.Warning( "missing 'anim%d' key on %s", current_cinematic, name.c_str() );
3554  return;
3555  }
3556 
3557  if ( g_debugCinematic.GetBool() ) {
3558  gameLocal.Printf( "%d: '%s' start '%s'\n", gameLocal.framenum, GetName(), animname );
3559  }
3560 
3563  headAnim.BecomeIdle();
3564 
3567  legsAnim.BecomeIdle();
3568 
3572 
3573  // make sure our model gets updated
3575 
3576  // update the anim bounds
3577  UpdateAnimation();
3578  UpdateVisuals();
3579  Present();
3580 
3581  if ( head.GetEntity() ) {
3582  // since the body anim was updated, we need to run physics to update the position of the head
3583  RunPhysics();
3584 
3585  // make sure our model gets updated
3587 
3588  // update the anim bounds
3591  head.GetEntity()->Present();
3592  }
3593 
3594  fl.neverDormant = true;
3595 }
3596 
3597 /*
3598 =====================
3599 idAI::Activate
3600 
3601 Notifies the script that a monster has been activated by a trigger or flashlight
3602 =====================
3603 */
3604 void idAI::Activate( idEntity *activator ) {
3605  idPlayer *player;
3606 
3607  if ( AI_DEAD ) {
3608  // ignore it when they're dead
3609  return;
3610  }
3611 
3612  // make sure he's not dormant
3613  dormantStart = 0;
3614 
3615  if ( num_cinematics ) {
3616  PlayCinematic();
3617  } else {
3618  AI_ACTIVATED = true;
3619  if ( !activator || !activator->IsType( idPlayer::Type ) ) {
3620  player = gameLocal.GetLocalPlayer();
3621  } else {
3622  player = static_cast<idPlayer *>( activator );
3623  }
3624 
3625  if ( ReactionTo( player ) & ATTACK_ON_ACTIVATE ) {
3626  SetEnemy( player );
3627  }
3628 
3629  // update the script in cinematics so that entities don't start anims or show themselves a frame late.
3630  if ( cinematic ) {
3631  UpdateAIScript();
3632 
3633  // make sure our model gets updated
3635 
3636  // update the anim bounds
3637  UpdateAnimation();
3638  UpdateVisuals();
3639  Present();
3640 
3641  if ( head.GetEntity() ) {
3642  // since the body anim was updated, we need to run physics to update the position of the head
3643  RunPhysics();
3644 
3645  // make sure our model gets updated
3647 
3648  // update the anim bounds
3651  head.GetEntity()->Present();
3652  }
3653  }
3654  }
3655 }
3656 
3657 /*
3658 =====================
3659 idAI::EnemyDead
3660 =====================
3661 */
3662 void idAI::EnemyDead( void ) {
3663  ClearEnemy();
3664  AI_ENEMY_DEAD = true;
3665 }
3666 
3667 /*
3668 =====================
3669 idAI::TalkTo
3670 =====================
3671 */
3672 void idAI::TalkTo( idActor *actor ) {
3673  if ( talk_state != TALK_OK ) {
3674  return;
3675  }
3676 
3677 #ifdef _D3XP
3678  // Wake up monsters that are pretending to be NPC's
3679  if ( team == 1 && actor->team != team ) {
3680  ProcessEvent( &EV_Activate, actor );
3681  }
3682 #endif
3683 
3684  talkTarget = actor;
3685  if ( actor ) {
3686  AI_TALK = true;
3687  } else {
3688  AI_TALK = false;
3689  }
3690 }
3691 
3692 /*
3693 =====================
3694 idAI::GetEnemy
3695 =====================
3696 */
3697 idActor *idAI::GetEnemy( void ) const {
3698  return enemy.GetEntity();
3699 }
3700 
3701 /*
3702 =====================
3703 idAI::GetTalkState
3704 =====================
3705 */
3707  if ( ( talk_state != TALK_NEVER ) && AI_DEAD ) {
3708  return TALK_DEAD;
3709  }
3710  if ( IsHidden() ) {
3711  return TALK_NEVER;
3712  }
3713  return talk_state;
3714 }
3715 
3716 /*
3717 =====================
3718 idAI::TouchedByFlashlight
3719 =====================
3720 */
3721 void idAI::TouchedByFlashlight( idActor *flashlight_owner ) {
3722  if ( wakeOnFlashlight ) {
3723  Activate( flashlight_owner );
3724  }
3725 }
3726 
3727 /*
3728 =====================
3729 idAI::ClearEnemy
3730 =====================
3731 */
3732 void idAI::ClearEnemy( void ) {
3733  if ( move.moveCommand == MOVE_TO_ENEMY ) {
3735  }
3736 
3737  enemyNode.Remove();
3738  enemy = NULL;
3739  AI_ENEMY_IN_FOV = false;
3740  AI_ENEMY_VISIBLE = false;
3741  AI_ENEMY_DEAD = true;
3742 
3743  SetChatSound();
3744 }
3745 
3746 /*
3747 =====================
3748 idAI::EnemyPositionValid
3749 =====================
3750 */
3751 bool idAI::EnemyPositionValid( void ) const {
3752  trace_t tr;
3753  idVec3 muzzle;
3754  idMat3 axis;
3755 
3756  if ( !enemy.GetEntity() ) {
3757  return false;
3758  }
3759 
3760  if ( AI_ENEMY_VISIBLE ) {
3761  return true;
3762  }
3763 
3765  if ( tr.fraction < 1.0f ) {
3766  // can't see the area yet, so don't know if he's there or not
3767  return true;
3768  }
3769 
3770  return false;
3771 }
3772 
3773 /*
3774 =====================
3775 idAI::SetEnemyPosition
3776 =====================
3777 */
3779  idActor *enemyEnt = enemy.GetEntity();
3780  int enemyAreaNum;
3781  int areaNum;
3782  int lastVisibleReachableEnemyAreaNum;
3783  aasPath_t path;
3784  idVec3 pos;
3785  bool onGround;
3786 
3787  if ( !enemyEnt ) {
3788  return;
3789  }
3790 
3792  lastVisibleEnemyEyeOffset = enemyEnt->EyeOffset();
3793  lastVisibleEnemyPos = enemyEnt->GetPhysics()->GetOrigin();
3794  if ( move.moveType == MOVETYPE_FLY ) {
3795  pos = lastVisibleEnemyPos;
3796  onGround = true;
3797  } else {
3798  onGround = enemyEnt->GetFloorPos( 64.0f, pos );
3799  if ( enemyEnt->OnLadder() ) {
3800  onGround = false;
3801  }
3802  }
3803 
3804  if ( !onGround ) {
3805  if ( move.moveCommand == MOVE_TO_ENEMY ) {
3806  AI_DEST_UNREACHABLE = true;
3807  }
3808  return;
3809  }
3810 
3811  // when we don't have an AAS, we can't tell if an enemy is reachable or not,
3812  // so just assume that he is.
3813  if ( !aas ) {
3815  if ( move.moveCommand == MOVE_TO_ENEMY ) {
3816  AI_DEST_UNREACHABLE = false;
3817  }
3818  enemyAreaNum = 0;
3819  areaNum = 0;
3820  } else {
3821  lastVisibleReachableEnemyAreaNum = move.toAreaNum;
3822  enemyAreaNum = PointReachableAreaNum( lastVisibleEnemyPos, 1.0f );
3823  if ( !enemyAreaNum ) {
3824  enemyAreaNum = PointReachableAreaNum( lastReachableEnemyPos, 1.0f );
3825  pos = lastReachableEnemyPos;
3826  }
3827  if ( !enemyAreaNum ) {
3828  if ( move.moveCommand == MOVE_TO_ENEMY ) {
3829  AI_DEST_UNREACHABLE = true;
3830  }
3831  areaNum = 0;
3832  } else {
3833  const idVec3 &org = physicsObj.GetOrigin();
3834  areaNum = PointReachableAreaNum( org );
3835  if ( PathToGoal( path, areaNum, org, enemyAreaNum, pos ) ) {
3837  lastVisibleReachableEnemyAreaNum = enemyAreaNum;
3838  if ( move.moveCommand == MOVE_TO_ENEMY ) {
3839  AI_DEST_UNREACHABLE = false;
3840  }
3841  } else if ( move.moveCommand == MOVE_TO_ENEMY ) {
3842  AI_DEST_UNREACHABLE = true;
3843  }
3844  }
3845  }
3846 
3847  if ( move.moveCommand == MOVE_TO_ENEMY ) {
3848  if ( !aas ) {
3849  // keep the move destination up to date for wandering
3851  } else if ( enemyAreaNum ) {
3852  move.toAreaNum = lastVisibleReachableEnemyAreaNum;
3854  }
3855 
3856  if ( move.moveType == MOVETYPE_FLY ) {
3858  idVec3 end = move.moveDest;
3859  end.z += enemyEnt->EyeOffset().z + fly_offset;
3860  idAI::PredictPath( this, aas, move.moveDest, end - move.moveDest, 1000, 1000, SE_BLOCKED, path );
3861  move.moveDest = path.endPos;
3863  }
3864  }
3865 }
3866 
3867 /*
3868 =====================
3869 idAI::UpdateEnemyPosition
3870 =====================
3871 */
3873  idActor *enemyEnt = enemy.GetEntity();
3874  int enemyAreaNum;
3875  int areaNum;
3876  aasPath_t path;
3877  predictedPath_t predictedPath;
3878  idVec3 enemyPos;
3879  bool onGround;
3880 
3881  if ( !enemyEnt ) {
3882  return;
3883  }
3884 
3885  const idVec3 &org = physicsObj.GetOrigin();
3886 
3887  if ( move.moveType == MOVETYPE_FLY ) {
3888  enemyPos = enemyEnt->GetPhysics()->GetOrigin();
3889  onGround = true;
3890  } else {
3891  onGround = enemyEnt->GetFloorPos( 64.0f, enemyPos );
3892  if ( enemyEnt->OnLadder() ) {
3893  onGround = false;
3894  }
3895  }
3896 
3897  if ( onGround ) {
3898  // when we don't have an AAS, we can't tell if an enemy is reachable or not,
3899  // so just assume that he is.
3900  if ( !aas ) {
3901  enemyAreaNum = 0;
3902  lastReachableEnemyPos = enemyPos;
3903  } else {
3904  enemyAreaNum = PointReachableAreaNum( enemyPos, 1.0f );
3905  if ( enemyAreaNum ) {
3906  areaNum = PointReachableAreaNum( org );
3907  if ( PathToGoal( path, areaNum, org, enemyAreaNum, enemyPos ) ) {
3908  lastReachableEnemyPos = enemyPos;
3909  }
3910  }
3911  }
3912  }
3913 
3914  AI_ENEMY_IN_FOV = false;
3915  AI_ENEMY_VISIBLE = false;
3916 
3917  if ( CanSee( enemyEnt, false ) ) {
3918  AI_ENEMY_VISIBLE = true;
3919  if ( CheckFOV( enemyEnt->GetPhysics()->GetOrigin() ) ) {
3920  AI_ENEMY_IN_FOV = true;
3921  }
3922 
3923  SetEnemyPosition();
3924  } else {
3925  // check if we heard any sounds in the last frame
3926  if ( enemyEnt == gameLocal.GetAlertEntity() ) {
3927  float dist = ( enemyEnt->GetPhysics()->GetOrigin() - org ).LengthSqr();
3928  if ( dist < Square( AI_HEARING_RANGE ) ) {
3929  SetEnemyPosition();
3930  }
3931  }
3932  }
3933 
3934  if ( ai_debugMove.GetBool() ) {
3937  }
3938 }
3939 
3940 /*
3941 =====================
3942 idAI::SetEnemy
3943 =====================
3944 */
3945 void idAI::SetEnemy( idActor *newEnemy ) {
3946  int enemyAreaNum;
3947 
3948  if ( AI_DEAD ) {
3949  ClearEnemy();
3950  return;
3951  }
3952 
3953  AI_ENEMY_DEAD = false;
3954  if ( !newEnemy ) {
3955  ClearEnemy();
3956  } else if ( enemy.GetEntity() != newEnemy ) {
3957  enemy = newEnemy;
3958  enemyNode.AddToEnd( newEnemy->enemyList );
3959  if ( newEnemy->health <= 0 ) {
3960  EnemyDead();
3961  return;
3962  }
3963  // let the monster know where the enemy is
3964  newEnemy->GetAASLocation( aas, lastReachableEnemyPos, enemyAreaNum );
3965  SetEnemyPosition();
3966  SetChatSound();
3967 
3970  enemyAreaNum = PointReachableAreaNum( lastReachableEnemyPos, 1.0f );
3971  if ( aas && enemyAreaNum ) {
3974  }
3975  }
3976 }
3977 
3978 /*
3979 ============
3980 idAI::FirstVisiblePointOnPath
3981 ============
3982 */
3983 idVec3 idAI::FirstVisiblePointOnPath( const idVec3 origin, const idVec3 &target, int travelFlags ) const {
3984  int i, areaNum, targetAreaNum, curAreaNum, travelTime;
3985  idVec3 curOrigin;
3986  idReachability *reach;
3987 
3988  if ( !aas ) {
3989  return origin;
3990  }
3991 
3992  areaNum = PointReachableAreaNum( origin );
3993  targetAreaNum = PointReachableAreaNum( target );
3994 
3995  if ( !areaNum || !targetAreaNum ) {
3996  return origin;
3997  }
3998 
3999  if ( ( areaNum == targetAreaNum ) || PointVisible( origin ) ) {
4000  return origin;
4001  }
4002 
4003  curAreaNum = areaNum;
4004  curOrigin = origin;
4005 
4006  for( i = 0; i < 10; i++ ) {
4007 
4008  if ( !aas->RouteToGoalArea( curAreaNum, curOrigin, targetAreaNum, travelFlags, travelTime, &reach ) ) {
4009  break;
4010  }
4011 
4012  if ( !reach ) {
4013  return target;
4014  }
4015 
4016  curAreaNum = reach->toAreaNum;
4017  curOrigin = reach->end;
4018 
4019  if ( PointVisible( curOrigin ) ) {
4020  return curOrigin;
4021  }
4022  }
4023 
4024  return origin;
4025 }
4026 
4027 /*
4028 ===================
4029 idAI::CalculateAttackOffsets
4030 
4031 calculate joint positions on attack frames so we can do proper "can hit" tests
4032 ===================
4033 */
4035  const idDeclModelDef *modelDef;
4036  int num;
4037  int i;
4038  int frame;
4039  const frameCommand_t *command;
4040  idMat3 axis;
4041  const idAnim *anim;
4042  jointHandle_t joint;
4043 
4044  modelDef = animator.ModelDef();
4045  if ( !modelDef ) {
4046  return;
4047  }
4048  num = modelDef->NumAnims();
4049 
4050  // needs to be off while getting the offsets so that we account for the distance the monster moves in the attack anim
4051  animator.RemoveOriginOffset( false );
4052 
4053  // anim number 0 is reserved for non-existant anims. to avoid off by one issues, just allocate an extra spot for
4054  // launch offsets so that anim number can be used without subtracting 1.
4056  missileLaunchOffset.SetNum( num + 1 );
4057  missileLaunchOffset[ 0 ].Zero();
4058 
4059  for( i = 1; i <= num; i++ ) {
4060  missileLaunchOffset[ i ].Zero();
4061  anim = modelDef->GetAnim( i );
4062  if ( anim ) {
4063  frame = anim->FindFrameForFrameCommand( FC_LAUNCHMISSILE, &command );
4064  if ( frame >= 0 ) {
4065  joint = animator.GetJointHandle( command->string->c_str() );
4066  if ( joint == INVALID_JOINT ) {
4067  gameLocal.Error( "Invalid joint '%s' on 'launch_missile' frame command on frame %d of model '%s'", command->string->c_str(), frame, modelDef->GetName() );
4068  }
4069  GetJointTransformForAnim( joint, i, FRAME2MS( frame ), missileLaunchOffset[ i ], axis );
4070  }
4071  }
4072  }
4073 
4074  animator.RemoveOriginOffset( true );
4075 }
4076 
4077 /*
4078 =====================
4079 idAI::CreateProjectileClipModel
4080 =====================
4081 */
4083  if ( projectileClipModel == NULL ) {
4084  idBounds projectileBounds( vec3_origin );
4085  projectileBounds.ExpandSelf( projectileRadius );
4086  projectileClipModel = new idClipModel( idTraceModel( projectileBounds ) );
4087  }
4088 }
4089 
4090 /*
4091 =====================
4092 idAI::GetAimDir
4093 =====================
4094 */
4095 bool idAI::GetAimDir( const idVec3 &firePos, idEntity *aimAtEnt, const idEntity *ignore, idVec3 &aimDir ) const {
4096  idVec3 targetPos1;
4097  idVec3 targetPos2;
4098  idVec3 delta;
4099  float max_height;
4100  bool result;
4101 
4102  // if no aimAtEnt or projectile set
4103  if ( !aimAtEnt || !projectileDef ) {
4104  aimDir = viewAxis[ 0 ] * physicsObj.GetGravityAxis();
4105  return false;
4106  }
4107 
4108  if ( projectileClipModel == NULL ) {
4110  }
4111 
4112  if ( aimAtEnt == enemy.GetEntity() ) {
4113  static_cast<idActor *>( aimAtEnt )->GetAIAimTargets( lastVisibleEnemyPos, targetPos1, targetPos2 );
4114  } else if ( aimAtEnt->IsType( idActor::Type ) ) {
4115  static_cast<idActor *>( aimAtEnt )->GetAIAimTargets( aimAtEnt->GetPhysics()->GetOrigin(), targetPos1, targetPos2 );
4116  } else {
4117  targetPos1 = aimAtEnt->GetPhysics()->GetAbsBounds().GetCenter();
4118  targetPos2 = targetPos1;
4119  }
4120 
4121 #ifdef _D3XP
4122  if ( this->team == 0 && !idStr::Cmp( aimAtEnt->GetEntityDefName(), "monster_demon_vulgar" ) ) {
4123  targetPos1.z -= 28.f;
4124  targetPos2.z -= 12.f;
4125  }
4126 #endif
4127 
4128  // try aiming for chest
4129  delta = firePos - targetPos1;
4130  max_height = delta.LengthFast() * projectile_height_to_distance_ratio;
4131  result = PredictTrajectory( firePos, targetPos1, projectileSpeed, projectileGravity, projectileClipModel, MASK_SHOT_RENDERMODEL, max_height, ignore, aimAtEnt, ai_debugTrajectory.GetBool() ? 1000 : 0, aimDir );
4132  if ( result || !aimAtEnt->IsType( idActor::Type ) ) {
4133  return result;
4134  }
4135 
4136  // try aiming for head
4137  delta = firePos - targetPos2;
4138  max_height = delta.LengthFast() * projectile_height_to_distance_ratio;
4139  result = PredictTrajectory( firePos, targetPos2, projectileSpeed, projectileGravity, projectileClipModel, MASK_SHOT_RENDERMODEL, max_height, ignore, aimAtEnt, ai_debugTrajectory.GetBool() ? 1000 : 0, aimDir );
4140 
4141  return result;
4142 }
4143 
4144 /*
4145 =====================
4146 idAI::BeginAttack
4147 =====================
4148 */
4149 void idAI::BeginAttack( const char *name ) {
4150  attack = name;
4152 }
4153 
4154 /*
4155 =====================
4156 idAI::EndAttack
4157 =====================
4158 */
4159 void idAI::EndAttack( void ) {
4160  attack = "";
4161 }
4162 
4163 /*
4164 =====================
4165 idAI::CreateProjectile
4166 =====================
4167 */
4168 idProjectile *idAI::CreateProjectile( const idVec3 &pos, const idVec3 &dir ) {
4169  idEntity *ent;
4170  const char *clsname;
4171 
4172  if ( !projectile.GetEntity() ) {
4173  gameLocal.SpawnEntityDef( *projectileDef, &ent, false );
4174  if ( !ent ) {
4175  clsname = projectileDef->GetString( "classname" );
4176  gameLocal.Error( "Could not spawn entityDef '%s'", clsname );
4177  }
4178 
4179  if ( !ent->IsType( idProjectile::Type ) ) {
4180  clsname = ent->GetClassname();
4181  gameLocal.Error( "'%s' is not an idProjectile", clsname );
4182  }
4183  projectile = ( idProjectile * )ent;
4184  }
4185 
4186  projectile.GetEntity()->Create( this, pos, dir );
4187 
4188  return projectile.GetEntity();
4189 }
4190 
4191 /*
4192 =====================
4193 idAI::RemoveProjectile
4194 =====================
4195 */
4197  if ( projectile.GetEntity() ) {
4199  projectile = NULL;
4200  }
4201 }
4202 
4203 /*
4204 =====================
4205 idAI::LaunchProjectile
4206 =====================
4207 */
4208 idProjectile *idAI::LaunchProjectile( const char *jointname, idEntity *target, bool clampToAttackCone ) {
4209  idVec3 muzzle;
4210  idVec3 dir;
4211  idVec3 start;
4212  trace_t tr;
4213  idBounds projBounds;
4214  float distance;
4215  const idClipModel *projClip;
4216  float attack_accuracy;
4217  float attack_cone;
4218  float projectile_spread;
4219  float diff;
4220  float angle;
4221  float spin;
4222  idAngles ang;
4223  int num_projectiles;
4224  int i;
4225  idMat3 axis;
4226 #ifdef _D3XP
4227  idMat3 proj_axis;
4228  bool forceMuzzle;
4229 #endif
4230  idVec3 tmp;
4231  idProjectile *lastProjectile;
4232 
4233  if ( !projectileDef ) {
4234  gameLocal.Warning( "%s (%s) doesn't have a projectile specified", name.c_str(), GetEntityDefName() );
4235  return NULL;
4236  }
4237 
4238  attack_accuracy = spawnArgs.GetFloat( "attack_accuracy", "7" );
4239  attack_cone = spawnArgs.GetFloat( "attack_cone", "70" );
4240  projectile_spread = spawnArgs.GetFloat( "projectile_spread", "0" );
4241  num_projectiles = spawnArgs.GetInt( "num_projectiles", "1" );
4242 #ifdef _D3XP
4243  forceMuzzle = spawnArgs.GetBool( "forceMuzzle", "0" );
4244 #endif
4245 
4246  GetMuzzle( jointname, muzzle, axis );
4247 
4248  if ( !projectile.GetEntity() ) {
4249  CreateProjectile( muzzle, axis[ 0 ] );
4250  }
4251 
4252  lastProjectile = projectile.GetEntity();
4253 
4254  if ( target != NULL ) {
4255  tmp = target->GetPhysics()->GetAbsBounds().GetCenter() - muzzle;
4256  tmp.Normalize();
4257  axis = tmp.ToMat3();
4258  } else {
4259  axis = viewAxis;
4260  }
4261 
4262  // rotate it because the cone points up by default
4263  tmp = axis[2];
4264  axis[2] = axis[0];
4265  axis[0] = -tmp;
4266 
4267 #ifdef _D3XP
4268  proj_axis = axis;
4269 #endif
4270 
4271  if ( !forceMuzzle ) { // _D3XP
4272  // make sure the projectile starts inside the monster bounding box
4273  const idBounds &ownerBounds = physicsObj.GetAbsBounds();
4274  projClip = lastProjectile->GetPhysics()->GetClipModel();
4275  projBounds = projClip->GetBounds().Rotate( axis );
4276 
4277  // check if the owner bounds is bigger than the projectile bounds
4278  if ( ( ( ownerBounds[1][0] - ownerBounds[0][0] ) > ( projBounds[1][0] - projBounds[0][0] ) ) &&
4279  ( ( ownerBounds[1][1] - ownerBounds[0][1] ) > ( projBounds[1][1] - projBounds[0][1] ) ) &&
4280  ( ( ownerBounds[1][2] - ownerBounds[0][2] ) > ( projBounds[1][2] - projBounds[0][2] ) ) ) {
4281  if ( (ownerBounds - projBounds).RayIntersection( muzzle, viewAxis[ 0 ], distance ) ) {
4282  start = muzzle + distance * viewAxis[ 0 ];
4283  } else {
4284  start = ownerBounds.GetCenter();
4285  }
4286  } else {
4287  // projectile bounds bigger than the owner bounds, so just start it from the center
4288  start = ownerBounds.GetCenter();
4289  }
4290 
4291  gameLocal.clip.Translation( tr, start, muzzle, projClip, axis, MASK_SHOT_RENDERMODEL, this );
4292  muzzle = tr.endpos;
4293  }
4294 
4295  // set aiming direction
4296  GetAimDir( muzzle, target, this, dir );
4297  ang = dir.ToAngles();
4298 
4299  // adjust his aim so it's not perfect. uses sine based movement so the tracers appear less random in their spread.
4300  float t = MS2SEC( gameLocal.time + entityNumber * 497 );
4301  ang.pitch += idMath::Sin16( t * 5.1 ) * attack_accuracy;
4302  ang.yaw += idMath::Sin16( t * 6.7 ) * attack_accuracy;
4303 
4304  if ( clampToAttackCone ) {
4305  // clamp the attack direction to be within monster's attack cone so he doesn't do
4306  // things like throw the missile backwards if you're behind him
4307  diff = idMath::AngleDelta( ang.yaw, current_yaw );
4308  if ( diff > attack_cone ) {
4309  ang.yaw = current_yaw + attack_cone;
4310  } else if ( diff < -attack_cone ) {
4311  ang.yaw = current_yaw - attack_cone;
4312  }
4313  }
4314 
4315  axis = ang.ToMat3();
4316 
4317  float spreadRad = DEG2RAD( projectile_spread );
4318  for( i = 0; i < num_projectiles; i++ ) {
4319  // spread the projectiles out
4320  angle = idMath::Sin( spreadRad * gameLocal.random.RandomFloat() );
4321  spin = (float)DEG2RAD( 360.0f ) * gameLocal.random.RandomFloat();
4322  dir = axis[ 0 ] + axis[ 2 ] * ( angle * idMath::Sin( spin ) ) - axis[ 1 ] * ( angle * idMath::Cos( spin ) );
4323  dir.Normalize();
4324 
4325  // launch the projectile
4326  if ( !projectile.GetEntity() ) {
4327  CreateProjectile( muzzle, dir );
4328  }
4329  lastProjectile = projectile.GetEntity();
4330  lastProjectile->Launch( muzzle, dir, vec3_origin );
4331  projectile = NULL;
4332  }
4333 
4334  TriggerWeaponEffects( muzzle );
4335 
4337 
4338  return lastProjectile;
4339 }
4340 
4341 /*
4342 ================
4343 idAI::DamageFeedback
4344 
4345 callback function for when another entity received damage from this entity. damage can be adjusted and returned to the caller.
4346 
4347 FIXME: This gets called when we call idPlayer::CalcDamagePoints from idAI::AttackMelee, which then checks for a saving throw,
4348 possibly forcing a miss. This is harmless behavior ATM, but is not intuitive.
4349 ================
4350 */
4351 void idAI::DamageFeedback( idEntity *victim, idEntity *inflictor, int &damage ) {
4352  if ( ( victim == this ) && inflictor->IsType( idProjectile::Type ) ) {
4353  // monsters only get half damage from their own projectiles
4354  damage = ( damage + 1 ) / 2; // round up so we don't do 0 damage
4355 
4356  } else if ( victim == enemy.GetEntity() ) {
4357  AI_HIT_ENEMY = true;
4358  }
4359 }
4360 
4361 /*
4362 =====================
4363 idAI::DirectDamage
4364 
4365 Causes direct damage to an entity
4366 
4367 kickDir is specified in the monster's coordinate system, and gives the direction
4368 that the view kick and knockback should go
4369 =====================
4370 */
4371 void idAI::DirectDamage( const char *meleeDefName, idEntity *ent ) {
4372  const idDict *meleeDef;
4373  const char *p;
4374  const idSoundShader *shader;
4375 
4376  meleeDef = gameLocal.FindEntityDefDict( meleeDefName, false );
4377  if ( !meleeDef ) {
4378  gameLocal.Error( "Unknown damage def '%s' on '%s'", meleeDefName, name.c_str() );
4379  }
4380 
4381  if ( !ent->fl.takedamage ) {
4382  const idSoundShader *shader = declManager->FindSound(meleeDef->GetString( "snd_miss" ));
4383  StartSoundShader( shader, SND_CHANNEL_DAMAGE, 0, false, NULL );
4384  return;
4385  }
4386 
4387  //
4388  // do the damage
4389  //
4390  p = meleeDef->GetString( "snd_hit" );
4391  if ( p && *p ) {
4392  shader = declManager->FindSound( p );
4393  StartSoundShader( shader, SND_CHANNEL_DAMAGE, 0, false, NULL );
4394  }
4395 
4396  idVec3 kickDir;
4397  meleeDef->GetVector( "kickDir", "0 0 0", kickDir );
4398 
4399  idVec3 globalKickDir;
4400  globalKickDir = ( viewAxis * physicsObj.GetGravityAxis() ) * kickDir;
4401 
4402  ent->Damage( this, this, globalKickDir, meleeDefName, 1.0f, INVALID_JOINT );
4403 
4404  // end the attack if we're a multiframe attack
4405  EndAttack();
4406 }
4407 
4408 /*
4409 =====================
4410 idAI::TestMelee
4411 =====================
4412 */
4413 bool idAI::TestMelee( void ) const {
4414  trace_t trace;
4415  idActor *enemyEnt = enemy.GetEntity();
4416 
4417  if ( !enemyEnt || !melee_range ) {
4418  return false;
4419  }
4420 
4421  //FIXME: make work with gravity vector
4422  idVec3 org = physicsObj.GetOrigin();
4423  const idBounds &myBounds = physicsObj.GetBounds();
4424  idBounds bounds;
4425 
4426  // expand the bounds out by our melee range
4427  bounds[0][0] = -melee_range;
4428  bounds[0][1] = -melee_range;
4429  bounds[0][2] = myBounds[0][2] - 4.0f;
4430  bounds[1][0] = melee_range;
4431  bounds[1][1] = melee_range;
4432  bounds[1][2] = myBounds[1][2] + 4.0f;
4433  bounds.TranslateSelf( org );
4434 
4435  idVec3 enemyOrg = enemyEnt->GetPhysics()->GetOrigin();
4436  idBounds enemyBounds = enemyEnt->GetPhysics()->GetBounds();
4437  enemyBounds.TranslateSelf( enemyOrg );
4438 
4439  if ( ai_debugMove.GetBool() ) {
4441  }
4442 
4443  if ( !bounds.IntersectsBounds( enemyBounds ) ) {
4444  return false;
4445  }
4446 
4448  idVec3 end = enemyEnt->GetEyePosition();
4449 
4450  gameLocal.clip.TracePoint( trace, start, end, MASK_SHOT_BOUNDINGBOX, this );
4451  if ( ( trace.fraction == 1.0f ) || ( gameLocal.GetTraceEntity( trace ) == enemyEnt ) ) {
4452  return true;
4453  }
4454 
4455  return false;
4456 }
4457 
4458 /*
4459 =====================
4460 idAI::AttackMelee
4461 
4462 jointname allows the endpoint to be exactly specified in the model,
4463 as for the commando tentacle. If not specified, it will be set to
4464 the facing direction + melee_range.
4465 
4466 kickDir is specified in the monster's coordinate system, and gives the direction
4467 that the view kick and knockback should go
4468 =====================
4469 */
4470 bool idAI::AttackMelee( const char *meleeDefName ) {
4471  const idDict *meleeDef;
4472  idActor *enemyEnt = enemy.GetEntity();
4473  const char *p;
4474  const idSoundShader *shader;
4475 
4476  meleeDef = gameLocal.FindEntityDefDict( meleeDefName, false );
4477  if ( !meleeDef ) {
4478  gameLocal.Error( "Unknown melee '%s'", meleeDefName );
4479  }
4480 
4481  if ( !enemyEnt ) {
4482  p = meleeDef->GetString( "snd_miss" );
4483  if ( p && *p ) {
4484  shader = declManager->FindSound( p );
4485  StartSoundShader( shader, SND_CHANNEL_DAMAGE, 0, false, NULL );
4486  }
4487  return false;
4488  }
4489 
4490  // check for the "saving throw" automatic melee miss on lethal blow
4491  // stupid place for this.
4492  bool forceMiss = false;
4493  if ( enemyEnt->IsType( idPlayer::Type ) && g_skill.GetInteger() < 2 ) {
4494  int damage, armor;
4495  idPlayer *player = static_cast<idPlayer*>( enemyEnt );
4496  player->CalcDamagePoints( this, this, meleeDef, 1.0f, INVALID_JOINT, &damage, &armor );
4497 
4498  if ( enemyEnt->health <= damage ) {
4499  int t = gameLocal.time - player->lastSavingThrowTime;
4500  if ( t > SAVING_THROW_TIME ) {
4502  t = 0;
4503  }
4504  if ( t < 1000 ) {
4505  gameLocal.Printf( "Saving throw.\n" );
4506  forceMiss = true;
4507  }
4508  }
4509  }
4510 
4511  // make sure the trace can actually hit the enemy
4512  if ( forceMiss || !TestMelee() ) {
4513  // missed
4514  p = meleeDef->GetString( "snd_miss" );
4515  if ( p && *p ) {
4516  shader = declManager->FindSound( p );
4517  StartSoundShader( shader, SND_CHANNEL_DAMAGE, 0, false, NULL );
4518  }
4519  return false;
4520  }
4521 
4522  //
4523  // do the damage
4524  //
4525  p = meleeDef->GetString( "snd_hit" );
4526  if ( p && *p ) {
4527  shader = declManager->FindSound( p );
4528  StartSoundShader( shader, SND_CHANNEL_DAMAGE, 0, false, NULL );
4529  }
4530 
4531  idVec3 kickDir;
4532  meleeDef->GetVector( "kickDir", "0 0 0", kickDir );
4533 
4534  idVec3 globalKickDir;
4535  globalKickDir = ( viewAxis * physicsObj.GetGravityAxis() ) * kickDir;
4536 
4537  enemyEnt->Damage( this, this, globalKickDir, meleeDefName, 1.0f, INVALID_JOINT );
4538 
4540 
4541  return true;
4542 }
4543 
4544 /*
4545 ================
4546 idAI::PushWithAF
4547 ================
4548 */
4549 void idAI::PushWithAF( void ) {
4550  int i, j;
4551  afTouch_t touchList[ MAX_GENTITIES ];
4552  idEntity *pushed_ents[ MAX_GENTITIES ];
4553  idEntity *ent;
4554  idVec3 vel;
4555  int num_pushed;
4556 
4557  num_pushed = 0;
4558  af.ChangePose( this, gameLocal.time );
4559  int num = af.EntitiesTouchingAF( touchList );
4560  for( i = 0; i < num; i++ ) {
4561  if ( touchList[ i ].touchedEnt->IsType( idProjectile::Type ) ) {
4562  // skip projectiles
4563  continue;
4564  }
4565 
4566  // make sure we havent pushed this entity already. this avoids causing double damage
4567  for( j = 0; j < num_pushed; j++ ) {
4568  if ( pushed_ents[ j ] == touchList[ i ].touchedEnt ) {
4569  break;
4570  }
4571  }
4572  if ( j >= num_pushed ) {
4573  ent = touchList[ i ].touchedEnt;
4574  pushed_ents[num_pushed++] = ent;
4575  vel = ent->GetPhysics()->GetAbsBounds().GetCenter() - touchList[ i ].touchedByBody->GetWorldOrigin();
4576  vel.Normalize();
4577  if ( attack.Length() && ent->IsType( idActor::Type ) ) {
4578  ent->Damage( this, this, vel, attack, 1.0f, INVALID_JOINT );
4579  } else {
4580  ent->GetPhysics()->SetLinearVelocity( 100.0f * vel, touchList[ i ].touchedClipModel->GetId() );
4581  }
4582  }
4583  }
4584 }
4585 
4586 /***********************************************************************
4587 
4588  Misc
4589 
4590 ***********************************************************************/
4591 
4592 /*
4593 ================
4594 idAI::GetMuzzle
4595 ================
4596 */
4597 void idAI::GetMuzzle( const char *jointname, idVec3 &muzzle, idMat3 &axis ) {
4598  jointHandle_t joint;
4599 
4600  if ( !jointname || !jointname[ 0 ] ) {
4601  muzzle = physicsObj.GetOrigin() + viewAxis[ 0 ] * physicsObj.GetGravityAxis() * 14;
4602  muzzle -= physicsObj.GetGravityNormal() * physicsObj.GetBounds()[ 1 ].z * 0.5f;
4603  } else {
4604  joint = animator.GetJointHandle( jointname );
4605  if ( joint == INVALID_JOINT ) {
4606  gameLocal.Error( "Unknown joint '%s' on %s", jointname, GetEntityDefName() );
4607  }
4608  GetJointWorldTransform( joint, gameLocal.time, muzzle, axis );
4609  }
4610 }
4611 
4612 /*
4613 ================
4614 idAI::TriggerWeaponEffects
4615 ================
4616 */
4617 void idAI::TriggerWeaponEffects( const idVec3 &muzzle ) {
4618  idVec3 org;
4619  idMat3 axis;
4620 
4621  if ( !g_muzzleFlash.GetBool() ) {
4622  return;
4623  }
4624 
4625  // muzzle flash
4626  // offset the shader parms so muzzle flashes show up
4629 
4630  if ( flashJointWorld != INVALID_JOINT ) {
4632 
4633  if ( worldMuzzleFlash.lightRadius.x > 0.0f ) {
4634  worldMuzzleFlash.axis = axis;
4636  if ( worldMuzzleFlashHandle != - 1 ) {
4638  } else {
4640  }
4642  UpdateVisuals();
4643  }
4644  }
4645 }
4646 
4647 /*
4648 ================
4649 idAI::UpdateMuzzleFlash
4650 ================
4651 */
4653  if ( worldMuzzleFlashHandle != -1 ) {
4654  if ( gameLocal.time >= muzzleFlashEnd ) {
4657  } else {
4658  idVec3 muzzle;
4661  muzzle = physicsObj.GetOrigin() + ( muzzle + modelOffset ) * viewAxis * physicsObj.GetGravityAxis();
4662  worldMuzzleFlash.origin = muzzle;
4664  }
4665  }
4666 }
4667 
4668 /*
4669 ================
4670 idAI::Hide
4671 ================
4672 */
4673 void idAI::Hide( void ) {
4674  idActor::Hide();
4675  fl.takedamage = false;
4676  physicsObj.SetContents( 0 );
4678  StopSound( SND_CHANNEL_AMBIENT, false );
4679  SetChatSound();
4680 
4681  AI_ENEMY_IN_FOV = false;
4682  AI_ENEMY_VISIBLE = false;
4684 }
4685 
4686 /*
4687 ================
4688 idAI::Show
4689 ================
4690 */
4691 void idAI::Show( void ) {
4692  idActor::Show();
4693  if ( spawnArgs.GetBool( "big_monster" ) ) {
4694  physicsObj.SetContents( 0 );
4695  } else if ( use_combat_bbox ) {
4697  } else {
4699  }
4701  fl.takedamage = !spawnArgs.GetBool( "noDamage" );
4702  SetChatSound();
4703  StartSound( "snd_ambient", SND_CHANNEL_AMBIENT, 0, false, NULL );
4704 }
4705 
4706 /*
4707 =====================
4708 idAI::SetChatSound
4709 =====================
4710 */
4711 void idAI::SetChatSound( void ) {
4712  const char *snd;
4713 
4714  if ( IsHidden() ) {
4715  snd = NULL;
4716  } else if ( enemy.GetEntity() ) {
4717  snd = spawnArgs.GetString( "snd_chatter_combat", NULL );
4718  chat_min = SEC2MS( spawnArgs.GetFloat( "chatter_combat_min", "5" ) );
4719  chat_max = SEC2MS( spawnArgs.GetFloat( "chatter_combat_max", "10" ) );
4720  } else if ( !spawnArgs.GetBool( "no_idle_chatter" ) ) {
4721  snd = spawnArgs.GetString( "snd_chatter", NULL );
4722  chat_min = SEC2MS( spawnArgs.GetFloat( "chatter_min", "5" ) );
4723  chat_max = SEC2MS( spawnArgs.GetFloat( "chatter_max", "10" ) );
4724  } else {
4725  snd = NULL;
4726  }
4727 
4728  if ( snd && *snd ) {
4729  chat_snd = declManager->FindSound( snd );
4730 
4731  // set the next chat time
4733  } else {
4734  chat_snd = NULL;
4735  }
4736 }
4737 
4738 /*
4739 ================
4740 idAI::CanPlayChatterSounds
4741 
4742 Used for playing chatter sounds on monsters.
4743 ================
4744 */
4745 bool idAI::CanPlayChatterSounds( void ) const {
4746  if ( AI_DEAD ) {
4747  return false;
4748  }
4749 
4750  if ( IsHidden() ) {
4751  return false;
4752  }
4753 
4754  if ( enemy.GetEntity() ) {
4755  return true;
4756  }
4757 
4758  if ( spawnArgs.GetBool( "no_idle_chatter" ) ) {
4759  return false;
4760  }
4761 
4762  return true;
4763 }
4764 
4765 /*
4766 =====================
4767 idAI::PlayChatter
4768 =====================
4769 */
4770 void idAI::PlayChatter( void ) {
4771  // check if it's time to play a chat sound
4772  if ( AI_DEAD || !chat_snd || ( chat_time > gameLocal.time ) ) {
4773  return;
4774  }
4775 
4777 
4778  // set the next chat time
4780 }
4781 
4782 /*
4783 =====================
4784 idAI::UpdateParticles
4785 =====================
4786 */
4788  if ( ( thinkFlags & TH_UPDATEPARTICLES) && !IsHidden() ) {
4789  idVec3 realVector;
4790  idMat3 realAxis;
4791 
4792  int particlesAlive = 0;
4793  for ( int i = 0; i < particles.Num(); i++ ) {
4794 #ifdef _D3XP
4795  // Smoke particles on AI characters will always be "slow", even when held by grabber
4796  SetTimeState ts(TIME_GROUP1);
4797 #endif
4798  if ( particles[i].particle && particles[i].time ) {
4799  particlesAlive++;
4800  if (af.IsActive()) {
4801  realAxis = mat3_identity;
4802  realVector = GetPhysics()->GetOrigin();
4803  } else {
4804  animator.GetJointTransform( particles[i].joint, gameLocal.time, realVector, realAxis );
4805  realAxis *= renderEntity.axis;
4806  realVector = physicsObj.GetOrigin() + ( realVector + modelOffset ) * ( viewAxis * physicsObj.GetGravityAxis() );
4807  }
4808 
4809  if ( !gameLocal.smokeParticles->EmitSmoke( particles[i].particle, particles[i].time, gameLocal.random.CRandomFloat(), realVector, realAxis, timeGroup /*_D3XP*/ )) {
4810  if ( restartParticles ) {
4811  particles[i].time = gameLocal.time;
4812  } else {
4813  particles[i].time = 0;
4814  particlesAlive--;
4815  }
4816  }
4817  }
4818  }
4819  if ( particlesAlive == 0 ) {
4820  BecomeInactive( TH_UPDATEPARTICLES );
4821  }
4822  }
4823 }
4824 
4825 /*
4826 =====================
4827 idAI::TriggerParticles
4828 =====================
4829 */
4830 void idAI::TriggerParticles( const char *jointName ) {
4831  jointHandle_t jointNum;
4832 
4833  jointNum = animator.GetJointHandle( jointName );
4834  for ( int i = 0; i < particles.Num(); i++ ) {
4835  if ( particles[i].joint == jointNum ) {
4836  particles[i].time = gameLocal.time;
4838  }
4839  }
4840 }
4841 
4842 #ifdef _D3XP
4843 void idAI::TriggerFX( const char* joint, const char* fx ) {
4844 
4845  if( !strcmp(joint, "origin") ) {
4846  idEntityFx::StartFx( fx, NULL, NULL, this, true );
4847  } else {
4848  idVec3 joint_origin;
4849  idMat3 joint_axis;
4850  jointHandle_t jointNum;
4851  jointNum = animator.GetJointHandle( joint );
4852 
4853  if ( jointNum == INVALID_JOINT ) {
4854  gameLocal.Warning( "Unknown fx joint '%s' on entity %s", joint, name.c_str() );
4855  return;
4856  }
4857 
4858  GetJointWorldTransform( jointNum, gameLocal.time, joint_origin, joint_axis );
4859  idEntityFx::StartFx( fx, &joint_origin, &joint_axis, this, true );
4860  }
4861 }
4862 
4863 idEntity* idAI::StartEmitter( const char* name, const char* joint, const char* particle ) {
4864 
4865  idEntity* existing = GetEmitter(name);
4866  if(existing) {
4867  return existing;
4868  }
4869 
4870  jointHandle_t jointNum;
4871  jointNum = animator.GetJointHandle( joint );
4872 
4873  idVec3 offset;
4874  idMat3 axis;
4875 
4876  GetJointWorldTransform( jointNum, gameLocal.time, offset, axis );
4877 
4878  /*animator.GetJointTransform( jointNum, gameLocal.time, offset, axis );
4879  offset = GetPhysics()->GetOrigin() + offset * GetPhysics()->GetAxis();
4880  axis = axis * GetPhysics()->GetAxis();*/
4881 
4882 
4883 
4884  idDict args;
4885 
4886  const idDeclEntityDef *emitterDef = gameLocal.FindEntityDef( "func_emitter", false );
4887  args = emitterDef->dict;
4888  args.Set("model", particle);
4889  args.Set( "origin", offset.ToString() );
4890  args.SetBool("start_off", true);
4891 
4892  idEntity* ent;
4893  gameLocal.SpawnEntityDef(args, &ent, false);
4894 
4895  ent->GetPhysics()->SetOrigin(offset);
4896  //ent->GetPhysics()->SetAxis(axis);
4897 
4898  // align z-axis of model with the direction
4899  /*idVec3 tmp;
4900  axis = (viewAxis[ 0 ] * physicsObj.GetGravityAxis()).ToMat3();
4901  tmp = axis[2];
4902  axis[2] = axis[0];
4903  axis[0] = -tmp;
4904 
4905  ent->GetPhysics()->SetAxis(axis);*/
4906 
4907  axis = physicsObj.GetGravityAxis();
4908  ent->GetPhysics()->SetAxis(axis);
4909 
4910 
4911  ent->GetPhysics()->GetClipModel()->SetOwner( this );
4912 
4913 
4914  //Keep a reference to the emitter so we can track it
4915  funcEmitter_t newEmitter;
4916  strcpy(newEmitter.name, name);
4917  newEmitter.particle = (idFuncEmitter*)ent;
4918  newEmitter.joint = jointNum;
4919  funcEmitters.Set(newEmitter.name, newEmitter);
4920 
4921  //Bind it to the joint and make it active
4922  newEmitter.particle->BindToJoint(this, jointNum, true);
4923  newEmitter.particle->BecomeActive(TH_THINK);
4924  newEmitter.particle->Show();
4925  newEmitter.particle->PostEventMS(&EV_Activate, 0, this);
4926  return newEmitter.particle;
4927 }
4928 
4929 idEntity* idAI::GetEmitter( const char* name ) {
4930  funcEmitter_t* emitter;
4931  funcEmitters.Get(name, &emitter);
4932  if(emitter) {
4933  return emitter->particle;
4934  }
4935  return NULL;
4936 }
4937 
4938 void idAI::StopEmitter( const char* name ) {
4939  funcEmitter_t* emitter;
4940  funcEmitters.Get(name, &emitter);
4941  if(emitter) {
4942  emitter->particle->Unbind();
4943  emitter->particle->PostEventMS( &EV_Remove, 0 );
4944  funcEmitters.Remove(name);
4945  }
4946 }
4947 
4948 #endif
4949 
4950 
4951 /***********************************************************************
4952 
4953  Head & torso aiming
4954 
4955 ***********************************************************************/
4956 
4957 /*
4958 ================
4959 idAI::UpdateAnimationControllers
4960 ================
4961 */
4963  idVec3 local;
4964  idVec3 focusPos;
4965  idQuat jawQuat;
4966  idVec3 left;
4967  idVec3 dir;
4968  idVec3 orientationJointPos;
4969  idVec3 localDir;
4970  idAngles newLookAng;
4971  idAngles diff;
4972  idMat3 mat;
4973  idMat3 axis;
4974  idMat3 orientationJointAxis;
4975  idAFAttachment *headEnt = head.GetEntity();
4976  idVec3 eyepos;
4977  idVec3 pos;
4978  int i;
4979  idAngles jointAng;
4980  float orientationJointYaw;
4981 
4982  if ( AI_DEAD ) {
4984  }
4985 
4986  if ( orientationJoint == INVALID_JOINT ) {
4987  orientationJointAxis = viewAxis;
4988  orientationJointPos = physicsObj.GetOrigin();
4989  orientationJointYaw = current_yaw;
4990  } else {
4991  GetJointWorldTransform( orientationJoint, gameLocal.time, orientationJointPos, orientationJointAxis );
4992  orientationJointYaw = orientationJointAxis[ 2 ].ToYaw();
4993  orientationJointAxis = idAngles( 0.0f, orientationJointYaw, 0.0f ).ToMat3();
4994  }
4995 
4996  if ( focusJoint != INVALID_JOINT ) {
4997  if ( headEnt ) {
4998  headEnt->GetJointWorldTransform( focusJoint, gameLocal.time, eyepos, axis );
4999  } else {
5000  GetJointWorldTransform( focusJoint, gameLocal.time, eyepos, axis );
5001  }
5002  eyeOffset.z = eyepos.z - physicsObj.GetOrigin().z;
5003  if ( ai_debugMove.GetBool() ) {
5004  gameRenderWorld->DebugLine( colorRed, eyepos, eyepos + orientationJointAxis[ 0 ] * 32.0f, gameLocal.msec );
5005  }
5006  } else {
5007  eyepos = GetEyePosition();
5008  }
5009 
5010  if ( headEnt ) {
5012  }
5013 
5014  // Update the IK after we've gotten all the joint positions we need, but before we set any joint positions.
5015  // Getting the joint positions causes the joints to be updated. The IK gets joint positions itself (which
5016  // are already up to date because of getting the joints in this function) and then sets their positions, which
5017  // forces the heirarchy to be updated again next time we get a joint or present the model. If IK is enabled,
5018  // or if we have a seperate head, we end up transforming the joints twice per frame. Characters with no
5019  // head entity and no ik will only transform their joints once. Set g_debuganim to the current entity number
5020  // in order to see how many times an entity transforms the joints per frame.
5022 
5023  idEntity *focusEnt = focusEntity.GetEntity();
5024  if ( !allowJointMod || !allowEyeFocus || ( gameLocal.time >= focusTime ) ) {
5025  focusPos = GetEyePosition() + orientationJointAxis[ 0 ] * 512.0f;
5026  } else if ( focusEnt == NULL ) {
5027  // keep looking at last position until focusTime is up
5028  focusPos = currentFocusPos;
5029  } else if ( focusEnt == enemy.GetEntity() ) {
5031  } else if ( focusEnt->IsType( idActor::Type ) ) {
5032  focusPos = static_cast<idActor *>( focusEnt )->GetEyePosition() - eyeVerticalOffset * focusEnt->GetPhysics()->GetGravityNormal();
5033  } else {
5034  focusPos = focusEnt->GetPhysics()->GetOrigin();
5035  }
5036 
5038 
5039  // determine yaw from origin instead of from focus joint since joint may be offset, which can cause us to bounce between two angles
5040  dir = focusPos - orientationJointPos;
5041  newLookAng.yaw = idMath::AngleNormalize180( dir.ToYaw() - orientationJointYaw );
5042  newLookAng.roll = 0.0f;
5043  newLookAng.pitch = 0.0f;
5044 
5045 #if 0
5046  gameRenderWorld->DebugLine( colorRed, orientationJointPos, focusPos, gameLocal.msec );
5047  gameRenderWorld->DebugLine( colorYellow, orientationJointPos, orientationJointPos + orientationJointAxis[ 0 ] * 32.0f, gameLocal.msec );
5048  gameRenderWorld->DebugLine( colorGreen, orientationJointPos, orientationJointPos + newLookAng.ToForward() * 48.0f, gameLocal.msec );
5049 #endif
5050 
5051  // determine pitch from joint position
5052  dir = focusPos - eyepos;
5053  dir.NormalizeFast();
5054  orientationJointAxis.ProjectVector( dir, localDir );
5055  newLookAng.pitch = -idMath::AngleNormalize180( localDir.ToPitch() );
5056  newLookAng.roll = 0.0f;
5057 
5058  diff = newLookAng - lookAng;
5059 
5060  if ( eyeAng != diff ) {
5061  eyeAng = diff;
5062  eyeAng.Clamp( eyeMin, eyeMax );
5063  idAngles angDelta = diff - eyeAng;
5064  if ( !angDelta.Compare( ang_zero, 0.1f ) ) {
5066  } else {
5068  }
5069  }
5070 
5071  if ( idMath::Fabs( newLookAng.yaw ) < 0.1f ) {
5073  }
5074 
5075  if ( ( gameLocal.time >= alignHeadTime ) || ( gameLocal.time < forceAlignHeadTime ) ) {
5077  destLookAng = newLookAng;
5079  }
5080 
5081  diff = destLookAng - lookAng;
5082  if ( ( lookMin.pitch == -180.0f ) && ( lookMax.pitch == 180.0f ) ) {
5083  if ( ( diff.pitch > 180.0f ) || ( diff.pitch <= -180.0f ) ) {
5084  diff.pitch = 360.0f - diff.pitch;
5085  }
5086  }
5087  if ( ( lookMin.yaw == -180.0f ) && ( lookMax.yaw == 180.0f ) ) {
5088  if ( diff.yaw > 180.0f ) {
5089  diff.yaw -= 360.0f;
5090  } else if ( diff.yaw <= -180.0f ) {
5091  diff.yaw += 360.0f;
5092  }
5093  }
5094  lookAng = lookAng + diff * headFocusRate;
5095  lookAng.Normalize180();
5096 
5097  jointAng.roll = 0.0f;
5098  for( i = 0; i < lookJoints.Num(); i++ ) {
5099  jointAng.pitch = lookAng.pitch * lookJointAngles[ i ].pitch;
5100  jointAng.yaw = lookAng.yaw * lookJointAngles[ i ].yaw;
5101  animator.SetJointAxis( lookJoints[ i ], JOINTMOD_WORLD, jointAng.ToMat3() );
5102  }
5103 
5104  if ( move.moveType == MOVETYPE_FLY ) {
5105  // lean into turns
5107  }
5108 
5109  if ( headEnt ) {
5110  idAnimator *headAnimator = headEnt->GetAnimator();
5111 
5112  if ( allowEyeFocus ) {
5113  idMat3 eyeAxis = ( lookAng + eyeAng ).ToMat3(); idMat3 headTranspose = headEnt->GetPhysics()->GetAxis().Transpose();
5114  axis = eyeAxis * orientationJointAxis;
5115  left = axis[ 1 ] * eyeHorizontalOffset;
5116  eyepos -= headEnt->GetPhysics()->GetOrigin();
5117  headAnimator->SetJointPos( leftEyeJoint, JOINTMOD_WORLD_OVERRIDE, eyepos + ( axis[ 0 ] * 64.0f + left ) * headTranspose );
5118  headAnimator->SetJointPos( rightEyeJoint, JOINTMOD_WORLD_OVERRIDE, eyepos + ( axis[ 0 ] * 64.0f - left ) * headTranspose );
5119  } else {
5120  headAnimator->ClearJoint( leftEyeJoint );
5121  headAnimator->ClearJoint( rightEyeJoint );
5122  }
5123  } else {
5124  if ( allowEyeFocus ) {
5125  idMat3 eyeAxis = ( lookAng + eyeAng ).ToMat3();
5126  axis = eyeAxis * orientationJointAxis;
5127  left = axis[ 1 ] * eyeHorizontalOffset;
5128  eyepos += axis[ 0 ] * 64.0f - physicsObj.GetOrigin();
5131  } else {
5134  }
5135  }
5136 
5137  return true;
5138 }
5139 
5140 /***********************************************************************
5141 
5142 idCombatNode
5143 
5144 ***********************************************************************/
5145 
5146 const idEventDef EV_CombatNode_MarkUsed( "markUsed" );
5147 
5149  EVENT( EV_CombatNode_MarkUsed, idCombatNode::Event_MarkUsed )
5150  EVENT( EV_Activate, idCombatNode::Event_Activate )
5151 END_CLASS
5152 
5153 /*
5154 =====================
5155 idCombatNode::idCombatNode
5156 =====================
5157 */
5159  min_dist = 0.0f;
5160  max_dist = 0.0f;
5161  cone_dist = 0.0f;
5162  min_height = 0.0f;
5163  max_height = 0.0f;
5164  cone_left.Zero();
5165  cone_right.Zero();
5166  offset.Zero();
5167  disabled = false;
5168 }
5169 
5170 /*
5171 =====================
5172 idCombatNode::Save
5173 =====================
5174 */
5175 void idCombatNode::Save( idSaveGame *savefile ) const {
5176  savefile->WriteFloat( min_dist );
5177  savefile->WriteFloat( max_dist );
5178  savefile->WriteFloat( cone_dist );
5179  savefile->WriteFloat( min_height );
5180  savefile->WriteFloat( max_height );
5181  savefile->WriteVec3( cone_left );
5182  savefile->WriteVec3( cone_right );
5183  savefile->WriteVec3( offset );
5184  savefile->WriteBool( disabled );
5185 }
5186 
5187 /*
5188 =====================
5189 idCombatNode::Restore
5190 =====================
5191 */
5193  savefile->ReadFloat( min_dist );
5194  savefile->ReadFloat( max_dist );
5195  savefile->ReadFloat( cone_dist );
5196  savefile->ReadFloat( min_height );
5197  savefile->ReadFloat( max_height );
5198  savefile->ReadVec3( cone_left );
5199  savefile->ReadVec3( cone_right );
5200  savefile->ReadVec3( offset );
5201  savefile->ReadBool( disabled );
5202 }
5203 
5204 /*
5205 =====================
5206 idCombatNode::Spawn
5207 =====================
5208 */
5209 void idCombatNode::Spawn( void ) {
5210  float fov;
5211  float yaw;
5212  float height;
5213 
5214  min_dist = spawnArgs.GetFloat( "min" );
5215  max_dist = spawnArgs.GetFloat( "max" );
5216  height = spawnArgs.GetFloat( "height" );
5217  fov = spawnArgs.GetFloat( "fov", "60" );
5218  offset = spawnArgs.GetVector( "offset" );
5219 
5220  const idVec3 &org = GetPhysics()->GetOrigin() + offset;
5221  min_height = org.z - height * 0.5f;
5223 
5224  const idMat3 &axis = GetPhysics()->GetAxis();
5225  yaw = axis[ 0 ].ToYaw();
5226 
5227  idAngles leftang( 0.0f, yaw + fov * 0.5f - 90.0f, 0.0f );
5228  cone_left = leftang.ToForward();
5229 
5230  idAngles rightang( 0.0f, yaw - fov * 0.5f + 90.0f, 0.0f );
5231  cone_right = rightang.ToForward();
5232 
5233  disabled = spawnArgs.GetBool( "start_off" );
5234 }
5235 
5236 /*
5237 =====================
5238 idCombatNode::IsDisabled
5239 =====================
5240 */
5241 bool idCombatNode::IsDisabled( void ) const {
5242  return disabled;
5243 }
5244 
5245 /*
5246 =====================
5247 idCombatNode::DrawDebugInfo
5248 =====================
5249 */
5251  idEntity *ent;
5252  idCombatNode *node;
5253  idPlayer *player = gameLocal.GetLocalPlayer();
5254  idVec4 color;
5255  idBounds bounds( idVec3( -16, -16, 0 ), idVec3( 16, 16, 0 ) );
5256 
5257  for( ent = gameLocal.spawnedEntities.Next(); ent != NULL; ent = ent->spawnNode.Next() ) {
5258  if ( !ent->IsType( idCombatNode::Type ) ) {
5259  continue;
5260  }
5261 
5262  node = static_cast<idCombatNode *>( ent );
5263  if ( node->disabled ) {
5264  color = colorMdGrey;
5265  } else if ( player && node->EntityInView( player, player->GetPhysics()->GetOrigin() ) ) {
5266  color = colorYellow;
5267  } else {
5268  color = colorRed;
5269  }
5270 
5271  idVec3 leftDir( -node->cone_left.y, node->cone_left.x, 0.0f );
5272  idVec3 rightDir( node->cone_right.y, -node->cone_right.x, 0.0f );
5273  idVec3 org = node->GetPhysics()->GetOrigin() + node->offset;
5274 
5275  bounds[ 1 ].z = node->max_height;
5276 
5277  leftDir.NormalizeFast();
5278  rightDir.NormalizeFast();
5279 
5280  const idMat3 &axis = node->GetPhysics()->GetAxis();
5281  float cone_dot = node->cone_right * axis[ 1 ];
5282  if ( idMath::Fabs( cone_dot ) > 0.1 ) {
5283  float cone_dist = node->max_dist / cone_dot;
5284  idVec3 pos1 = org + leftDir * node->min_dist;
5285  idVec3 pos2 = org + leftDir * cone_dist;
5286  idVec3 pos3 = org + rightDir * node->min_dist;
5287  idVec3 pos4 = org + rightDir * cone_dist;
5288 
5289  gameRenderWorld->DebugLine( color, node->GetPhysics()->GetOrigin(), ( pos1 + pos3 ) * 0.5f, gameLocal.msec );
5290  gameRenderWorld->DebugLine( color, pos1, pos2, gameLocal.msec );
5291  gameRenderWorld->DebugLine( color, pos1, pos3, gameLocal.msec );
5292  gameRenderWorld->DebugLine( color, pos3, pos4, gameLocal.msec );
5293  gameRenderWorld->DebugLine( color, pos2, pos4, gameLocal.msec );
5294  gameRenderWorld->DebugBounds( color, bounds, org, gameLocal.msec );
5295  }
5296  }
5297 }
5298 
5299 /*
5300 =====================
5301 idCombatNode::EntityInView
5302 =====================
5303 */
5304 bool idCombatNode::EntityInView( idActor *actor, const idVec3 &pos ) {
5305  if ( !actor || ( actor->health <= 0 ) ) {
5306  return false;
5307  }
5308 
5309  const idBounds &bounds = actor->GetPhysics()->GetBounds();
5310  if ( ( pos.z + bounds[ 1 ].z < min_height ) || ( pos.z + bounds[ 0 ].z >= max_height ) ) {
5311  return false;
5312  }
5313 
5314  const idVec3 &org = GetPhysics()->GetOrigin() + offset;
5315  const idMat3 &axis = GetPhysics()->GetAxis();
5316  idVec3 dir = pos - org;
5317  float dist = dir * axis[ 0 ];
5318 
5319  if ( ( dist < min_dist ) || ( dist > max_dist ) ) {
5320  return false;
5321  }
5322 
5323  float left_dot = dir * cone_left;
5324  if ( left_dot < 0.0f ) {
5325  return false;
5326  }
5327 
5328  float right_dot = dir * cone_right;
5329  if ( right_dot < 0.0f ) {
5330  return false;
5331  }
5332 
5333  return true;
5334 }
5335 
5336 /*
5337 =====================
5338 idCombatNode::Event_Activate
5339 =====================
5340 */
5342  disabled = !disabled;
5343 }
5344 
5345 /*
5346 =====================
5347 idCombatNode::Event_MarkUsed
5348 =====================
5349 */
5351  if ( spawnArgs.GetBool( "use_once" ) ) {
5352  disabled = true;
5353  }
5354 }
virtual idVec3 AreaCenter(int areaNum) const =0
virtual const idVec3 & GetOrigin(int id=0) const =0
bool GetAimDir(const idVec3 &firePos, idEntity *aimAtEnt, const idEntity *ignore, idVec3 &aimDir) const
Definition: AI.cpp:4095
virtual void DormantBegin(void)
Definition: Entity.cpp:968
jointHandle_t
Definition: Model.h:156
idPlayer * GetLocalPlayer() const
idVec3 moveDir
Definition: AI.h:195
idList< idAngles > lookJointAngles
Definition: AI.h:378
void SetupPose(idEntity *ent, int time)
Definition: AF.cpp:231
int ClipModelsTouchingBounds(const idBounds &bounds, int contentMask, idClipModel **clipModelList, int maxCount) const
Definition: Clip.cpp:804
void UseFlyMove(bool force)
float fly_bob_horz
Definition: AI.h:322
void Restore(idRestoreGame *savefile)
Definition: AI.cpp:560
idEntity * GetEntity(void) const
Definition: Clip.h:178
bool TouchTriggers(void) const
Definition: Entity.cpp:3696
idAAS * aas
Definition: AI.h:293
void ClearEnemy(void)
Definition: AI.cpp:3732
byte color[4]
Definition: MegaTexture.cpp:54
renderEntity_t renderEntity
Definition: Entity.h:371
bool TracePoint(trace_t &results, const idVec3 &start, const idVec3 &end, int contentMask, const idEntity *passEntity)
Definition: Clip.h:333
idBounds & TranslateSelf(const idVec3 &translation)
Definition: Bounds.h:336
void SetClipModel(idClipModel *model, float density, int id=0, bool freeOld=true)
#define strcmp
Definition: Str.h:41
bool TurnToward(float yaw)
Definition: AI.cpp:2541
float GetFloat(const char *key, const char *defaultString="0") const
Definition: Dict.h:248
idMat3 ToMat3(void) const
Definition: Vector.cpp:195
void TouchedByFlashlight(idActor *flashlight_owner)
Definition: AI.cpp:3721
idBounds absBounds
Definition: AAS.h:63
bool GetJointTransform(jointHandle_t jointHandle, int currenttime, idVec3 &offset, idMat3 &axis)
virtual void GetAASLocation(idAAS *aas, idVec3 &pos, int &areaNum) const
Definition: Actor.cpp:1905
void EndAttack(void)
Definition: AI.cpp:4159
virtual bool TestArea(const idAAS *aas, int areaNum)
Definition: AI.cpp:159
bool allowHiddenMovement
Definition: AI.h:333
void WriteString(const char *string)
Definition: SaveGame.cpp:231
float Normalize(void)
Definition: Vector.h:646
idVec4 colorGreen
Definition: Lib.cpp:118
bool EmitSmoke(const idDeclParticle *smoke, const int startTime, const float diversity, const idVec3 &origin, const idMat3 &axis, int timeGroup)
moveStatus_t
Definition: AI.h:96
bool PointVisible(const idVec3 &point) const
Definition: Actor.cpp:1534
int GetInt(const char *key, const char *defaultString="0") const
Definition: Dict.h:252
idAI()
Definition: AI.cpp:284
float eyeFocusRate
Definition: AI.h:381
const idEventDef EV_CombatNode_MarkUsed("markUsed")
void ReadSoundShader(const idSoundShader *&shader)
Definition: SaveGame.cpp:1196
idVec3 targetPos
Definition: AI.h:246
void SetMaxStepHeight(const float newMaxStepHeight)
const idSoundShader * chat_snd
Definition: AI.h:355
virtual const idSoundShader * FindSound(const char *name, bool makeDefault=true)=0
const idDict * FindEntityDefDict(const char *name, bool makeDefault=true) const
void ReadParticle(const idDeclParticle *&particle)
Definition: SaveGame.cpp:1164
int startTime
Definition: AI.h:199
virtual const idVec3 & GetGravityNormal(void) const =0
void Link(idClip &clp)
Definition: Clip.cpp:545
#define ATTACK_IGNORE
Definition: AI.h:48
void Restore(idRestoreGame *savefile)
Definition: Game_local.h:661
const int SHADERPARM_DIVERSITY
Definition: RenderWorld.h:52
int Cmp(const char *text) const
Definition: Str.h:652
idClipModel * projectileClipModel
Definition: AI.h:346
virtual void FreeLightDef(qhandle_t lightHandle)=0
bool MoveToAttackPosition(idEntity *ent, int attack_anim)
Definition: AI.cpp:1913
void TalkTo(idActor *actor)
Definition: AI.cpp:3672
idMat3 mat3_identity(idVec3(1, 0, 0), idVec3(0, 1, 0), idVec3(0, 0, 1))
bool EntityInView(idActor *actor, const idVec3 &pos)
Definition: AI.cpp:5304
short toAreaNum
Definition: AASFile.h:96
void LinkScriptVariables(void)
Definition: AI.cpp:1247
idLinkList< idActor > enemyNode
Definition: Actor.h:119
void WriteObject(const idClass *obj)
Definition: SaveGame.cpp:329
virtual void DebugArrow(const idVec4 &color, const idVec3 &start, const idVec3 &end, int size, const int lifetime=0)=0
idVec3 GetCenter(void) const
Definition: Bounds.h:211
virtual bool TestArea(const idAAS *aas, int areaNum)
Definition: AI.cpp:246
idAngles eyeAng
Definition: AI.h:372
virtual void DeconstructScriptObject(void)
Definition: Entity.cpp:3227
bool GetFloorPos(float max_dist, idVec3 &floorpos) const
Definition: Entity.cpp:2821
idClip clip
Definition: Game_local.h:296
bool MoveToEntity(idEntity *ent)
Definition: AI.cpp:1785
void CheckObstacleAvoidance(const idVec3 &goalPos, idVec3 &newPos)
Definition: AI.cpp:2618
idVec3 cone_right
Definition: AI.h:728
idVec4 colorWhite
Definition: Lib.cpp:116
#define DI_NODIR
Definition: AI.h:108
virtual void Launch(const idVec3 &start, const idVec3 &dir, const idVec3 &pushVelocity, const float timeSinceFire=0.0f, const float launchPower=1.0f, const float dmgPower=1.0f)
Definition: Projectile.cpp:306
void Printf(const char *fmt,...) const id_attribute((format(printf
Definition: Game_local.cpp:699
idVec3 targetPos
Definition: AI.h:231
idEntity * target
Definition: AI.h:244
int NumAnims(void) const
idEntityPtr< idActor > talkTarget
Definition: AI.h:360
void Clamp(const idAngles &min, const idAngles &max)
Definition: Angles.h:232
idAngles lookMin
Definition: AI.h:375
struct idEntity::entityFlags_s fl
void BindToJoint(idEntity *master, const char *jointname, bool orientated)
Definition: Entity.cpp:1921
void FreeCurrentPVS(pvsHandle_t handle) const
Definition: Pvs.cpp:1127
#define MAX_GENTITIES
Definition: Game_local.h:83
int chat_max
Definition: AI.h:357
type * GetEntity(void) const
Definition: Game_local.h:695
float y
Definition: Vector.h:55
idEntityPtr< idEntity > focusEntity
Definition: AI.h:367
const float AI_HEARING_RANGE
Definition: AI.h:45
void ChangePose(idEntity *ent, int time)
Definition: AF.cpp:284
idActor * GetEnemy(void) const
Definition: AI.cpp:3697
idSmokeParticles * smokeParticles
Definition: Game_local.h:307
float GetFloat(void) const
Definition: CVarSystem.h:144
float maxStepHeight
Definition: AASFile.h:228
void PlayChatter(void)
Definition: AI.cpp:4770
const idStr & GetKey(void) const
Definition: Dict.h:52
void Restore(idRestoreGame *savefile)
Definition: AI.cpp:5192
bool disableGravity
Definition: AI.h:334
idScriptBool AI_DEAD
Definition: AI.h:426
void SetNum(int newnum, bool resize=true)
Definition: List.h:289
idScriptBool AI_ENEMY_IN_FOV
Definition: AI.h:428
bool SetSyncedAnimWeight(int num, float weight)
idVec3 lastVisibleReachableEnemyPos
Definition: AI.h:409
bool FacingIdeal(void)
Definition: AI.cpp:2519
idScriptBool AI_HIT_ENEMY
Definition: AI.h:439
void SetMass(float mass, int id=-1)
idEntity * startPosObstacle
Definition: AI.h:115
const idEventDef EV_Activate("activate","e")
idStr attack
Definition: AI.h:352
float anim_turn_angles
Definition: AI.h:312
GLint location
Definition: glext.h:3631
idCVar ai_showObstacleAvoidance("ai_showObstacleAvoidance","0", CVAR_GAME|CVAR_INTEGER,"draws obstacle avoidance information for monsters. if 2, draws obstacles for player, as well", 0, 2, idCmdSystem::ArgCompletion_Integer< 0, 2 >)
#define ATTACK_ON_SIGHT
Definition: AI.h:51
void SetEnemy(idActor *newEnemy)
Definition: AI.cpp:3945
float kickForce
Definition: AI.h:299
idMat3 Transpose(void) const
Definition: Matrix.h:677
idRandom random
Definition: Game_local.h:291
idVec3 modelOffset
Definition: Actor.h:221
int Length(void) const
Definition: Str.h:702
void void void void void Error(const char *fmt,...) const id_attribute((format(printf
Definition: Game_local.cpp:783
idScriptBool AI_DAMAGE
Definition: AI.h:423
int areaNum
Definition: AAS.h:57
idScriptObject scriptObject
Definition: Entity.h:123
idAngles GetAngles(const char *key, const char *defaultString=NULL) const
Definition: Dict.h:278
void Spawn(void)
Definition: AI.cpp:5209
moveStatus_t moveStatus
Definition: AI.h:193
void Save(idSaveGame *savefile) const
Definition: AI.cpp:416
int chat_time
Definition: AI.h:358
void DeadMove(void)
Definition: AI.cpp:2709
bool IsType(const idTypeInfo &c) const
Definition: Class.h:337
void SetCombatModel(void)
Definition: Actor.cpp:1581
idScriptBool AI_JUMP
Definition: AI.h:434
float max_dist
Definition: AI.h:723
void Set(const float x, const float y, const float z)
Definition: Vector.h:409
int lastMoveTime
Definition: AI.h:208
void CreateProjectileClipModel(void) const
Definition: AI.cpp:4082
idEntityPtr< idActor > enemy
Definition: AI.h:406
int lastHitCheckTime
Definition: AI.h:339
idCVar g_monsters("g_monsters","1", CVAR_GAME|CVAR_BOOL,"")
const idAI * self
Definition: AI.h:243
int PVSAreas[idEntity::MAX_PVS_AREAS]
Definition: AI.h:221
int lastSavingThrowTime
Definition: Player.h:267
void DirectDamage(const char *meleeDefName, idEntity *ent)
Definition: AI.cpp:4371
int previousTime
Definition: Game_local.h:318
idLinkList< idActor > enemyList
Definition: Actor.h:120
bool WanderAround(void)
Definition: AI.cpp:2091
float z
Definition: Vector.h:320
#define MASK_SOLID
Definition: Game_local.h:735
int current_cinematic
Definition: AI.h:364
void SetOrigin(const idVec3 &newOrigin, int id=-1)
const idKeyValue * MatchPrefix(const char *prefix, const idKeyValue *lastMatch=NULL) const
Definition: Dict.cpp:523
int focusAlignTime
Definition: AI.h:383
float headFocusRate
Definition: AI.h:382
bool ProcessEvent(const idEventDef *ev)
Definition: Class.cpp:858
void DisableClip(void)
int forceAlignHeadTime
Definition: AI.h:371
int endEvent
Definition: AI.h:134
void UpdateEnemyPosition(void)
Definition: AI.cpp:3872
void SetEnemyPosition(void)
Definition: AI.cpp:3778
void SetGranularity(int newgranularity)
Definition: List.h:305
Definition: AAS.h:56
idAngles lookMax
Definition: AI.h:376
idScriptFloat AI_SPECIAL_DAMAGE
Definition: AI.h:425
float melee_range
Definition: AI.h:341
float fly_roll
Definition: AI.h:327
int FindFrameForFrameCommand(frameCommandType_t framecommand, const frameCommand_t **command) const
static float AngleDelta(float angle1, float angle2)
Definition: Math.h:918
jointHandle_t GetJointHandle(const char *name) const
moveCommand_t moveCommand
Definition: AI.h:192
float anim_turn_amount
Definition: AI.h:311
const char * GetName(void) const
Definition: DeclManager.h:140
Definition: Vector.h:316
idEntity * seekPosObstacle
Definition: AI.h:117
void ReadJoint(jointHandle_t &value)
Definition: SaveGame.cpp:931
case const float
Definition: Callbacks.cpp:62
void ReadBool(bool &value)
Definition: SaveGame.cpp:976
float eyeVerticalOffset
Definition: AI.h:379
idAngles ang_zero(0.0f, 0.0f, 0.0f)
void Activate(idEntity *activator)
Definition: AI.cpp:3604
void WriteStaticObject(const idClass &obj)
Definition: SaveGame.cpp:348
ID_INLINE T Square(T x)
Definition: Math.h:104
bool SlideToPosition(const idVec3 &pos, float time)
Definition: AI.cpp:2062
float fly_pitch
Definition: AI.h:330
idPhysics_AF * GetPhysics(void)
Definition: AF.h:77
int GetPVSAreas(const idBounds &bounds, int *areas, int maxAreas) const
Definition: Pvs.cpp:939
static void List_f(const idCmdArgs &args)
Definition: AI.cpp:1043
bool wakeOnFlashlight
Definition: AI.h:411
idVec3 projectileVelocity
Definition: AI.h:349
const int SHADERPARM_GREEN
Definition: RenderWorld.h:47
const char * Left(int len, idStr &result) const
Definition: Str.h:892
int team
Definition: Actor.h:115
idVec3 Seek(idVec3 &vel, const idVec3 &org, const idVec3 &goal, float prediction)
Definition: AI.cpp:2833
void UseVelocityMove(bool force)
static bool FindPathAroundObstacles(const idPhysics *physics, const idAAS *aas, const idEntity *ignore, const idVec3 &startPos, const idVec3 &seekPos, obstaclePath_t &path)
Definition: AI_pathing.cpp:925
static bool PredictPath(const idEntity *ent, const idAAS *aas, const idVec3 &start, const idVec3 &velocity, int totalTime, int frameTime, int stopEvent, predictedPath_t &path)
virtual bool FindNearestGoal(aasGoal_t &goal, int areaNum, const idVec3 origin, const idVec3 &target, int travelFlags, aasObstacle_t *obstacles, int numObstacles, idAASCallback &callback) const =0
int blink_time
Definition: Actor.h:252
virtual void SetLinearVelocity(const idVec3 &newLinearVelocity, int id=0)=0
void FlySeekGoal(idVec3 &vel, idVec3 &goalPos)
Definition: AI.cpp:3073
void SetJointPos(jointHandle_t jointnum, jointModTransform_t transform_type, const idVec3 &pos)
idVec3 lastVisibleEnemyPos
Definition: AI.h:407
void Create(idEntity *owner, const idVec3 &start, const idVec3 &dir)
Definition: Projectile.cpp:218
bool IsDisabled(void) const
Definition: AI.cpp:5241
virtual qhandle_t AddLightDef(const renderLight_t *rlight)=0
virtual int PointReachableAreaNum(const idVec3 &origin, const idBounds &bounds, const int areaFlags) const =0
virtual const idMaterial * FindMaterial(const char *name, bool makeDefault=true)=0
#define AREA_REACHABLE_FLY
Definition: AASFile.h:75
bool allowEyeFocus
Definition: Actor.h:264
float fly_speed
Definition: AI.h:319
virtual void LinkCombat(void)
Definition: Actor.cpp:1613
idProjectile * CreateProjectile(const idVec3 &pos, const idVec3 &dir)
Definition: AI.cpp:4168
idCVar ai_debugTrajectory("ai_debugTrajectory","0", CVAR_GAME|CVAR_BOOL,"draws trajectory tests for monsters")
idVec3 moveGoal
Definition: AAS.h:49
const idDict * projectileDef
Definition: AI.h:345
const char * GetClassname(void) const
Definition: Class.cpp:593
#define MASK_OPAQUE
Definition: Game_local.h:740
float GetRadius(void) const
Definition: Bounds.cpp:39
void Set(const char *key, const char *value)
Definition: Dict.cpp:275
virtual void ApplyImpulse(idEntity *ent, int id, const idVec3 &point, const idVec3 &impulse)
Definition: Entity.cpp:2887
bool cinematic
Definition: Entity.h:127
idAngles lookAng
Definition: AI.h:373
idMat3 gravityAxis
Definition: AI.h:248
void CopyJointsFromBodyToHead(void)
Definition: Actor.cpp:748
idScriptBool AI_ACTIVATED
Definition: AI.h:432
void SetDelta(const idVec3 &d)
float x
Definition: Vector.h:318
idActor * GetAlertEntity(void)
idDict spawnArgs
Definition: Entity.h:122
bool PathToGoal(aasPath_t &path, int areaNum, const idVec3 &origin, int goalAreaNum, const idVec3 &goalOrigin) const
Definition: AI.cpp:1474
idBounds boundingBoxes[MAX_AAS_BOUNDING_BOXES]
Definition: AASFile.h:215
float current_yaw
Definition: AI.h:307
void DrawRoute(void) const
Definition: AI.cpp:1403
idAASFindAreaOutOfRange(const idVec3 &targetPos, float maxDist)
Definition: AI.cpp:180
int i
Definition: process.py:33
const char * Name(void) const
bool MoveToPosition(const idVec3 &pos)
Definition: AI.cpp:1965
#define MASK_MONSTERSOLID
Definition: Game_local.h:736
idVec3 lastReachableEnemyPos
Definition: AI.h:410
GLintptr offset
Definition: glext.h:3113
bool EntityCanSeePos(idActor *actor, const idVec3 &actorOrigin, const idVec3 &pos)
Definition: AI.cpp:2376
virtual void Hide(void)
Definition: AI.cpp:4673
virtual void DormantEnd(void)
Definition: AI.cpp:1102
bool Compare(const idAngles &a) const
Definition: Angles.h:204
idAnimState headAnim
Definition: Actor.h:259
GLuint GLuint num
Definition: glext.h:5390
void SetChatSound(void)
Definition: AI.cpp:4711
Boolean result
void WriteVec3(const idVec3 &vec)
Definition: SaveGame.cpp:253
idAngles & Zero(void)
Definition: Angles.h:126
idVec3 cone_left
Definition: AI.h:727
virtual void ClearJointMods(void)
Definition: IK.cpp:779
Definition: AI.h:122
const idDeclParticle * SpawnParticlesOnJoint(particleEmitter_t &pe, const char *particleName, const char *jointName)
Definition: AI.cpp:3361
float fly_bob_vert
Definition: AI.h:321
const idVec3 & GetOrigin(int id=0) const
virtual bool UpdateAnimationControllers(void)
Definition: AI.cpp:4962
idScriptBool AI_ENEMY_VISIBLE
Definition: AI.h:427
static const int MAX_PVS_AREAS
Definition: Entity.h:109
bool EnemyPositionValid(void) const
Definition: AI.cpp:3751
GLsizei range
Definition: glext.h:4368
const idVec2 & ToVec2(void) const
Definition: Vector.h:711
float fly_roll_max
Definition: AI.h:326
bool CheckDormant(void)
Definition: Entity.cpp:946
void WriteSoundShader(const idSoundShader *shader)
Definition: SaveGame.cpp:445
const int SHADERPARM_ALPHA
Definition: RenderWorld.h:49
float min_dist
Definition: AI.h:722
#define EVENT(event, function)
Definition: Class.h:53
idMat3 viewAxis
Definition: Actor.h:117
void SetContents(int contents, int id=-1)
Definition: AI.h:88
int lastAnimBlendFrames
Definition: Actor.h:67
float fraction
void SetJointAxis(jointHandle_t jointnum, jointModTransform_t transform_type, const idMat3 &mat)
idVec3 endpos
void WriteJoint(const jointHandle_t value)
Definition: SaveGame.cpp:177
void WriteBool(const bool value)
Definition: SaveGame.cpp:222
void UpdateParticles(void)
Definition: AI.cpp:4787
int toAreaNum
Definition: AI.h:198
idVec4 colorLtGrey
Definition: Lib.cpp:127
idVec4 colorRed
Definition: Lib.cpp:117
bool useBoneAxis
Definition: AI.h:390
virtual void Killed(idEntity *inflictor, idEntity *attacker, int damage, const idVec3 &dir, int location)
Definition: AI.cpp:3398
float shaderParms[MAX_ENTITY_SHADER_PARMS]
Definition: RenderWorld.h:201
bool ValidForBounds(const idAASSettings *settings, const idBounds &bounds)
Definition: AI.cpp:1358
idCVar g_skill("g_skill","1", CVAR_GAME|CVAR_INTEGER,"")
void FlyTurn(void)
Definition: AI.cpp:3110
idAnimator animator
Definition: Entity.h:534
bool IsHidden(void) const
Definition: Entity.cpp:1217
static float Sin16(float a)
Definition: Math.h:314
void KickObstacles(const idVec3 &dir, float force, idEntity *alwaysKick)
Definition: AI.cpp:1296
idEntity * firstObstacle
Definition: AI.h:113
const int SHADERPARM_TIMESCALE
Definition: RenderWorld.h:50
int muzzleFlashEnd
Definition: AI.h:396
void Event_MarkUsed(void)
Definition: AI.cpp:5350
bool AnimDone(int blendFrames) const
Definition: Actor.cpp:231
idScriptBool AI_MOVE_DONE
Definition: AI.h:430
void void void Warning(const char *fmt,...) const id_attribute((format(printf
Definition: Game_local.cpp:735
void AddFlyBob(idVec3 &vel)
Definition: AI.cpp:3005
bool CanSee(idEntity *ent, bool useFOV) const
Definition: Actor.cpp:1500
float LengthFast(void) const
Definition: Vector.h:159
pvsHandle_t SetupCurrentPVS(const idVec3 &source, const pvsType_t type=PVS_NORMAL) const
Definition: Pvs.cpp:948
Definition: AAS.h:47
idClipModel * GetClipModel(int id=0) const
idAngles deltaViewAngles
Definition: Actor.h:223
virtual const idBounds & GetBounds(int id=-1) const =0
virtual bool OnLadder(void) const
Definition: Actor.cpp:1896
idAASFindAttackPosition(const idAI *self, const idMat3 &gravityAxis, idEntity *target, const idVec3 &targetPos, const idVec3 &fireOffset)
Definition: AI.cpp:214
bool StartSoundShader(const idSoundShader *shader, const s_channelType channel, int soundShaderFlags, bool broadcast, int *length)
Definition: Entity.cpp:1656
Definition: AI.h:87
const int SHADERPARM_BLUE
Definition: RenderWorld.h:48
bool AddPoint(const idVec3 &v)
Definition: Bounds.h:226
int RandomInt(void)
Definition: Random.h:70
pvsHandle_t targetPVS
Definition: AI.h:249
void TriggerWeaponEffects(const idVec3 &muzzle)
Definition: AI.cpp:4617
idVec3 & Truncate(float length)
Definition: Vector.h:657
void GetDelta(int fromtime, int totime, idVec3 &delta) const
idVec3 GetEyePosition(void) const
Definition: Actor.cpp:1454
void CalcDamagePoints(idEntity *inflictor, idEntity *attacker, const idDict *damageDef, const float damageScale, const int location, int *health, int *armor)
void SetSelf(idEntity *e)
void Restore(idRestoreGame *savefile)
Definition: AI.cpp:109
GLuint GLuint GLsizei count
Definition: glext.h:2845
bool MoveToCover(idEntity *entity, const idVec3 &pos)
Definition: AI.cpp:2013
bool ReachedPos(const idVec3 &pos, const moveCommand_t moveCommand) const
Definition: AI.cpp:1418
idVec4 colorYellow
Definition: Lib.cpp:120
#define ATTACK_ON_DAMAGE
Definition: AI.h:49
bool StepDirection(float dir)
Definition: AI.cpp:2125
float TravelDistance(const idVec3 &start, const idVec3 &end) const
Definition: AI.cpp:1511
float projectile_height_to_distance_ratio
Definition: AI.h:342
idAAS * GetAAS(int num) const
Definition: Vector.h:52
bool allowJointMod
Definition: AI.h:366
void AdjustFlySpeed(idVec3 &vel)
Definition: AI.cpp:3087
int flashTime
Definition: AI.h:397
void RemoveOriginOffset(bool remove)
idScriptBool AI_PAIN
Definition: AI.h:424
idEntityPtr< idAFAttachment > head
Definition: Actor.h:233
idCVar g_debugCinematic("g_debugCinematic","0", CVAR_GAME|CVAR_BOOL,"")
void SetOwner(idEntity *newOwner)
Definition: Clip.h:190
idPhysics * GetPhysics(void) const
Definition: Entity.cpp:2607
virtual void DebugLine(const idVec4 &color, const idVec3 &start, const idVec3 &end, const int lifetime=0, const bool depthTest=false)=0
idAngles ToAngles(void) const
Definition: Vector.cpp:130
idProjectile * LaunchProjectile(const char *jointname, idEntity *target, bool clampToAttackCone)
Definition: AI.cpp:4208
const char * GetString(const char *key, const char *defaultString="") const
Definition: Dict.h:240
monsterMoveResult_t
bool SpawnEntityDef(const idDict &args, idEntity **ent=NULL, bool setDefaults=true)
void ReadFloat(float &value)
Definition: SaveGame.cpp:967
bool InCurrentPVS(const pvsHandle_t handle, const idVec3 &target) const
Definition: Pvs.cpp:1139
Definition: Vector.h:808
idVec3 lightRadius
Definition: RenderWorld.h:178
float Length(void) const
Definition: Vector.h:631
float ToYaw(void) const
Definition: Vector.cpp:84
idVec3 origin
Definition: AAS.h:58
idVec3 ToForward(void) const
Definition: Angles.cpp:117
float min_height
Definition: AI.h:725
Definition: AI.h:85
virtual void ShowWalkPath(const idVec3 &origin, int goalAreaNum, const idVec3 &goalOrigin) const =0
float RandomFloat(void)
Definition: Random.h:82
bool StartSound(const char *soundName, const s_channelType channel, int soundShaderFlags, bool broadcast, int *length)
Definition: Entity.cpp:1622
idVec3 vec3_origin(0.0f, 0.0f, 0.0f)
idAnimState legsAnim
Definition: Actor.h:261
virtual bool RouteToGoalArea(int areaNum, const idVec3 origin, int goalAreaNum, int travelFlags, int &travelTime, idReachability **reach) const =0
idVec3 lastMoveOrigin
Definition: AI.h:207
int PointReachableAreaNum(const idVec3 &pos, const float boundsScale=2.0f) const
Definition: AI.cpp:1446
idVec3 currentFocusPos
Definition: AI.h:368
void EnableClip(void)
idCVar g_debugDamage("g_debugDamage","0", CVAR_GAME|CVAR_BOOL,"")
#define vec3_zero
Definition: Vector.h:390
void SetPhysics(idPhysics *phys)
Definition: Entity.cpp:2574
void Unbind(void)
Definition: Entity.cpp:2011
idEntityPtr< idEntity > obstacle
Definition: AI.h:206
const int SAVING_THROW_TIME
Definition: Player.h:71
int EntitiesTouchingAF(afTouch_t touchList[MAX_GENTITIES]) const
Definition: AF.cpp:336
GLuint GLuint end
Definition: glext.h:2845
bool allowPain
Definition: Actor.h:263
static float Sin(float a)
Definition: Math.h:310
const function_t * state
Definition: Actor.h:237
void WriteFloat(const float value)
Definition: SaveGame.cpp:213
bool TestMelee(void) const
Definition: AI.cpp:4413
static float Fabs(float f)
Definition: Math.h:779
idCVar g_gravity("g_gravity", DEFAULT_GRAVITY_STRING, CVAR_GAME|CVAR_FLOAT,"")
const idVec3 & GetGravityNormal(void) const
idVec3 end
Definition: AASFile.h:99
virtual float GetMass(int id=-1) const =0
const int DEFAULT_FLY_OFFSET
Definition: AI.h:46
void ClearAllAnims(int currentTime, int cleartime)
void UpdateMuzzleFlash(void)
Definition: AI.cpp:4652
bool GetBool(const char *key, const char *defaultString="0") const
Definition: Dict.h:256
Definition: AF.h:49
Definition: Dict.h:65
#define NULL
Definition: Lib.h:88
float wanderYaw
Definition: AI.h:203
jointHandle_t flashJointWorld
Definition: AI.h:395
const float AI_TURN_SCALE
Definition: AI.h:42
static float AngleNormalize360(float angle)
Definition: Math.h:903
idCVar g_muzzleFlash("g_muzzleFlash","1", CVAR_GAME|CVAR_ARCHIVE|CVAR_BOOL,"show muzzle flashes")
const idBounds & GetBounds(void) const
Definition: Clip.h:198
idAnimBlend * CurrentAnim(int channelNum)
virtual const idDecl * FindType(declType_t type, const char *name, bool makeDefault=true)=0
float y
Definition: Vector.h:319
void BlockedFailSafe(void)
Definition: AI.cpp:2419
int ReactionTo(const idEntity *ent)
Definition: AI.cpp:3256
bool disabled
Definition: AI.h:730
int GetInteger(void) const
Definition: CVarSystem.h:143
~idAI()
Definition: AI.cpp:395
virtual bool TestArea(const idAAS *aas, int areaNum)
Definition: AI.cpp:190
pvsHandle_t hidePVS
Definition: AI.h:220
idVec3 GetVector(const char *key, const char *defaultString=NULL) const
Definition: Dict.h:260
idMoveState move
Definition: AI.h:296
bool GetMovePos(idVec3 &seekPos)
Definition: AI.cpp:2278
idAnimState torsoAnim
Definition: Actor.h:260
float projectileRadius
Definition: AI.h:347
int rank
Definition: Actor.h:116
const char * GetDamageGroup(int location)
Definition: Actor.cpp:2449
virtual void SetOrigin(const idVec3 &newOrigin, int id=-1)=0
#define TFL_WALK
Definition: AASFile.h:45
float speed
Definition: AI.h:201
const idEventDef AI_PlayAnim("playAnim","ds", 'd')
idScriptBool AI_DEST_UNREACHABLE
Definition: AI.h:438
virtual bool FlyPathToGoal(aasPath_t &path, int areaNum, const idVec3 &origin, int goalAreaNum, const idVec3 &goalOrigin, int travelFlags) const =0
const idVec3 & GetGravity(void) const
jointHandle_t joint
Definition: AI.h:173
bool IsTraceModel(void) const
Definition: Clip.h:218
void SetAAS(void)
Definition: AI.cpp:1377
int dormantStart
Definition: Entity.h:126
int nextWanderTime
Definition: AI.h:204
#define CM_CLIP_EPSILON
const idEntity * blockingEntity
Definition: AI.h:135
idVec3 seekPos
Definition: AI.h:112
float x
Definition: Vector.h:54
float fly_pitch_max
Definition: AI.h:329
float roll
Definition: Angles.h:55
bool use_combat_bbox
Definition: Actor.h:232
virtual void Show(void)
Definition: AI.cpp:4691
void BecomeIdle(void)
Definition: Actor.cpp:213
const char * path
Definition: sws.c:117
bool anim_turn
Definition: Anim.h:178
int GetNumPVSAreas(void)
Definition: Entity.cpp:1363
void BeginAttack(const char *name)
Definition: AI.cpp:4149
const idStr & GetValue(void) const
Definition: Dict.h:53
virtual const idMat3 & GetAxis(int id=0) const =0
virtual idAnimator * GetAnimator(void)
Definition: Entity.cpp:5213
idVec3 endVelocity
Definition: AI.h:131
idAngles eyeMin
Definition: AI.h:400
float NormalizeFast(void)
Definition: Vector.h:524
const char * Right(int len, idStr &result) const
Definition: Str.h:896
bool MoveOutOfRange(idEntity *entity, float range)
Definition: AI.cpp:1856
void ServiceAnims(int fromtime, int totime)
int focusTime
Definition: AI.h:369
const idDeclModelDef * ModelDef(void) const
virtual void DrawText(const char *text, const idVec3 &origin, float scale, const idVec4 &color, const idMat3 &viewAxis, const int align=1, const int lifetime=0, bool depthTest=false)=0
int Find(const char c, int start=0, int end=-1) const
Definition: Str.h:874
idBounds Rotate(const idMat3 &rotation) const
Definition: Bounds.h:342
#define MASK_SHOT_RENDERMODEL
Definition: Game_local.h:741
bool FaceEntity(idEntity *ent)
Definition: AI.cpp:1630
idLinkList< idEntity > spawnNode
Definition: Entity.h:114
virtual bool WalkPathToGoal(aasPath_t &path, int areaNum, const idVec3 &origin, int goalAreaNum, const idVec3 &goalOrigin, int travelFlags) const =0
float ToPitch(void) const
Definition: Vector.cpp:104
animFlags_t GetAnimFlags(void) const
Definition: Actor.cpp:259
float pitch
Definition: Angles.h:53
float fly_seek_scale
Definition: AI.h:324
idGameLocal gameLocal
Definition: Game_local.cpp:64
void SetBool(const char *key, bool val)
Definition: Dict.h:196
float LengthSqr(void) const
Definition: Vector.h:635
int PVSAreas[idEntity::MAX_PVS_AREAS]
Definition: AI.h:250
bool RunPhysics(void)
Definition: Entity.cpp:2616
void ForceUpdate(void)
const int SHADERPARM_TIMEOFFSET
Definition: RenderWorld.h:51
const float AI_SEEK_PREDICTION
Definition: AI.h:43
int blockedMoveTime
Definition: AI.h:302
idEntity * touchedEnt
Definition: AF.h:50
void AdjustFlyHeight(idVec3 &vel, const idVec3 &goalPos)
Definition: AI.cpp:3025
void AlertAI(idEntity *ent)
virtual void Damage(idEntity *inflictor, idEntity *attacker, const idVec3 &dir, const char *damageDefName, const float damageScale, const int location)
Definition: Entity.cpp:3061
#define END_CLASS
Definition: Class.h:54
bool restartParticles
Definition: AI.h:389
bool DirectMoveToPosition(const idVec3 &pos)
Definition: AI.cpp:1656
float fly_pitch_scale
Definition: AI.h:328
float max_height
Definition: AI.h:726
const idBounds & GetAbsBounds(int id=-1) const
virtual void UpdateLightDef(qhandle_t lightHandle, const renderLight_t *rlight)=0
#define DEG2RAD(a)
Definition: Math.h:56
idBounds Expand(const float d) const
Definition: Bounds.h:317
bool MoveToEnemy(void)
Definition: AI.cpp:1713
void CalculateAttackOffsets(void)
Definition: AI.cpp:4034
void Event_Activate(idEntity *activator)
Definition: AI.cpp:5341
bool ai_no_turn
Definition: Anim.h:177
idLinkList< idEntity > spawnedEntities
Definition: Game_local.h:281
idScriptBool AI_OBSTACLE_IN_PATH
Definition: AI.h:437
int blockTime
Definition: AI.h:205
virtual void ApplyImpulse(idEntity *ent, int id, const idVec3 &point, const idVec3 &impulse)
Definition: AI.cpp:2580
const float AI_FLY_DAMPENING
Definition: AI.h:44
void Save(idSaveGame *savefile) const
Definition: AI.cpp:82
virtual idClipModel * GetClipModel(int id=0) const =0
GLenum GLsizei GLsizei height
Definition: glext.h:2856
const char * ToString(int precision=2) const
Definition: Vector.cpp:221
void SlideMove(void)
Definition: AI.cpp:2851
void WriteInt(const int value)
Definition: SaveGame.cpp:168
virtual void Present(void)
Definition: Entity.cpp:1471
idVec3 fireOffset
Definition: AI.h:247
void ProjectVector(const idVec3 &src, idVec3 &dst) const
Definition: Matrix.h:628
renderLight_t worldMuzzleFlash
Definition: AI.h:393
void RemoveAttachments(void)
Definition: Actor.cpp:1740
int duration
Definition: AI.h:200
idDeclManager * declManager
Definition: AI.h:80
#define SEC2MS(t)
Definition: Math.h:59
void Save(idSaveGame *savefile) const
Definition: AI.cpp:5175
virtual void DamageFeedback(idEntity *victim, idEntity *inflictor, int &damage)
Definition: AI.cpp:4351
idScriptBool AI_BLOCKED
Definition: AI.h:436
void ForceDeltaMove(bool force)
bool GetDeltaRotation(int fromtime, int totime, idMat3 &delta) const
idEntity * entities[MAX_GENTITIES]
Definition: Game_local.h:275
Definition: Quat.h:48
int lastAttackTime
Definition: AI.h:340
talkState_t talk_state
Definition: AI.h:359
jointHandle_t orientationJoint
Definition: AI.h:403
void LinkTo(idScriptObject &obj, const char *name)
int health
Definition: Entity.h:134
idEntity * GetTraceEntity(const trace_t &trace) const
idVec4 colorCyan
Definition: Lib.cpp:122
void ReadStaticObject(idClass &obj)
Definition: SaveGame.cpp:1098
void StaticMove(void)
Definition: AI.cpp:3212
int Append(const type &obj)
Definition: List.h:646
idBounds excludeBounds
Definition: AI.h:245
idVec4 colorOrange
Definition: Lib.cpp:123
bool NewWanderDir(const idVec3 &dest)
Definition: AI.cpp:2187
float eyeHorizontalOffset
Definition: AI.h:380
void GetMuzzle(const char *jointname, idVec3 &muzzle, idMat3 &axis)
Definition: AI.cpp:4597
virtual void ApplyImpulse(idEntity *ent, int id, const idVec3 &point, const idVec3 &impulse)
Definition: AFEntity.cpp:721
float ideal_yaw
Definition: AI.h:306
Definition: Matrix.h:333
const int SHADERPARM_RED
Definition: RenderWorld.h:46
idVec3 FirstVisiblePointOnPath(const idVec3 origin, const idVec3 &target, int travelFlags) const
Definition: AI.cpp:3983
idAngles eyeMax
Definition: AI.h:401
void UpdateAnimState(void)
Definition: Actor.cpp:2058
void WriteAngles(const idAngles &angles)
Definition: SaveGame.cpp:318
void SetClipMask(int mask, int id=-1)
float yaw
Definition: Angles.h:54
idVec3 lastVisibleEnemyEyeOffset
Definition: AI.h:408
const idVec3 & GetWorldOrigin(void) const
Definition: Physics_AF.h:669
static bool PredictTrajectory(const idVec3 &firePos, const idVec3 &target, float projectileSpeed, const idVec3 &projGravity, const idClipModel *clip, int clipmask, float max_height, const idEntity *ignore, const idEntity *targetEntity, int drawtime, idVec3 &aimDir)
idStr * string
Definition: Anim.h:164
void UpdateVisuals(void)
Definition: Entity.cpp:1310
void UpdateAIScript(void)
Definition: AI.cpp:1273
idPhysics_Monster physicsObj
Definition: AI.h:315
virtual bool CanPlayChatterSounds(void) const
Definition: AI.cpp:4745
bool GetBool(void) const
Definition: CVarSystem.h:142
const idDeclParticle * particle
Definition: AI.h:170
virtual bool IsPushable(void) const =0
void SetGravity(const idVec3 &newGravity)
float cone_dist
Definition: AI.h:724
void AnimMove(void)
Definition: AI.cpp:2729
tuple f
Definition: idal.py:89
void GetMoveDelta(const idMat3 &oldaxis, const idMat3 &axis, idVec3 &delta)
Definition: AI.cpp:2594
void SetWaitState(const char *_waitstate)
Definition: Actor.cpp:2049
jointHandle_t leftEyeJoint
Definition: Actor.h:241
bool af_push_moveables
Definition: AI.h:335
Definition: AI.h:86
static idVec3 GetGravity(const idDict *projectile)
virtual void ShowFlyPath(const idVec3 &origin, int goalAreaNum, const idVec3 &goalOrigin) const =0
idAngles viewAngles
Definition: Player.h:258
Definition: Actor.h:111
int GetClipMask(int id=-1) const
int Num(void) const
Definition: List.h:265
#define AREA_REACHABLE_WALK
Definition: AASFile.h:74
bool MoveDone(void) const
Definition: AI.cpp:2116
int fly_offset
Definition: AI.h:323
#define ATTACK_ON_ACTIVATE
Definition: AI.h:50
idMat3 ToMat3(void) const
Definition: Angles.cpp:199
virtual bool Pain(idEntity *inflictor, idEntity *attacker, int damage, const idVec3 &dir, int location)
Definition: Actor.cpp:2292
virtual const idAASSettings * GetSettings(void) const =0
~idAASFindCover()
Definition: AI.cpp:150
bool IntersectsBounds(const idBounds &a) const
Definition: Bounds.h:361
virtual const idBounds & GetAbsBounds(int id=-1) const =0
idCVar ai_debugMove("ai_debugMove","0", CVAR_GAME|CVAR_BOOL,"draws movement information for monsters")
idVec3 projectileGravity
Definition: AI.h:350
#define TFL_AIR
Definition: AASFile.h:58
void EnemyDead(void)
Definition: AI.cpp:3662
idScriptBool AI_TALK
Definition: AI.h:422
const GLcharARB * name
Definition: glext.h:3629
float turnVel
Definition: AI.h:309
GLsizeiptr size
Definition: glext.h:3112
idVec3 goalEntityOrigin
Definition: AI.h:197
virtual void GetAIAimTargets(const idVec3 &lastSightPos, idVec3 &headPos, idVec3 &chestPos)
Definition: Actor.cpp:1553
static float AngleNormalize180(float angle)
Definition: Math.h:910
bool GetJointWorldTransform(jointHandle_t jointHandle, int currentTime, idVec3 &offset, idMat3 &axis)
Definition: Entity.cpp:5248
idVec3 eyeOffset
Definition: Actor.h:220
virtual bool UpdateAnimationControllers(void)
Definition: Actor.cpp:1719
void SpawnParticles(const char *keyName)
Definition: AI.cpp:3332
virtual void Damage(idEntity *inflictor, idEntity *attacker, const idVec3 &dir, const char *damageDefName, const float damageScale, const int location)
Definition: Actor.cpp:2203
idScriptBool AI_ONGROUND
Definition: AI.h:431
#define CLASS_DECLARATION(nameofsuperclass, nameofclass)
Definition: Class.h:110
Definition: Str.h:116
float anim_turn_yaw
Definition: AI.h:310
bool OnGround(void) const
const function_t * GetScriptFunction(const char *funcname)
Definition: Actor.cpp:1328
void SetLinearVelocity(const idVec3 &newLinearVelocity, int id=0)
const char * GetName(void) const
Definition: Entity.cpp:875
float Normalize(void)
Definition: Vector.h:170
int animBlendFrames
Definition: Actor.h:66
void UpdateAnimation(void)
Definition: Entity.cpp:5172
static int SnapTimeToPhysicsFrame(int t)
Definition: Physics.cpp:76
idAFBody * touchedByBody
Definition: AF.h:52
void Save(idSaveGame *savefile) const
Definition: Game_local.h:656
void ReadVec3(idVec3 &vec)
Definition: SaveGame.cpp:1011
static idVec3 GetVelocity(const idDict *projectile)
virtual void Show(void)
Definition: Actor.cpp:1050
virtual bool Pain(idEntity *inflictor, idEntity *attacker, int damage, const idVec3 &dir, int location)
Definition: AI.cpp:3297
float LengthFast(void) const
Definition: Vector.h:639
const char * c_str(void) const
Definition: Str.h:487
idList< jointHandle_t > lookJoints
Definition: AI.h:377
idVec3 endPos
Definition: AI.h:130
const idAnim * GetAnim(int index) const
int num_cinematics
Definition: AI.h:363
int shrivel_start
Definition: AI.h:387
virtual void Hide(void)
Definition: Actor.cpp:1024
bool Disabled(void) const
Definition: Actor.cpp:222
bool MoveToEnemyHeight(void)
Definition: AI.cpp:1687
void ReadAngles(idAngles &angles)
Definition: SaveGame.cpp:1073
void RestorePhysics(idPhysics *phys)
Definition: Entity.cpp:2596
ID_INLINE int FRAME2MS(int framenum)
Definition: Anim.h:48
void PlayCinematic(void)
Definition: AI.cpp:3529
const char * GetJointName(jointHandle_t handle) const
void BecomeActive(int flags)
Definition: Entity.cpp:995
idRenderSystemLocal tr
bool ignore_obstacles
Definition: AI.h:300
jointHandle_t flyTiltJoint
Definition: AI.h:318
void InitMuzzleFlash(void)
Definition: AI.cpp:1013
const int ANIMCHANNEL_TORSO
Definition: Anim.h:42
const idEventDef EV_Remove("<immediateremove>", NULL)
int anim
Definition: AI.h:209
idScriptBool AI_ENEMY_DEAD
Definition: AI.h:429
int worldMuzzleFlashHandle
Definition: AI.h:394
const int * GetPVSAreas(void)
Definition: Entity.cpp:1375
virtual void Present(void)
Definition: AFEntity.cpp:1065
#define MASK_SHOT_BOUNDINGBOX
Definition: Game_local.h:742
bool GetJointTransformForAnim(jointHandle_t jointHandle, int animNum, int currentTime, idVec3 &offset, idMat3 &axis) const
Definition: Entity.cpp:5262
bool lastHitCheckResult
Definition: AI.h:338
idEntityPtr< idProjectile > projectile
Definition: AI.h:351
idVec3 EyeOffset(void) const
Definition: Actor.cpp:1445
float range
Definition: AI.h:202
void CheckBlink(void)
Definition: Actor.cpp:1197
monsterMoveResult_t GetMoveResult(void) const
bool CheckFOV(const idVec3 &pos) const
Definition: Actor.cpp:1473
const idMat3 & GetGravityAxis(void) const
idVec4 colorMdGrey
Definition: Lib.cpp:128
const int ANIMCHANNEL_LEGS
Definition: Anim.h:43
bool FaceEnemy(void)
Definition: AI.cpp:1602
void FlyMove(void)
Definition: AI.cpp:3129
GLint j
Definition: qgl.h:264
idBounds & ExpandSelf(const float d)
Definition: Bounds.h:322
bool AttackMelee(const char *meleeDefName)
Definition: AI.cpp:4470
virtual void PushPointIntoAreaNum(int areaNum, idVec3 &origin) const =0
virtual void DebugBounds(const idVec4 &color, const idBounds &bounds, const idVec3 &org=vec3_origin, const int lifetime=0)=0
idList< idVec3 > missileLaunchOffset
Definition: AI.h:343
Definition: AI.h:64
idAASFindCover(const idVec3 &hideFromPos)
Definition: AI.cpp:136
const char * GetEntityDefName(void) const
Definition: Entity.cpp:842
float blockedRadius
Definition: AI.h:301
const idDeclEntityDef * FindEntityDef(const char *name, bool makeDefault=true) const
idRenderWorld * gameRenderWorld
Definition: Game_local.cpp:55
idEntity * GetSlideMoveEntity(void) const
idVec3 moveDest
Definition: AI.h:194
char * va(const char *fmt,...)
Definition: Str.cpp:1568
void StopMove(moveStatus_t status)
Definition: AI.cpp:1574
bool Translation(trace_t &results, const idVec3 &start, const idVec3 &end, const idClipModel *mdl, const idMat3 &trmAxis, int contentMask, const idEntity *passEntity)
Definition: Clip.cpp:1056
static idEntityFx * StartFx(const char *fx, const idVec3 *useOrigin, const idMat3 *useAxis, idEntity *ent, bool bind)
Definition: Fx.cpp:716
const idVec3 & GetLinearVelocity(int id=0) const
bool PostEventMS(const idEventDef *ev, int time)
Definition: Class.cpp:666
moveType_t moveType
Definition: AI.h:191
int entityNumber
Definition: Entity.h:111
idScriptBool AI_PUSHED
Definition: AI.h:440
GLfloat GLfloat p
Definition: glext.h:4674
virtual void SetModel(const char *modelname)
Definition: Entity.cpp:5222
const idMaterial * shader
Definition: RenderWorld.h:200
idStr name
Definition: Entity.h:121
float fly_roll_scale
Definition: AI.h:325
moveCommand_t
Definition: AI.h:63
jointHandle_t focusJoint
Definition: AI.h:402
int thinkFlags
Definition: Entity.h:125
void Unlink(void)
Definition: Clip.cpp:491
talkState_t
Definition: AI.h:84
idList< particleEmitter_t > particles
Definition: AI.h:391
void AdjustFlyingAngles(void)
Definition: AI.cpp:2962
bool IsActive(void) const
Definition: AF.h:74
void WriteParticle(const idDeclParticle *particle)
Definition: SaveGame.cpp:406
GLdouble GLdouble z
Definition: glext.h:3067
idVec4 colorMagenta
Definition: Lib.cpp:121
int chat_min
Definition: AI.h:356
static void DrawDebugInfo(void)
Definition: AI.cpp:5250
float fly_bob_strength
Definition: AI.h:320
bool ContainsPoint(const idVec3 &p) const
Definition: Bounds.h:353
void Zero(void)
Definition: Vector.h:415
void Think(void)
Definition: AI.cpp:1122
talkState_t GetTalkState(void) const
Definition: AI.cpp:3706
float shaderParms[MAX_ENTITY_SHADER_PARMS]
Definition: RenderWorld.h:127
void ReadString(idStr &string)
Definition: SaveGame.cpp:985
idCVar ai_blockedFailSafe("ai_blockedFailSafe","1", CVAR_GAME|CVAR_BOOL,"enable blocked fail safe handling")
void UpdateDamageEffects(void)
Definition: Entity.cpp:5438
idIK_Walk walkIK
Definition: Actor.h:245
const idBounds & GetBounds(int id=-1) const
idEntityPtr< idEntity > goalEntity
Definition: AI.h:196
void BecomeInactive(int flags)
Definition: Entity.cpp:1025
idScriptBool AI_FORWARD
Definition: AI.h:433
void Spawn(void)
Definition: AI.cpp:763
int travelFlags
Definition: AI.h:294
float turnRate
Definition: AI.h:308
void TriggerParticles(const char *jointName)
Definition: AI.cpp:4830
void ReadInt(int &value)
Definition: SaveGame.cpp:922
float CRandomFloat(void)
Definition: Random.h:86
idMoveState()
Definition: AI.cpp:55
jointHandle_t rightEyeJoint
Definition: Actor.h:242
void Turn(void)
Definition: AI.cpp:2447
void ActivateTargets(idEntity *activator) const
Definition: Entity.cpp:3650
GLuint start
Definition: glext.h:2845
float projectileSpeed
Definition: AI.h:348
void StopSound(const s_channelType channel, bool broadcast)
Definition: Entity.cpp:1713
virtual void Gib(const idVec3 &dir, const char *damageDefName)
Definition: Actor.cpp:2167
Definition: AI.h:253
float LengthSqr(void) const
Definition: Vector.h:166
void UpdateScript(void)
Definition: Actor.cpp:1375
Definition: Anim.h:280
void SetState(const function_t *newState)
Definition: Actor.cpp:1344
bool StartRagdoll(void)
Definition: Actor.cpp:1651
idVec4 colorBlue
Definition: Lib.cpp:119
idVec3 offset
Definition: AI.h:729
void ClearJoint(jointHandle_t jointnum)
bool StripLeadingOnce(const char *string)
Definition: Str.cpp:498
#define MS2SEC(t)
Definition: Math.h:60
int alignHeadTime
Definition: AI.h:370
void ReadObject(idClass *&obj)
Definition: SaveGame.cpp:1083
idMoveState savedMove
Definition: AI.h:297
Definition: AAS.h:75
void RemoveProjectile(void)
Definition: AI.cpp:4196
void EnableClip(void)
virtual void DormantEnd(void)
Definition: Entity.cpp:978
virtual void SetAxis(const idMat3 &newAxis, int id=-1)=0
GLdouble GLdouble t
Definition: glext.h:2943
void PushWithAF(void)
Definition: AI.cpp:4549
idAngles destLookAng
Definition: AI.h:374
static float Cos(float a)
Definition: Math.h:346
bool allowMove
Definition: AI.h:332
int blockedAttackTime
Definition: AI.h:303
virtual void DormantBegin(void)
Definition: AI.cpp:1079
float shrivel_rate
Definition: AI.h:386