codemp/game/NPC_AI_Mark1.c

Go to the documentation of this file.
00001 #include "b_local.h"
00002 #include "g_nav.h"
00003 
00004 #define MIN_MELEE_RANGE                         320
00005 #define MIN_MELEE_RANGE_SQR                     ( MIN_MELEE_RANGE * MIN_MELEE_RANGE )
00006 
00007 #define MIN_DISTANCE                            128
00008 #define MIN_DISTANCE_SQR                        ( MIN_DISTANCE * MIN_DISTANCE )
00009 
00010 #define TURN_OFF                                        0x00000100
00011 
00012 #define LEFT_ARM_HEALTH                         40
00013 #define RIGHT_ARM_HEALTH                        40
00014 #define AMMO_POD_HEALTH                         40
00015 
00016 #define BOWCASTER_VELOCITY                      1300
00017 #define BOWCASTER_NPC_DAMAGE_EASY       12
00018 #define BOWCASTER_NPC_DAMAGE_NORMAL     24
00019 #define BOWCASTER_NPC_DAMAGE_HARD       36
00020 #define BOWCASTER_SIZE                          2
00021 #define BOWCASTER_SPLASH_DAMAGE         0
00022 #define BOWCASTER_SPLASH_RADIUS         0
00023 
00024 //Local state enums
00025 enum
00026 {
00027         LSTATE_NONE = 0,
00028         LSTATE_ASLEEP,
00029         LSTATE_WAKEUP,
00030         LSTATE_FIRED0,
00031         LSTATE_FIRED1,
00032         LSTATE_FIRED2,
00033         LSTATE_FIRED3,
00034         LSTATE_FIRED4,
00035 };
00036 
00037 qboolean NPC_CheckPlayerTeamStealth( void );
00038 void Mark1_BlasterAttack(qboolean advance);
00039 void DeathFX( gentity_t *ent );
00040 
00041 #include "../namespace_begin.h"
00042 extern gitem_t *BG_FindItemForAmmo( ammo_t ammo );
00043 #include "../namespace_end.h"
00044 
00045 /*
00046 -------------------------
00047 NPC_Mark1_Precache
00048 -------------------------
00049 */
00050 void NPC_Mark1_Precache(void)
00051 {
00052         G_SoundIndex( "sound/chars/mark1/misc/mark1_wakeup");
00053         G_SoundIndex( "sound/chars/mark1/misc/shutdown");
00054         G_SoundIndex( "sound/chars/mark1/misc/walk");
00055         G_SoundIndex( "sound/chars/mark1/misc/run");
00056         G_SoundIndex( "sound/chars/mark1/misc/death1");
00057         G_SoundIndex( "sound/chars/mark1/misc/death2");
00058         G_SoundIndex( "sound/chars/mark1/misc/anger");
00059         G_SoundIndex( "sound/chars/mark1/misc/mark1_fire");
00060         G_SoundIndex( "sound/chars/mark1/misc/mark1_pain");
00061         G_SoundIndex( "sound/chars/mark1/misc/mark1_explo");
00062 
00063 //      G_EffectIndex( "small_chunks");
00064         G_EffectIndex( "env/med_explode2");
00065         G_EffectIndex( "explosions/probeexplosion1");
00066         G_EffectIndex( "blaster/smoke_bolton");
00067         G_EffectIndex( "bryar/muzzle_flash");
00068         G_EffectIndex( "explosions/droidexplosion1" );
00069 
00070         RegisterItem( BG_FindItemForAmmo(       AMMO_METAL_BOLTS));
00071         RegisterItem( BG_FindItemForAmmo( AMMO_BLASTER ));
00072         RegisterItem( BG_FindItemForWeapon( WP_BOWCASTER ));
00073         RegisterItem( BG_FindItemForWeapon( WP_BRYAR_PISTOL ));
00074 }
00075 
00076 /*
00077 -------------------------
00078 NPC_Mark1_Part_Explode
00079 -------------------------
00080 */
00081 void NPC_Mark1_Part_Explode( gentity_t *self, int bolt )
00082 {
00083         if ( bolt >=0 )
00084         {
00085                 mdxaBone_t      boltMatrix;
00086                 vec3_t          org, dir;
00087 
00088                 trap_G2API_GetBoltMatrix( self->ghoul2, 0, 
00089                                         bolt,
00090                                         &boltMatrix, self->r.currentAngles, self->r.currentOrigin, level.time,
00091                                         NULL, self->modelScale );
00092 
00093                 BG_GiveMeVectorFromMatrix( &boltMatrix, ORIGIN, org );
00094                 BG_GiveMeVectorFromMatrix( &boltMatrix, NEGATIVE_Y, dir );
00095 
00096                 G_PlayEffectID( G_EffectIndex("env/med_explode2"), org, dir );
00097 
00098                 G_PlayEffectID( G_EffectIndex("blaster/smoke_bolton"), org, dir );
00099         }
00100 
00101         //G_PlayEffectID( G_EffectIndex("blaster/smoke_bolton"), self->playerModel, bolt, self->s.number );
00102 }
00103 
00104 /*
00105 -------------------------
00106 Mark1_Idle
00107 -------------------------
00108 */
00109 void Mark1_Idle( void )
00110 {
00111 
00112         NPC_BSIdle();
00113 
00114         NPC_SetAnim( NPC, SETANIM_BOTH, BOTH_SLEEP1, SETANIM_FLAG_NORMAL );
00115 }
00116 
00117 /*
00118 -------------------------
00119 Mark1Dead_FireRocket
00120 - Shoot the left weapon, the multi-blaster
00121 -------------------------
00122 */
00123 void Mark1Dead_FireRocket (void)
00124 {
00125         mdxaBone_t      boltMatrix;
00126         vec3_t  muzzle1,muzzle_dir;
00127         gentity_t *missile;
00128 
00129         int     damage  = 50;
00130         int bolt = trap_G2API_AddBolt(NPC->ghoul2, 0, "*flash5");
00131 
00132         trap_G2API_GetBoltMatrix( NPC->ghoul2, 0, 
00133                                 bolt,
00134                                 &boltMatrix, NPC->r.currentAngles, NPC->r.currentOrigin, level.time,
00135                                 NULL, NPC->modelScale );
00136 
00137         BG_GiveMeVectorFromMatrix( &boltMatrix, ORIGIN, muzzle1 );
00138         BG_GiveMeVectorFromMatrix( &boltMatrix, NEGATIVE_Y, muzzle_dir );
00139 
00140         G_PlayEffectID( G_EffectIndex("bryar/muzzle_flash"), muzzle1, muzzle_dir );
00141 
00142         G_Sound( NPC, CHAN_AUTO, G_SoundIndex("sound/chars/mark1/misc/mark1_fire"));
00143 
00144         missile = CreateMissile( muzzle1, muzzle_dir, BOWCASTER_VELOCITY, 10000, NPC, qfalse );
00145 
00146         missile->classname = "bowcaster_proj";
00147         missile->s.weapon = WP_BOWCASTER;
00148 
00149         VectorSet( missile->r.maxs, BOWCASTER_SIZE, BOWCASTER_SIZE, BOWCASTER_SIZE );
00150         VectorScale( missile->r.maxs, -1, missile->r.mins );
00151 
00152         missile->damage = damage;
00153         missile->dflags = DAMAGE_DEATH_KNOCKBACK;
00154         //missile->methodOfDeath = MOD_ENERGY;
00155         missile->methodOfDeath = MOD_ROCKET;
00156         missile->clipmask = MASK_SHOT | CONTENTS_LIGHTSABER;
00157         missile->splashDamage = BOWCASTER_SPLASH_DAMAGE;
00158         missile->splashRadius = BOWCASTER_SPLASH_RADIUS;
00159 
00160         // we don't want it to bounce
00161         missile->bounceCount = 0;
00162 
00163 }
00164 
00165 /*
00166 -------------------------
00167 Mark1Dead_FireBlaster
00168 - Shoot the left weapon, the multi-blaster
00169 -------------------------
00170 */
00171 void Mark1Dead_FireBlaster (void)
00172 {
00173         vec3_t  muzzle1,muzzle_dir;
00174         gentity_t       *missile;
00175         mdxaBone_t      boltMatrix;
00176         int                     bolt;
00177 
00178         bolt = trap_G2API_AddBolt(NPC->ghoul2, 0, "*flash1"); 
00179 
00180         trap_G2API_GetBoltMatrix( NPC->ghoul2, 0, 
00181                                 bolt,
00182                                 &boltMatrix, NPC->r.currentAngles, NPC->r.currentOrigin, level.time,
00183                                 NULL, NPC->modelScale );
00184 
00185         BG_GiveMeVectorFromMatrix( &boltMatrix, ORIGIN, muzzle1 );
00186         BG_GiveMeVectorFromMatrix( &boltMatrix, NEGATIVE_Y, muzzle_dir );
00187 
00188         G_PlayEffectID( G_EffectIndex("bryar/muzzle_flash"), muzzle1, muzzle_dir );
00189 
00190         missile = CreateMissile( muzzle1, muzzle_dir, 1600, 10000, NPC, qfalse );
00191 
00192         G_Sound( NPC, CHAN_AUTO, G_SoundIndex("sound/chars/mark1/misc/mark1_fire"));
00193 
00194         missile->classname = "bryar_proj";
00195         missile->s.weapon = WP_BRYAR_PISTOL;
00196 
00197         missile->damage = 1;
00198         missile->dflags = DAMAGE_DEATH_KNOCKBACK;
00199         missile->methodOfDeath = MOD_BRYAR_PISTOL;
00200         missile->clipmask = MASK_SHOT | CONTENTS_LIGHTSABER;
00201 
00202 }
00203 
00204 /*
00205 -------------------------
00206 Mark1_die
00207 -------------------------
00208 */
00209 void Mark1_die( gentity_t *self, gentity_t *inflictor, gentity_t *attacker, int damage, int mod,int dFlags,int hitLoc )
00210 {
00211         /*
00212         int     anim;
00213 
00214         // Is he dead already?
00215         anim = self->client->ps.legsAnim;
00216         if (((anim==BOTH_DEATH1) || (anim==BOTH_DEATH2)) && (self->client->ps.torsoTimer<=0)) 
00217         {       // This is because self->health keeps getting zeroed out. HL_NONE acts as health in this case.
00218                 self->locationDamage[HL_NONE] += damage;
00219                 if (self->locationDamage[HL_NONE] > 50)
00220                 {
00221                         DeathFX(self);
00222                         self->client->ps.eFlags |= EF_NODRAW;
00223                         self->contents = CONTENTS_CORPSE;
00224                         // G_FreeEntity( self ); // Is this safe?  I can't see why we'd mark it nodraw and then just leave it around??
00225                         self->e_ThinkFunc = thinkF_G_FreeEntity;
00226                         self->nextthink = level.time + FRAMETIME;
00227                 }
00228                 return;
00229         }
00230         */
00231 
00232         G_Sound( self, CHAN_AUTO, G_SoundIndex(va("sound/chars/mark1/misc/death%d.wav",Q_irand( 1, 2))));
00233 
00234         // Choose a death anim
00235         if (Q_irand( 1, 10) > 5)
00236         {
00237                 NPC_SetAnim( self, SETANIM_BOTH, BOTH_DEATH2, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD );
00238         }
00239         else
00240         {
00241                 NPC_SetAnim( self, SETANIM_BOTH, BOTH_DEATH1, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD );
00242         }
00243 }
00244 
00245 /*
00246 -------------------------
00247 Mark1_dying
00248 -------------------------
00249 */
00250 void Mark1_dying( gentity_t *self )
00251 {
00252         int     num,newBolt;
00253 
00254         if (self->client->ps.torsoTimer>0)
00255         {
00256                 if (TIMER_Done(self,"dyingExplosion"))
00257                 {
00258                         num = Q_irand( 1, 3);
00259 
00260                         // Find place to generate explosion
00261                         if (num == 1)
00262                         {
00263                                 num = Q_irand( 8, 10);
00264                                 newBolt = trap_G2API_AddBolt( self->ghoul2, 0, va("*flash%d",num) );
00265                                 NPC_Mark1_Part_Explode(self,newBolt);
00266                         }
00267                         else
00268                         {
00269                                 num = Q_irand( 1, 6);
00270                                 newBolt = trap_G2API_AddBolt( self->ghoul2, 0, va("*torso_tube%d",num) );
00271                                 NPC_Mark1_Part_Explode(self,newBolt);
00272                                 NPC_SetSurfaceOnOff( self, va("torso_tube%d",num), TURN_OFF );
00273                         }
00274 
00275                         TIMER_Set( self, "dyingExplosion", Q_irand( 300, 1000 ) );
00276                 }
00277 
00278 
00279 //              int             dir;
00280 //              vec3_t  right;
00281 
00282                 // Shove to the side
00283 //              AngleVectors( self->client->renderInfo.eyeAngles, NULL, right, NULL );
00284 //              VectorMA( self->client->ps.velocity, -80, right, self->client->ps.velocity );
00285 
00286                 // See which weapons are there
00287                 // Randomly fire blaster
00288                 if (!trap_G2API_GetSurfaceRenderStatus( self->ghoul2, 0, "l_arm" ))     // Is the blaster still on the model?
00289                 {
00290                         if (Q_irand( 1, 5) == 1)
00291                         {
00292                                 SaveNPCGlobals();
00293                                 SetNPCGlobals( self );
00294                                 Mark1Dead_FireBlaster();
00295                                 RestoreNPCGlobals();
00296                         }
00297                 }
00298 
00299                 // Randomly fire rocket
00300                 if (!trap_G2API_GetSurfaceRenderStatus( self->ghoul2, 0, "r_arm" ))     // Is the rocket still on the model?
00301                 {
00302                         if (Q_irand( 1, 10) == 1)
00303                         {
00304                                 SaveNPCGlobals();
00305                                 SetNPCGlobals( self );
00306                                 Mark1Dead_FireRocket();
00307                                 RestoreNPCGlobals();
00308                         }
00309                 }
00310         }
00311 
00312 }
00313 
00314 /*
00315 -------------------------
00316 NPC_Mark1_Pain
00317 - look at what was hit and see if it should be removed from the model.
00318 -------------------------
00319 */
00320 void NPC_Mark1_Pain(gentity_t *self, gentity_t *attacker, int damage)
00321 {
00322         int newBolt,i,chance;
00323         int hitLoc = gPainHitLoc;
00324         
00325         NPC_Pain( self, attacker, damage );
00326 
00327         G_Sound( self, CHAN_AUTO, G_SoundIndex("sound/chars/mark1/misc/mark1_pain"));
00328 
00329         // Hit in the CHEST???
00330         if (hitLoc==HL_CHEST)
00331         {
00332                 chance = Q_irand( 1, 4);
00333         
00334                 if ((chance == 1) && (damage > 5))
00335                 {
00336                         NPC_SetAnim( self, SETANIM_BOTH, BOTH_PAIN1, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD );
00337                 }
00338         }
00339         // Hit in the left arm?
00340         else if ((hitLoc==HL_ARM_LT) && (self->locationDamage[HL_ARM_LT] > LEFT_ARM_HEALTH))
00341         {
00342                 if (self->locationDamage[hitLoc] >= LEFT_ARM_HEALTH)    // Blow it up?
00343                 {
00344                         newBolt = trap_G2API_AddBolt( self->ghoul2, 0, "*flash3" );
00345                         if ( newBolt != -1 )
00346                         {
00347                                 NPC_Mark1_Part_Explode(self,newBolt);
00348                         }
00349 
00350                         NPC_SetSurfaceOnOff( self, "l_arm", TURN_OFF );
00351                 }
00352         }
00353         // Hit in the right arm?
00354         else if ((hitLoc==HL_ARM_RT) && (self->locationDamage[HL_ARM_RT] > RIGHT_ARM_HEALTH))   // Blow it up?
00355         {
00356                 if (self->locationDamage[hitLoc] >= RIGHT_ARM_HEALTH)
00357                 {                       
00358                         newBolt = trap_G2API_AddBolt( self->ghoul2, 0, "*flash4" );
00359                         if ( newBolt != -1 )
00360                         {
00361 //                              G_PlayEffect( "small_chunks", self->playerModel, self->genericBolt2, self->s.number);
00362                                 NPC_Mark1_Part_Explode( self, newBolt );
00363                         }
00364 
00365                         NPC_SetSurfaceOnOff( self, "r_arm", TURN_OFF );
00366                 }
00367         }
00368         // Check ammo pods
00369         else
00370         {
00371                 for (i=0;i<6;i++)
00372                 {
00373                         if ((hitLoc==HL_GENERIC1+i) && (self->locationDamage[HL_GENERIC1+i] > AMMO_POD_HEALTH)) // Blow it up?
00374                         {
00375                                 if (self->locationDamage[hitLoc] >= AMMO_POD_HEALTH)
00376                                 {                       
00377                                         newBolt = trap_G2API_AddBolt( self->ghoul2, 0, va("*torso_tube%d",(i+1)) );
00378                                         if ( newBolt != -1 )
00379                                         {
00380                                                 NPC_Mark1_Part_Explode(self,newBolt);
00381                                         }
00382                                         NPC_SetSurfaceOnOff( self, va("torso_tube%d",(i+1)), TURN_OFF );
00383                                         NPC_SetAnim( self, SETANIM_BOTH, BOTH_PAIN1, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD );
00384                                         break;
00385                                 }
00386                         }
00387                 }
00388         }
00389 
00390         // Are both guns shot off?
00391         if ((trap_G2API_GetSurfaceRenderStatus( self->ghoul2, 0, "l_arm" )>0) &&
00392                 (trap_G2API_GetSurfaceRenderStatus( self->ghoul2, 0, "r_arm" )>0))
00393         {
00394                 G_Damage(self,NULL,NULL,NULL,NULL,self->health,0,MOD_UNKNOWN);
00395         }
00396 }
00397 
00398 /*
00399 -------------------------
00400 Mark1_Hunt
00401 - look for enemy.
00402 -------------------------`
00403 */
00404 void Mark1_Hunt(void)
00405 {
00406 
00407         if ( NPCInfo->goalEntity == NULL )
00408         {
00409                 NPCInfo->goalEntity = NPC->enemy;
00410         }
00411 
00412         NPC_FaceEnemy( qtrue );
00413 
00414         NPCInfo->combatMove = qtrue;
00415         NPC_MoveToGoal( qtrue );
00416 }
00417 
00418 /*
00419 -------------------------
00420 Mark1_FireBlaster
00421 - Shoot the left weapon, the multi-blaster
00422 -------------------------
00423 */
00424 void Mark1_FireBlaster(void)
00425 {
00426         vec3_t  muzzle1,enemy_org1,delta1,angleToEnemy1;
00427         static  vec3_t  forward, vright, up;
00428         static  vec3_t  muzzle;
00429         gentity_t       *missile;
00430         mdxaBone_t      boltMatrix;
00431         int                     bolt;
00432 
00433         // Which muzzle to fire from?
00434         if ((NPCInfo->localState <= LSTATE_FIRED0) || (NPCInfo->localState == LSTATE_FIRED4)) 
00435         {
00436                 NPCInfo->localState = LSTATE_FIRED1;
00437                 bolt = trap_G2API_AddBolt(NPC->ghoul2, 0, "*flash1"); 
00438         }
00439         else if (NPCInfo->localState == LSTATE_FIRED1)
00440         {
00441                 NPCInfo->localState = LSTATE_FIRED2;
00442                 bolt = trap_G2API_AddBolt(NPC->ghoul2, 0, "*flash2"); 
00443         }
00444         else if (NPCInfo->localState == LSTATE_FIRED2)
00445         {
00446                 NPCInfo->localState = LSTATE_FIRED3;
00447                 bolt = trap_G2API_AddBolt(NPC->ghoul2, 0, "*flash3"); 
00448         }
00449         else
00450         {
00451                 NPCInfo->localState = LSTATE_FIRED4;
00452                 bolt = trap_G2API_AddBolt(NPC->ghoul2, 0, "*flash4"); 
00453         }
00454 
00455         trap_G2API_GetBoltMatrix( NPC->ghoul2, 0, 
00456                                 bolt,
00457                                 &boltMatrix, NPC->r.currentAngles, NPC->r.currentOrigin, level.time,
00458                                 NULL, NPC->modelScale );
00459 
00460         BG_GiveMeVectorFromMatrix( &boltMatrix, ORIGIN, muzzle1 );
00461 
00462         if (NPC->health)
00463         {
00464                 CalcEntitySpot( NPC->enemy, SPOT_HEAD, enemy_org1 );
00465                 VectorSubtract (enemy_org1, muzzle1, delta1);
00466                 vectoangles ( delta1, angleToEnemy1 );
00467                 AngleVectors (angleToEnemy1, forward, vright, up);
00468         }
00469         else
00470         {
00471                 AngleVectors (NPC->r.currentAngles, forward, vright, up);
00472         }
00473 
00474         G_PlayEffectID( G_EffectIndex("bryar/muzzle_flash"), muzzle1, forward );
00475 
00476         G_Sound( NPC, CHAN_AUTO, G_SoundIndex("sound/chars/mark1/misc/mark1_fire"));
00477 
00478         missile = CreateMissile( muzzle1, forward, 1600, 10000, NPC, qfalse );
00479 
00480         missile->classname = "bryar_proj";
00481         missile->s.weapon = WP_BRYAR_PISTOL;
00482 
00483         missile->damage = 1;
00484         missile->dflags = DAMAGE_DEATH_KNOCKBACK;
00485         missile->methodOfDeath = MOD_BRYAR_PISTOL;
00486         missile->clipmask = MASK_SHOT | CONTENTS_LIGHTSABER;
00487 
00488 }
00489 
00490 /*
00491 -------------------------
00492 Mark1_BlasterAttack
00493 -------------------------
00494 */
00495 void Mark1_BlasterAttack(qboolean advance )
00496 {
00497         int chance;
00498 
00499         if ( TIMER_Done( NPC, "attackDelay" ) ) // Attack?
00500         {
00501                 chance = Q_irand( 1, 5);
00502         
00503                 NPCInfo->burstCount++;
00504 
00505                 if (NPCInfo->burstCount<3)      // Too few shots this burst?
00506                 {
00507                         chance = 2;                             // Force it to keep firing.
00508                 }
00509                 else if (NPCInfo->burstCount>12)        // Too many shots fired this burst?
00510                 {
00511                         NPCInfo->burstCount = 0;
00512                         chance = 1;                             // Force it to stop firing.
00513                 }
00514 
00515                 // Stop firing.
00516                 if (chance == 1)
00517                 {
00518                         NPCInfo->burstCount = 0;
00519                         TIMER_Set( NPC, "attackDelay", Q_irand( 1000, 3000) );
00520                         NPC->client->ps.torsoTimer=0;                                           // Just in case the firing anim is running.
00521                 }
00522                 else
00523                 {
00524                         if (TIMER_Done( NPC, "attackDelay2" ))  // Can't be shooting every frame.
00525                         {
00526                                 TIMER_Set( NPC, "attackDelay2", Q_irand( 50, 50) );
00527                                 Mark1_FireBlaster();
00528                                 NPC_SetAnim( NPC, SETANIM_BOTH, BOTH_ATTACK1, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD );
00529                         }
00530                         return;
00531                 }
00532         }
00533         else if (advance)
00534         {
00535                 if ( NPC->client->ps.torsoAnim == BOTH_ATTACK1 )
00536                 {
00537                         NPC->client->ps.torsoTimer=0;                                           // Just in case the firing anim is running.
00538                 }
00539                 Mark1_Hunt();
00540         }
00541         else    // Make sure he's not firing.
00542         {
00543                 if ( NPC->client->ps.torsoAnim == BOTH_ATTACK1 )
00544                 {
00545                         NPC->client->ps.torsoTimer=0;                                           // Just in case the firing anim is running.
00546                 }
00547         }
00548 }
00549 
00550 /*
00551 -------------------------
00552 Mark1_FireRocket
00553 -------------------------
00554 */
00555 void Mark1_FireRocket(void)
00556 {
00557         mdxaBone_t      boltMatrix;
00558         vec3_t  muzzle1,enemy_org1,delta1,angleToEnemy1;
00559         static  vec3_t  forward, vright, up;
00560         int bolt = trap_G2API_AddBolt(NPC->ghoul2, 0, "*flash5");
00561         gentity_t *missile;
00562 
00563         int     damage  = 50;
00564 
00565         trap_G2API_GetBoltMatrix( NPC->ghoul2, 0, 
00566                                 bolt,
00567                                 &boltMatrix, NPC->r.currentAngles, NPC->r.currentOrigin, level.time,
00568                                 NULL, NPC->modelScale );
00569 
00570         BG_GiveMeVectorFromMatrix( &boltMatrix, ORIGIN, muzzle1 );
00571 
00572 //      G_PlayEffect( "blaster/muzzle_flash", muzzle1 );
00573 
00574         CalcEntitySpot( NPC->enemy, SPOT_HEAD, enemy_org1 );
00575         VectorSubtract (enemy_org1, muzzle1, delta1);
00576         vectoangles ( delta1, angleToEnemy1 );
00577         AngleVectors (angleToEnemy1, forward, vright, up);
00578 
00579         G_Sound( NPC, CHAN_AUTO, G_SoundIndex("sound/chars/mark1/misc/mark1_fire" ));
00580 
00581         missile = CreateMissile( muzzle1, forward, BOWCASTER_VELOCITY, 10000, NPC, qfalse );
00582 
00583         missile->classname = "bowcaster_proj";
00584         missile->s.weapon = WP_BOWCASTER;
00585 
00586         VectorSet( missile->r.maxs, BOWCASTER_SIZE, BOWCASTER_SIZE, BOWCASTER_SIZE );
00587         VectorScale( missile->r.maxs, -1, missile->r.mins );
00588 
00589         missile->damage = damage;
00590         missile->dflags = DAMAGE_DEATH_KNOCKBACK;
00591         missile->methodOfDeath = MOD_ROCKET;
00592         missile->clipmask = MASK_SHOT | CONTENTS_LIGHTSABER;
00593         missile->splashDamage = BOWCASTER_SPLASH_DAMAGE;
00594         missile->splashRadius = BOWCASTER_SPLASH_RADIUS;
00595 
00596         // we don't want it to bounce
00597         missile->bounceCount = 0;
00598 
00599 }
00600 
00601 /*
00602 -------------------------
00603 Mark1_RocketAttack
00604 -------------------------
00605 */
00606 void Mark1_RocketAttack( qboolean advance )
00607 {
00608         if ( TIMER_Done( NPC, "attackDelay" ) ) // Attack?
00609         {
00610                 TIMER_Set( NPC, "attackDelay", Q_irand( 1000, 3000) );
00611                 NPC_SetAnim( NPC, SETANIM_TORSO, BOTH_ATTACK2, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD );
00612                 Mark1_FireRocket();
00613         }
00614         else if (advance)
00615         {
00616                 Mark1_Hunt();
00617         }
00618 }
00619 
00620 /*
00621 -------------------------
00622 Mark1_AttackDecision
00623 -------------------------
00624 */
00625 void Mark1_AttackDecision( void )
00626 {
00627         int blasterTest,rocketTest;
00628         float           distance;
00629         distance_e      distRate;
00630         qboolean        visible;
00631         qboolean        advance;
00632 
00633         //randomly talk
00634         if ( TIMER_Done(NPC,"patrolNoise") )
00635         {
00636                 if (TIMER_Done(NPC,"angerNoise"))
00637                 {
00638 //                      G_Sound( NPC, G_SoundIndex(va("sound/chars/mark1/misc/talk%d.wav",      Q_irand(1, 4))));
00639                         TIMER_Set( NPC, "patrolNoise", Q_irand( 4000, 10000 ) );
00640                 }
00641         }
00642 
00643         // Enemy is dead or he has no enemy.
00644         if ((NPC->enemy->health<1) || ( NPC_CheckEnemyExt(qfalse) == qfalse ))
00645         {
00646                 NPC->enemy = NULL;
00647                 return;
00648         }
00649 
00650         // Rate our distance to the target and visibility
00651         distance        = (int) DistanceHorizontalSquared( NPC->r.currentOrigin, NPC->enemy->r.currentOrigin ); 
00652         distRate        = ( distance > MIN_MELEE_RANGE_SQR ) ? DIST_LONG : DIST_MELEE;
00653         visible         = NPC_ClearLOS4( NPC->enemy );
00654         advance         = (qboolean)(distance > MIN_DISTANCE_SQR);
00655 
00656         // If we cannot see our target, move to see it
00657         if ((!visible) || (!NPC_FaceEnemy(qtrue)))
00658         {
00659                 Mark1_Hunt();
00660                 return;
00661         }
00662 
00663         // See if the side weapons are there
00664         blasterTest = trap_G2API_GetSurfaceRenderStatus( NPC->ghoul2, 0, "l_arm" );
00665         rocketTest = trap_G2API_GetSurfaceRenderStatus( NPC->ghoul2, 0, "r_arm" );
00666 
00667         // It has both side weapons
00668         if (!blasterTest  && !rocketTest)
00669         {
00670                 ;       // So do nothing.
00671         }
00672         else if (blasterTest!=-1
00673                 &&blasterTest)
00674         {
00675                 distRate = DIST_LONG;
00676         }
00677         else if (rocketTest!=-1
00678                 &&rocketTest)
00679         {
00680                 distRate = DIST_MELEE;
00681         }
00682         else    // It should never get here, but just in case
00683         { 
00684                 NPC->health = 0;
00685                 NPC->client->ps.stats[STAT_HEALTH] = 0;
00686                 //GEntity_DieFunc(NPC, NPC, NPC, 100, MOD_UNKNOWN);
00687                 if (NPC->die)
00688                 {
00689                         NPC->die(NPC, NPC, NPC, 100, MOD_UNKNOWN);
00690                 }
00691         }
00692 
00693         // We can see enemy so shoot him if timers let you.
00694         NPC_FaceEnemy( qtrue );
00695 
00696         if (distRate == DIST_MELEE)
00697         {
00698                 Mark1_BlasterAttack(advance);
00699         }
00700         else if (distRate == DIST_LONG)
00701         {
00702                 Mark1_RocketAttack(advance);
00703         }
00704 }
00705 
00706 /*
00707 -------------------------
00708 Mark1_Patrol
00709 -------------------------
00710 */
00711 void Mark1_Patrol( void )
00712 {
00713         if ( NPC_CheckPlayerTeamStealth() )
00714         {
00715                 G_Sound( NPC, CHAN_AUTO, G_SoundIndex("sound/chars/mark1/misc/mark1_wakeup"));
00716                 NPC_UpdateAngles( qtrue, qtrue );
00717                 return;
00718         }
00719 
00720         //If we have somewhere to go, then do that
00721         if (!NPC->enemy)
00722         {
00723                 if ( UpdateGoal() )
00724                 {
00725                         ucmd.buttons |= BUTTON_WALKING;
00726                         NPC_MoveToGoal( qtrue );
00727                         NPC_UpdateAngles( qtrue, qtrue );
00728                 }
00729 
00730                 //randomly talk
00731 //              if (TIMER_Done(NPC,"patrolNoise"))
00732 //              {
00733 //                      G_Sound( NPC, G_SoundIndex(va("sound/chars/mark1/misc/talk%d.wav",      Q_irand(1, 4))));
00734 //
00735 //                      TIMER_Set( NPC, "patrolNoise", Q_irand( 2000, 4000 ) );
00736 //              }
00737         }
00738 
00739 }
00740 
00741 
00742 /*
00743 -------------------------
00744 NPC_BSMark1_Default
00745 -------------------------
00746 */
00747 void NPC_BSMark1_Default( void )
00748 {
00749         //NPC->e_DieFunc = dieF_Mark1_die;
00750 
00751         if ( NPC->enemy )
00752         {
00753                 NPCInfo->goalEntity = NPC->enemy;
00754                 Mark1_AttackDecision();
00755         }
00756         else if ( NPCInfo->scriptFlags & SCF_LOOK_FOR_ENEMIES )
00757         {
00758                 Mark1_Patrol();
00759         }
00760         else
00761         {
00762                 Mark1_Idle();
00763         }
00764 }