codemp/game/NPC_AI_Remote.c

Go to the documentation of this file.
00001 #include "b_local.h"
00002 #include "g_nav.h"
00003 
00004 void Remote_Strafe( void );
00005 
00006 #define VELOCITY_DECAY  0.85f
00007 
00008 
00009 //Local state enums
00010 enum
00011 {
00012         LSTATE_NONE = 0,
00013 };
00014 
00015 void Remote_Idle( void );
00016 
00017 void NPC_Remote_Precache(void)
00018 {
00019         G_SoundIndex("sound/chars/remote/misc/fire.wav");
00020         G_SoundIndex( "sound/chars/remote/misc/hiss.wav");
00021         G_EffectIndex( "env/small_explode");
00022 }
00023 
00024 /*
00025 -------------------------
00026 NPC_Remote_Pain
00027 -------------------------
00028 */
00029 void NPC_Remote_Pain(gentity_t *self, gentity_t *attacker, int damage)
00030 {
00031         SaveNPCGlobals();
00032         SetNPCGlobals( self );
00033         Remote_Strafe();
00034         RestoreNPCGlobals();
00035 
00036         NPC_Pain( self, attacker, damage );
00037 }
00038 
00039 /*
00040 -------------------------
00041 Remote_MaintainHeight
00042 -------------------------
00043 */
00044 void Remote_MaintainHeight( void )
00045 {       
00046         float   dif;
00047 
00048         // Update our angles regardless
00049         NPC_UpdateAngles( qtrue, qtrue );
00050 
00051         if ( NPC->client->ps.velocity[2] )
00052         {
00053                 NPC->client->ps.velocity[2] *= VELOCITY_DECAY;
00054 
00055                 if ( fabs( NPC->client->ps.velocity[2] ) < 2 )
00056                 {
00057                         NPC->client->ps.velocity[2] = 0;
00058                 }
00059         }
00060         // If we have an enemy, we should try to hover at or a little below enemy eye level
00061         if ( NPC->enemy )
00062         {
00063                 if (TIMER_Done( NPC, "heightChange"))
00064                 {
00065                         TIMER_Set( NPC,"heightChange",Q_irand( 1000, 3000 ));
00066 
00067                         // Find the height difference
00068                         dif = (NPC->enemy->r.currentOrigin[2] +  Q_irand( 0, NPC->enemy->r.maxs[2]+8 )) - NPC->r.currentOrigin[2]; 
00069 
00070                         // cap to prevent dramatic height shifts
00071                         if ( fabs( dif ) > 2 )
00072                         {
00073                                 if ( fabs( dif ) > 24 )
00074                                 {
00075                                         dif = ( dif < 0 ? -24 : 24 );
00076                                 }
00077                                 dif *= 10;
00078                                 NPC->client->ps.velocity[2] = (NPC->client->ps.velocity[2]+dif)/2;
00079                         //      NPC->fx_time = level.time;
00080                                 G_Sound( NPC, CHAN_AUTO, G_SoundIndex("sound/chars/remote/misc/hiss.wav"));
00081                         }
00082                 }
00083         }
00084         else
00085         {
00086                 gentity_t *goal = NULL;
00087 
00088                 if ( NPCInfo->goalEntity )      // Is there a goal?
00089                 {
00090                         goal = NPCInfo->goalEntity;
00091                 }
00092                 else
00093                 {
00094                         goal = NPCInfo->lastGoalEntity;
00095                 }
00096                 if ( goal )
00097                 {
00098                         dif = goal->r.currentOrigin[2] - NPC->r.currentOrigin[2];
00099 
00100                         if ( fabs( dif ) > 24 )
00101                         {
00102                                 dif = ( dif < 0 ? -24 : 24 );
00103                                 NPC->client->ps.velocity[2] = (NPC->client->ps.velocity[2]+dif)/2;
00104                         }
00105                 }
00106         }
00107 
00108         // Apply friction
00109         if ( NPC->client->ps.velocity[0] )
00110         {
00111                 NPC->client->ps.velocity[0] *= VELOCITY_DECAY;
00112 
00113                 if ( fabs( NPC->client->ps.velocity[0] ) < 1 )
00114                 {
00115                         NPC->client->ps.velocity[0] = 0;
00116                 }
00117         }
00118 
00119         if ( NPC->client->ps.velocity[1] )
00120         {
00121                 NPC->client->ps.velocity[1] *= VELOCITY_DECAY;
00122 
00123                 if ( fabs( NPC->client->ps.velocity[1] ) < 1 )
00124                 {
00125                         NPC->client->ps.velocity[1] = 0;
00126                 }
00127         }
00128 }
00129 
00130 #define REMOTE_STRAFE_VEL       256
00131 #define REMOTE_STRAFE_DIS       200
00132 #define REMOTE_UPWARD_PUSH      32
00133 
00134 /*
00135 -------------------------
00136 Remote_Strafe
00137 -------------------------
00138 */
00139 void Remote_Strafe( void )
00140 {
00141         int             dir;
00142         vec3_t  end, right;
00143         trace_t tr;
00144 
00145         AngleVectors( NPC->client->renderInfo.eyeAngles, NULL, right, NULL );
00146 
00147         // Pick a random strafe direction, then check to see if doing a strafe would be
00148         //      reasonable valid
00149         dir = ( rand() & 1 ) ? -1 : 1;
00150         VectorMA( NPC->r.currentOrigin, REMOTE_STRAFE_DIS * dir, right, end );
00151 
00152         trap_Trace( &tr, NPC->r.currentOrigin, NULL, NULL, end, NPC->s.number, MASK_SOLID );
00153 
00154         // Close enough
00155         if ( tr.fraction > 0.9f )
00156         {
00157                 VectorMA( NPC->client->ps.velocity, REMOTE_STRAFE_VEL * dir, right, NPC->client->ps.velocity );
00158 
00159                 G_Sound( NPC, CHAN_AUTO, G_SoundIndex("sound/chars/remote/misc/hiss.wav"));
00160 
00161                 // Add a slight upward push
00162                 NPC->client->ps.velocity[2] += REMOTE_UPWARD_PUSH;
00163 
00164                 // Set the strafe start time so we can do a controlled roll
00165         //      NPC->fx_time = level.time;
00166                 NPCInfo->standTime = level.time + 3000 + random() * 500;
00167         }
00168 }
00169 
00170 #define REMOTE_FORWARD_BASE_SPEED       10
00171 #define REMOTE_FORWARD_MULTIPLIER       5
00172 
00173 /*
00174 -------------------------
00175 Remote_Hunt
00176 -------------------------
00177 */
00178 void Remote_Hunt( qboolean visible, qboolean advance, qboolean retreat )
00179 {
00180         float   distance, speed;
00181         vec3_t  forward;
00182 
00183         //If we're not supposed to stand still, pursue the player
00184         if ( NPCInfo->standTime < level.time )
00185         {
00186                 // Only strafe when we can see the player
00187                 if ( visible )
00188                 {
00189                         Remote_Strafe();
00190                         return;
00191                 }
00192         }
00193 
00194         //If we don't want to advance, stop here
00195         if ( advance == qfalse && visible == qtrue )
00196                 return;
00197 
00198         //Only try and navigate if the player is visible
00199         if ( visible == qfalse )
00200         {
00201                 // Move towards our goal
00202                 NPCInfo->goalEntity = NPC->enemy;
00203                 NPCInfo->goalRadius = 12;
00204 
00205                 //Get our direction from the navigator if we can't see our target
00206                 if ( NPC_GetMoveDirection( forward, &distance ) == qfalse )
00207                         return;
00208         }
00209         else
00210         {
00211                 VectorSubtract( NPC->enemy->r.currentOrigin, NPC->r.currentOrigin, forward );
00212                 distance = VectorNormalize( forward );
00213         }
00214 
00215         speed = REMOTE_FORWARD_BASE_SPEED + REMOTE_FORWARD_MULTIPLIER * g_spskill.integer;
00216         if ( retreat == qtrue )
00217         {
00218                 speed *= -1;
00219         }
00220         VectorMA( NPC->client->ps.velocity, speed, forward, NPC->client->ps.velocity );
00221 }
00222 
00223 
00224 /*
00225 -------------------------
00226 Remote_Fire
00227 -------------------------
00228 */
00229 void Remote_Fire (void)
00230 {
00231         vec3_t  delta1, enemy_org1, muzzle1;
00232         vec3_t  angleToEnemy1;
00233         static  vec3_t  forward, vright, up;
00234         static  vec3_t  muzzle;
00235         gentity_t       *missile;
00236 
00237         CalcEntitySpot( NPC->enemy, SPOT_HEAD, enemy_org1 );
00238         VectorCopy( NPC->r.currentOrigin, muzzle1 );
00239         
00240         VectorSubtract (enemy_org1, muzzle1, delta1);
00241 
00242         vectoangles ( delta1, angleToEnemy1 );
00243         AngleVectors (angleToEnemy1, forward, vright, up);
00244 
00245         missile = CreateMissile( NPC->r.currentOrigin, forward, 1000, 10000, NPC, qfalse );
00246 
00247         G_PlayEffectID( G_EffectIndex("bryar/muzzle_flash"), NPC->r.currentOrigin, forward );
00248 
00249         missile->classname = "briar";
00250         missile->s.weapon = WP_BRYAR_PISTOL;
00251 
00252         missile->damage = 10;
00253         missile->dflags = DAMAGE_DEATH_KNOCKBACK;
00254         missile->methodOfDeath = MOD_BRYAR_PISTOL;
00255         missile->clipmask = MASK_SHOT | CONTENTS_LIGHTSABER;
00256 
00257 }
00258 
00259 /*
00260 -------------------------
00261 Remote_Ranged
00262 -------------------------
00263 */
00264 void Remote_Ranged( qboolean visible, qboolean advance, qboolean retreat )
00265 {
00266 
00267         if ( TIMER_Done( NPC, "attackDelay" ) ) // Attack?
00268         {
00269                 TIMER_Set( NPC, "attackDelay", Q_irand( 500, 3000 ) );
00270                 Remote_Fire();
00271         }
00272 
00273         if ( NPCInfo->scriptFlags & SCF_CHASE_ENEMIES )
00274         {
00275                 Remote_Hunt( visible, advance, retreat );
00276         }
00277 }
00278 
00279 #define MIN_MELEE_RANGE         320
00280 #define MIN_MELEE_RANGE_SQR     ( MIN_MELEE_RANGE * MIN_MELEE_RANGE )
00281 
00282 #define MIN_DISTANCE            80
00283 #define MIN_DISTANCE_SQR        ( MIN_DISTANCE * MIN_DISTANCE )
00284 
00285 /*
00286 -------------------------
00287 Remote_Attack
00288 -------------------------
00289 */
00290 void Remote_Attack( void )
00291 {
00292         float           distance;
00293         qboolean        visible;
00294         float           idealDist;
00295         qboolean        advance;
00296         qboolean        retreat;
00297 
00298         if ( TIMER_Done(NPC,"spin") )
00299         {
00300                 TIMER_Set( NPC, "spin", Q_irand( 250, 1500 ) );
00301                 NPCInfo->desiredYaw += Q_irand( -200, 200 ); 
00302         }
00303         // Always keep a good height off the ground
00304         Remote_MaintainHeight();
00305 
00306         // If we don't have an enemy, just idle
00307         if ( NPC_CheckEnemyExt(qfalse) == qfalse )
00308         {
00309                 Remote_Idle();
00310                 return;
00311         }
00312 
00313         // Rate our distance to the target, and our visibilty
00314         distance        = (int) DistanceHorizontalSquared( NPC->r.currentOrigin, NPC->enemy->r.currentOrigin ); 
00315         visible         = NPC_ClearLOS4( NPC->enemy );
00316         idealDist       = MIN_DISTANCE_SQR+(MIN_DISTANCE_SQR*flrand( 0, 1 ));
00317         advance         = (qboolean)(distance > idealDist*1.25);
00318         retreat         = (qboolean)(distance < idealDist*0.75);
00319 
00320         // If we cannot see our target, move to see it
00321         if ( visible == qfalse )
00322         {
00323                 if ( NPCInfo->scriptFlags & SCF_CHASE_ENEMIES )
00324                 {
00325                         Remote_Hunt( visible, advance, retreat );
00326                         return;
00327                 }
00328         }
00329 
00330         Remote_Ranged( visible, advance, retreat );
00331 
00332 }
00333 
00334 /*
00335 -------------------------
00336 Remote_Idle
00337 -------------------------
00338 */
00339 void Remote_Idle( void )
00340 {
00341         Remote_MaintainHeight();
00342 
00343         NPC_BSIdle();
00344 }
00345 
00346 /*
00347 -------------------------
00348 Remote_Patrol
00349 -------------------------
00350 */
00351 void Remote_Patrol( void )
00352 {
00353         Remote_MaintainHeight();
00354 
00355         //If we have somewhere to go, then do that
00356         if (!NPC->enemy)
00357         {
00358                 if ( UpdateGoal() )
00359                 {
00360                         //start loop sound once we move
00361                         ucmd.buttons |= BUTTON_WALKING;
00362                         NPC_MoveToGoal( qtrue );
00363                 }
00364         }
00365 
00366         NPC_UpdateAngles( qtrue, qtrue );
00367 }
00368 
00369 
00370 /*
00371 -------------------------
00372 NPC_BSRemote_Default
00373 -------------------------
00374 */
00375 void NPC_BSRemote_Default( void )
00376 {
00377         if ( NPC->enemy )
00378         {
00379                 Remote_Attack();
00380         }
00381         else if ( NPCInfo->scriptFlags & SCF_LOOK_FOR_ENEMIES )
00382         {
00383                 Remote_Patrol();
00384         }
00385         else
00386         {
00387                 Remote_Idle();
00388         }
00389 }