AI_MineMonster.cpp 6.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270
  1. // leave this line at the top of all AI_xxxx.cpp files for PCH reasons...
  2. #include "g_headers.h"
  3. #include "b_local.h"
  4. // These define the working combat range for these suckers
  5. #define MIN_DISTANCE 54
  6. #define MIN_DISTANCE_SQR ( MIN_DISTANCE * MIN_DISTANCE )
  7. #define MAX_DISTANCE 128
  8. #define MAX_DISTANCE_SQR ( MAX_DISTANCE * MAX_DISTANCE )
  9. #define LSTATE_CLEAR 0
  10. #define LSTATE_WAITING 1
  11. /*
  12. -------------------------
  13. NPC_MineMonster_Precache
  14. -------------------------
  15. */
  16. void NPC_MineMonster_Precache( void )
  17. {
  18. for ( int i = 0; i < 4; i++ )
  19. {
  20. G_SoundIndex( va("sound/chars/mine/misc/bite%i.wav", i+1 ));
  21. G_SoundIndex( va("sound/chars/mine/misc/miss%i.wav", i+1 ));
  22. }
  23. }
  24. /*
  25. -------------------------
  26. MineMonster_Idle
  27. -------------------------
  28. */
  29. void MineMonster_Idle( void )
  30. {
  31. if ( UpdateGoal() )
  32. {
  33. ucmd.buttons &= ~BUTTON_WALKING;
  34. NPC_MoveToGoal( qtrue );
  35. }
  36. }
  37. /*
  38. -------------------------
  39. MineMonster_Patrol
  40. -------------------------
  41. */
  42. void MineMonster_Patrol( void )
  43. {
  44. NPCInfo->localState = LSTATE_CLEAR;
  45. //If we have somewhere to go, then do that
  46. if ( UpdateGoal() )
  47. {
  48. ucmd.buttons &= ~BUTTON_WALKING;
  49. NPC_MoveToGoal( qtrue );
  50. }
  51. vec3_t dif;
  52. VectorSubtract( g_entities[0].currentOrigin, NPC->currentOrigin, dif );
  53. if ( VectorLengthSquared( dif ) < 256 * 256 )
  54. {
  55. G_SetEnemy( NPC, &g_entities[0] );
  56. }
  57. if ( NPC_CheckEnemyExt( qtrue ) == qfalse )
  58. {
  59. MineMonster_Idle();
  60. return;
  61. }
  62. }
  63. /*
  64. -------------------------
  65. MineMonster_Move
  66. -------------------------
  67. */
  68. void MineMonster_Move( qboolean visible )
  69. {
  70. if ( NPCInfo->localState != LSTATE_WAITING )
  71. {
  72. NPCInfo->goalEntity = NPC->enemy;
  73. NPC_MoveToGoal( qtrue );
  74. NPCInfo->goalRadius = MAX_DISTANCE; // just get us within combat range
  75. }
  76. }
  77. //---------------------------------------------------------
  78. void MineMonster_TryDamage( gentity_t *enemy, int damage )
  79. {
  80. vec3_t end, dir;
  81. trace_t tr;
  82. if ( !enemy )
  83. {
  84. return;
  85. }
  86. AngleVectors( NPC->client->ps.viewangles, dir, NULL, NULL );
  87. VectorMA( NPC->currentOrigin, MIN_DISTANCE, dir, end );
  88. // Should probably trace from the mouth, but, ah well.
  89. gi.trace( &tr, NPC->currentOrigin, vec3_origin, vec3_origin, end, NPC->s.number, MASK_SHOT );
  90. if ( tr.entityNum >= 0 && tr.entityNum < ENTITYNUM_NONE )
  91. {
  92. G_Damage( &g_entities[tr.entityNum], NPC, NPC, dir, tr.endpos, damage, DAMAGE_NO_KNOCKBACK, MOD_MELEE );
  93. G_SoundOnEnt( NPC, CHAN_VOICE_ATTEN, va("sound/chars/mine/misc/bite%i.wav", Q_irand(1,4)));
  94. }
  95. else
  96. {
  97. G_SoundOnEnt( NPC, CHAN_VOICE_ATTEN, va("sound/chars/mine/misc/miss%i.wav", Q_irand(1,4)));
  98. }
  99. }
  100. //------------------------------
  101. void MineMonster_Attack( void )
  102. {
  103. if ( !TIMER_Exists( NPC, "attacking" ))
  104. {
  105. // usually try and play a jump attack if the player somehow got above them....or just really rarely
  106. if ( NPC->enemy && ((NPC->enemy->currentOrigin[2] - NPC->currentOrigin[2] > 10 && random() > 0.1f )
  107. || random() > 0.8f ))
  108. {
  109. // Going to do ATTACK4
  110. TIMER_Set( NPC, "attacking", 1750 + random() * 200 );
  111. NPC_SetAnim( NPC, SETANIM_BOTH, BOTH_ATTACK4, SETANIM_FLAG_OVERRIDE | SETANIM_FLAG_HOLD );
  112. TIMER_Set( NPC, "attack2_dmg", 950 ); // level two damage
  113. }
  114. else if ( random() > 0.5f )
  115. {
  116. if ( random() > 0.8f )
  117. {
  118. // Going to do ATTACK3, (rare)
  119. TIMER_Set( NPC, "attacking", 850 );
  120. NPC_SetAnim( NPC, SETANIM_BOTH, BOTH_ATTACK3, SETANIM_FLAG_OVERRIDE | SETANIM_FLAG_HOLD );
  121. TIMER_Set( NPC, "attack2_dmg", 400 ); // level two damage
  122. }
  123. else
  124. {
  125. // Going to do ATTACK1
  126. TIMER_Set( NPC, "attacking", 850 );
  127. NPC_SetAnim( NPC, SETANIM_BOTH, BOTH_ATTACK1, SETANIM_FLAG_OVERRIDE | SETANIM_FLAG_HOLD );
  128. TIMER_Set( NPC, "attack1_dmg", 450 ); // level one damage
  129. }
  130. }
  131. else
  132. {
  133. // Going to do ATTACK2
  134. TIMER_Set( NPC, "attacking", 1250 );
  135. NPC_SetAnim( NPC, SETANIM_BOTH, BOTH_ATTACK2, SETANIM_FLAG_OVERRIDE | SETANIM_FLAG_HOLD );
  136. TIMER_Set( NPC, "attack1_dmg", 700 ); // level one damage
  137. }
  138. }
  139. else
  140. {
  141. // Need to do delayed damage since the attack animations encapsulate multiple mini-attacks
  142. if ( TIMER_Done2( NPC, "attack1_dmg", qtrue ))
  143. {
  144. MineMonster_TryDamage( NPC->enemy, 5 );
  145. }
  146. else if ( TIMER_Done2( NPC, "attack2_dmg", qtrue ))
  147. {
  148. MineMonster_TryDamage( NPC->enemy, 10 );
  149. }
  150. }
  151. // Just using this to remove the attacking flag at the right time
  152. TIMER_Done2( NPC, "attacking", qtrue );
  153. }
  154. //----------------------------------
  155. void MineMonster_Combat( void )
  156. {
  157. // If we cannot see our target or we have somewhere to go, then do that
  158. if ( !NPC_ClearLOS( NPC->enemy ) || UpdateGoal( ))
  159. {
  160. NPCInfo->combatMove = qtrue;
  161. NPCInfo->goalEntity = NPC->enemy;
  162. NPCInfo->goalRadius = MAX_DISTANCE; // just get us within combat range
  163. NPC_MoveToGoal( qtrue );
  164. return;
  165. }
  166. // Sometimes I have problems with facing the enemy I'm attacking, so force the issue so I don't look dumb
  167. NPC_FaceEnemy( qtrue );
  168. float distance = DistanceHorizontalSquared( NPC->currentOrigin, NPC->enemy->currentOrigin );
  169. qboolean advance = (qboolean)( distance > MIN_DISTANCE_SQR ? qtrue : qfalse );
  170. if (( advance || NPCInfo->localState == LSTATE_WAITING ) && TIMER_Done( NPC, "attacking" )) // waiting monsters can't attack
  171. {
  172. if ( TIMER_Done2( NPC, "takingPain", qtrue ))
  173. {
  174. NPCInfo->localState = LSTATE_CLEAR;
  175. }
  176. else
  177. {
  178. MineMonster_Move( 1 );
  179. }
  180. }
  181. else
  182. {
  183. MineMonster_Attack();
  184. }
  185. }
  186. /*
  187. -------------------------
  188. NPC_MineMonster_Pain
  189. -------------------------
  190. */
  191. void NPC_MineMonster_Pain( gentity_t *self, gentity_t *inflictor, gentity_t *other, const vec3_t point, int damage, int mod,int hitLoc )
  192. {
  193. G_AddEvent( self, EV_PAIN, floor((float)self->health/self->max_health*100.0f) );
  194. if ( damage >= 10 )
  195. {
  196. TIMER_Remove( self, "attacking" );
  197. TIMER_Remove( self, "attacking1_dmg" );
  198. TIMER_Remove( self, "attacking2_dmg" );
  199. TIMER_Set( self, "takingPain", 1350 );
  200. VectorCopy( self->NPC->lastPathAngles, self->s.angles );
  201. NPC_SetAnim( self, SETANIM_BOTH, BOTH_PAIN1, SETANIM_FLAG_OVERRIDE | SETANIM_FLAG_HOLD );
  202. if ( self->NPC )
  203. {
  204. self->NPC->localState = LSTATE_WAITING;
  205. }
  206. }
  207. }
  208. /*
  209. -------------------------
  210. NPC_BSMineMonster_Default
  211. -------------------------
  212. */
  213. void NPC_BSMineMonster_Default( void )
  214. {
  215. if ( NPC->enemy )
  216. {
  217. MineMonster_Combat();
  218. }
  219. else if ( NPCInfo->scriptFlags & SCF_LOOK_FOR_ENEMIES )
  220. {
  221. MineMonster_Patrol();
  222. }
  223. else
  224. {
  225. MineMonster_Idle();
  226. }
  227. NPC_UpdateAngles( qtrue, qtrue );
  228. }