codemp/game/NPC_AI_Howler.c

Go to the documentation of this file.
00001 #include "b_local.h"
00002 
00003 // These define the working combat range for these suckers
00004 #define MIN_DISTANCE            54
00005 #define MIN_DISTANCE_SQR        ( MIN_DISTANCE * MIN_DISTANCE )
00006 
00007 #define MAX_DISTANCE            128
00008 #define MAX_DISTANCE_SQR        ( MAX_DISTANCE * MAX_DISTANCE )
00009 
00010 #define LSTATE_CLEAR            0
00011 #define LSTATE_WAITING          1
00012 
00013 /*
00014 -------------------------
00015 NPC_Howler_Precache
00016 -------------------------
00017 */
00018 void NPC_Howler_Precache( void )
00019 {
00020 }
00021 
00022 
00023 /*
00024 -------------------------
00025 Howler_Idle
00026 -------------------------
00027 */
00028 void Howler_Idle( void )
00029 {
00030 }
00031 
00032 
00033 /*
00034 -------------------------
00035 Howler_Patrol
00036 -------------------------
00037 */
00038 void Howler_Patrol( void )
00039 {
00040         vec3_t dif;
00041 
00042         NPCInfo->localState = LSTATE_CLEAR;
00043 
00044         //If we have somewhere to go, then do that
00045         if ( UpdateGoal() )
00046         {
00047                 ucmd.buttons &= ~BUTTON_WALKING;
00048                 NPC_MoveToGoal( qtrue );
00049         }
00050         else
00051         {
00052                 if ( TIMER_Done( NPC, "patrolTime" ))
00053                 {
00054                         TIMER_Set( NPC, "patrolTime", crandom() * 5000 + 5000 );
00055                 }
00056         }
00057 
00058         //rwwFIXMEFIXME: Care about all clients, not just client 0
00059         VectorSubtract( g_entities[0].r.currentOrigin, NPC->r.currentOrigin, dif );
00060 
00061         if ( VectorLengthSquared( dif ) < 256 * 256 )
00062         {
00063                 G_SetEnemy( NPC, &g_entities[0] );
00064         }
00065 
00066         if ( NPC_CheckEnemyExt( qtrue ) == qfalse )
00067         {
00068                 Howler_Idle();
00069                 return;
00070         }
00071 }
00072  
00073 /*
00074 -------------------------
00075 Howler_Move
00076 -------------------------
00077 */
00078 void Howler_Move( qboolean visible )
00079 {
00080         if ( NPCInfo->localState != LSTATE_WAITING )
00081         {
00082                 NPCInfo->goalEntity = NPC->enemy;
00083                 NPC_MoveToGoal( qtrue );
00084                 NPCInfo->goalRadius = MAX_DISTANCE;     // just get us within combat range
00085         }
00086 }
00087 
00088 //---------------------------------------------------------
00089 void Howler_TryDamage( gentity_t *enemy, int damage )
00090 {
00091         vec3_t  end, dir;
00092         trace_t tr;
00093 
00094         if ( !enemy )
00095         {
00096                 return;
00097         }
00098 
00099         AngleVectors( NPC->client->ps.viewangles, dir, NULL, NULL );
00100         VectorMA( NPC->r.currentOrigin, MIN_DISTANCE, dir, end );
00101 
00102         // Should probably trace from the mouth, but, ah well.
00103         trap_Trace( &tr, NPC->r.currentOrigin, vec3_origin, vec3_origin, end, NPC->s.number, MASK_SHOT );
00104 
00105         if ( tr.entityNum != ENTITYNUM_WORLD )
00106         {
00107                 G_Damage( &g_entities[tr.entityNum], NPC, NPC, dir, tr.endpos, damage, DAMAGE_NO_KNOCKBACK, MOD_MELEE );
00108         }
00109 }
00110 
00111 //------------------------------
00112 void Howler_Attack( void )
00113 {
00114         if ( !TIMER_Exists( NPC, "attacking" ))
00115         {
00116                 // Going to do ATTACK1
00117                 TIMER_Set( NPC, "attacking", 1700 + random() * 200 );
00118                 NPC_SetAnim( NPC, SETANIM_BOTH, BOTH_ATTACK1, SETANIM_FLAG_OVERRIDE | SETANIM_FLAG_HOLD );
00119 
00120                 TIMER_Set( NPC, "attack_dmg", 200 ); // level two damage
00121         }
00122 
00123         // Need to do delayed damage since the attack animations encapsulate multiple mini-attacks
00124         if ( TIMER_Done2( NPC, "attack_dmg", qtrue ))
00125         {
00126                 Howler_TryDamage( NPC->enemy, 5 );
00127         }
00128 
00129         // Just using this to remove the attacking flag at the right time
00130         TIMER_Done2( NPC, "attacking", qtrue );
00131 }
00132 
00133 //----------------------------------
00134 void Howler_Combat( void )
00135 {
00136         float distance;
00137         qboolean advance;
00138 
00139         // If we cannot see our target or we have somewhere to go, then do that
00140         if ( !NPC_ClearLOS4( NPC->enemy ) || UpdateGoal( ))
00141         {
00142                 NPCInfo->combatMove = qtrue;
00143                 NPCInfo->goalEntity = NPC->enemy;
00144                 NPCInfo->goalRadius = MAX_DISTANCE;     // just get us within combat range
00145 
00146                 NPC_MoveToGoal( qtrue );
00147                 return;
00148         }
00149 
00150         // Sometimes I have problems with facing the enemy I'm attacking, so force the issue so I don't look dumb
00151         NPC_FaceEnemy( qtrue );
00152 
00153         distance        = DistanceHorizontalSquared( NPC->r.currentOrigin, NPC->enemy->r.currentOrigin );       
00154         advance = (qboolean)( distance > MIN_DISTANCE_SQR ? qtrue : qfalse  );
00155 
00156         if (( advance || NPCInfo->localState == LSTATE_WAITING ) && TIMER_Done( NPC, "attacking" )) // waiting monsters can't attack
00157         {
00158                 if ( TIMER_Done2( NPC, "takingPain", qtrue ))
00159                 {
00160                         NPCInfo->localState = LSTATE_CLEAR;
00161                 }
00162                 else
00163                 {
00164                         Howler_Move( 1 );
00165                 }
00166         }
00167         else
00168         {
00169                 Howler_Attack();
00170         }
00171 }
00172 
00173 /*
00174 -------------------------
00175 NPC_Howler_Pain
00176 -------------------------
00177 */
00178 void NPC_Howler_Pain( gentity_t *self, gentity_t *attacker, int damage ) 
00179 {
00180         if ( damage >= 10 )
00181         {
00182                 TIMER_Remove( self, "attacking" );
00183                 TIMER_Set( self, "takingPain", 2900 );
00184 
00185                 VectorCopy( self->NPC->lastPathAngles, self->s.angles );
00186 
00187                 NPC_SetAnim( self, SETANIM_BOTH, BOTH_PAIN1, SETANIM_FLAG_OVERRIDE | SETANIM_FLAG_HOLD );
00188 
00189                 if ( self->NPC )
00190                 {
00191                         self->NPC->localState = LSTATE_WAITING;
00192                 }
00193         }
00194 }
00195 
00196 
00197 /*
00198 -------------------------
00199 NPC_BSHowler_Default
00200 -------------------------
00201 */
00202 void NPC_BSHowler_Default( void )
00203 {
00204         if ( NPC->enemy )
00205         {
00206                 Howler_Combat();
00207         }
00208         else if ( NPCInfo->scriptFlags & SCF_LOOK_FOR_ENEMIES )
00209         {
00210                 Howler_Patrol();
00211         }
00212         else
00213         {
00214                 Howler_Idle();
00215         }
00216 
00217         NPC_UpdateAngles( qtrue, qtrue );
00218 }