codemp/game/NPC_AI_Wampa.c

Go to the documentation of this file.
00001 #include "b_local.h"
00002 #include "g_nav.h"
00003 
00004 // These define the working combat range for these suckers
00005 #define MIN_DISTANCE            48
00006 #define MIN_DISTANCE_SQR        ( MIN_DISTANCE * MIN_DISTANCE )
00007 
00008 #define MAX_DISTANCE            1024
00009 #define MAX_DISTANCE_SQR        ( MAX_DISTANCE * MAX_DISTANCE )
00010 
00011 #define LSTATE_CLEAR            0
00012 #define LSTATE_WAITING          1
00013 
00014 float enemyDist = 0;
00015 
00016 void Wampa_SetBolts( gentity_t *self )
00017 {
00018         if ( self && self->client )
00019         {
00020                 renderInfo_t *ri = &self->client->renderInfo;
00021                 ri->headBolt = trap_G2API_AddBolt(self->ghoul2, 0, "*head_eyes");
00022                 //ri->cervicalBolt = trap_G2API_AddBolt(self->ghoul2, 0, "neck_bone" );
00023                 //ri->chestBolt = trap_G2API_AddBolt(self->ghoul2, 0, "upper_spine");
00024                 //ri->gutBolt = trap_G2API_AddBolt(self->ghoul2, 0, "mid_spine");
00025                 ri->torsoBolt = trap_G2API_AddBolt(self->ghoul2, 0, "lower_spine");
00026                 ri->crotchBolt = trap_G2API_AddBolt(self->ghoul2, 0, "rear_bone");
00027                 //ri->elbowLBolt = trap_G2API_AddBolt(self->ghoul2, 0, "*l_arm_elbow");
00028                 //ri->elbowRBolt = trap_G2API_AddBolt(self->ghoul2, 0, "*r_arm_elbow");
00029                 ri->handLBolt = trap_G2API_AddBolt(self->ghoul2, 0, "*l_hand");
00030                 ri->handRBolt = trap_G2API_AddBolt(self->ghoul2, 0, "*r_hand");
00031                 //ri->kneeLBolt = trap_G2API_AddBolt(self->ghoul2, 0, "*hips_l_knee");
00032                 //ri->kneeRBolt = trap_G2API_AddBolt(self->ghoul2, 0, "*hips_r_knee");
00033                 ri->footLBolt = trap_G2API_AddBolt(self->ghoul2, 0, "*l_leg_foot");
00034                 ri->footRBolt = trap_G2API_AddBolt(self->ghoul2, 0, "*r_leg_foot");
00035         }
00036 }
00037 
00038 /*
00039 -------------------------
00040 NPC_Wampa_Precache
00041 -------------------------
00042 */
00043 void NPC_Wampa_Precache( void )
00044 {
00045         /*
00046         int i;
00047         for ( i = 1; i < 4; i ++ )
00048         {
00049                 G_SoundIndex( va("sound/chars/wampa/growl%d.wav", i) );
00050         }
00051         for ( i = 1; i < 3; i ++ )
00052         {
00053                 G_SoundIndex( va("sound/chars/wampa/snort%d.wav", i) );
00054         }
00055         */
00056         G_SoundIndex( "sound/chars/rancor/swipehit.wav" );
00057         //G_SoundIndex( "sound/chars/wampa/chomp.wav" );
00058 }
00059 
00060 
00061 /*
00062 -------------------------
00063 Wampa_Idle
00064 -------------------------
00065 */
00066 void Wampa_Idle( void )
00067 {
00068         NPCInfo->localState = LSTATE_CLEAR;
00069 
00070         //If we have somewhere to go, then do that
00071         if ( UpdateGoal() )
00072         {
00073                 ucmd.buttons &= ~BUTTON_WALKING;
00074                 NPC_MoveToGoal( qtrue );
00075         }
00076 }
00077 
00078 qboolean Wampa_CheckRoar( gentity_t *self )
00079 {
00080         if ( self->wait < level.time )
00081         {
00082                 self->wait = level.time + Q_irand( 5000, 20000 );
00083                 NPC_SetAnim( self, SETANIM_BOTH, Q_irand(BOTH_GESTURE1,BOTH_GESTURE2), (SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD) );
00084                 TIMER_Set( self, "rageTime", self->client->ps.legsTimer );
00085                 return qtrue;
00086         }
00087         return qfalse;
00088 }
00089 /*
00090 -------------------------
00091 Wampa_Patrol
00092 -------------------------
00093 */
00094 void Wampa_Patrol( void )
00095 {
00096         NPCInfo->localState = LSTATE_CLEAR;
00097 
00098         //If we have somewhere to go, then do that
00099         if ( UpdateGoal() )
00100         {
00101                 ucmd.buttons |= BUTTON_WALKING;
00102                 NPC_MoveToGoal( qtrue );
00103         }
00104         else
00105         {
00106                 if ( TIMER_Done( NPC, "patrolTime" ))
00107                 {
00108                         TIMER_Set( NPC, "patrolTime", crandom() * 5000 + 5000 );
00109                 }
00110         }
00111 
00112         if ( NPC_CheckEnemyExt( qtrue ) == qfalse )
00113         {
00114                 Wampa_Idle();
00115                 return;
00116         }
00117         Wampa_CheckRoar( NPC );
00118         TIMER_Set( NPC, "lookForNewEnemy", Q_irand( 5000, 15000 ) );
00119 }
00120  
00121 /*
00122 -------------------------
00123 Wampa_Move
00124 -------------------------
00125 */
00126 void Wampa_Move( qboolean visible )
00127 {
00128         if ( NPCInfo->localState != LSTATE_WAITING )
00129         {
00130                 NPCInfo->goalEntity = NPC->enemy;
00131 
00132                 if ( NPC->enemy )
00133                 {//pick correct movement speed and anim
00134                         //run by default
00135                         ucmd.buttons &= ~BUTTON_WALKING;
00136                         if ( !TIMER_Done( NPC, "runfar" ) 
00137                                 || !TIMER_Done( NPC, "runclose" ) )
00138                         {//keep running with this anim & speed for a bit
00139                         }
00140                         else if ( !TIMER_Done( NPC, "walk" ) )
00141                         {//keep walking for a bit
00142                                 ucmd.buttons |= BUTTON_WALKING;
00143                         }
00144                         else if ( visible && enemyDist > 384 && NPCInfo->stats.runSpeed == 180 )
00145                         {//fast run, all fours
00146                                 NPCInfo->stats.runSpeed = 300;
00147                                 TIMER_Set( NPC, "runfar", Q_irand( 2000, 4000 ) );
00148                         }
00149                         else if ( enemyDist > 256 && NPCInfo->stats.runSpeed == 300 )
00150                         {//slow run, upright
00151                                 NPCInfo->stats.runSpeed = 180;
00152                                 TIMER_Set( NPC, "runclose", Q_irand( 3000, 5000 ) );
00153                         }
00154                         else if ( enemyDist < 128 )
00155                         {//walk
00156                                 NPCInfo->stats.runSpeed = 180;
00157                                 ucmd.buttons |= BUTTON_WALKING;
00158                                 TIMER_Set( NPC, "walk", Q_irand( 4000, 6000 ) );
00159                         }
00160                 }
00161 
00162                 if ( NPCInfo->stats.runSpeed == 300 )
00163                 {//need to use the alternate run - hunched over on all fours
00164                         NPC->client->ps.eFlags2 |= EF2_USE_ALT_ANIM;
00165                 }
00166                 NPC_MoveToGoal( qtrue );
00167                 NPCInfo->goalRadius = MAX_DISTANCE;     // just get us within combat range
00168         }
00169 }
00170 
00171 //---------------------------------------------------------
00172 //extern void G_Knockdown( gentity_t *self, gentity_t *attacker, const vec3_t pushDir, float strength, qboolean breakSaberLock );
00173 extern void G_Knockdown( gentity_t *victim );
00174 extern void G_Dismember( gentity_t *ent, gentity_t *enemy, vec3_t point, int limbType, float limbRollBase, float limbPitchBase, int deathAnim, qboolean postDeath );
00175 extern int NPC_GetEntsNearBolt( int *radiusEnts, float radius, int boltIndex, vec3_t boltOrg );
00176 
00177 void Wampa_Slash( int boltIndex, qboolean backhand )
00178 {
00179         int                     radiusEntNums[128];
00180         int                     numEnts;
00181         const float     radius = 88;
00182         const float     radiusSquared = (radius*radius);
00183         int                     i;
00184         vec3_t          boltOrg;
00185         int                     damage = (backhand)?Q_irand(10,15):Q_irand(20,30);
00186 
00187         numEnts = NPC_GetEntsNearBolt( radiusEntNums, radius, boltIndex, boltOrg );
00188 
00189         for ( i = 0; i < numEnts; i++ )
00190         {
00191                 gentity_t *radiusEnt = &g_entities[radiusEntNums[i]];
00192                 if ( !radiusEnt->inuse )
00193                 {
00194                         continue;
00195                 }
00196                 
00197                 if ( radiusEnt == NPC )
00198                 {//Skip the wampa ent
00199                         continue;
00200                 }
00201                 
00202                 if ( radiusEnt->client == NULL )
00203                 {//must be a client
00204                         continue;
00205                 }
00206 
00207                 if ( DistanceSquared( radiusEnt->r.currentOrigin, boltOrg ) <= radiusSquared )
00208                 {
00209                         //smack
00210                         G_Damage( radiusEnt, NPC, NPC, vec3_origin, radiusEnt->r.currentOrigin, damage, ((backhand)?DAMAGE_NO_ARMOR:(DAMAGE_NO_ARMOR|DAMAGE_NO_KNOCKBACK)), MOD_MELEE );
00211                         if ( backhand )
00212                         {
00213                                 //actually push the enemy
00214                                 vec3_t pushDir;
00215                                 vec3_t angs;
00216                                 VectorCopy( NPC->client->ps.viewangles, angs );
00217                                 angs[YAW] += flrand( 25, 50 );
00218                                 angs[PITCH] = flrand( -25, -15 );
00219                                 AngleVectors( angs, pushDir, NULL, NULL );
00220                                 if ( radiusEnt->client->NPC_class != CLASS_WAMPA
00221                                         && radiusEnt->client->NPC_class != CLASS_RANCOR
00222                                         && radiusEnt->client->NPC_class != CLASS_ATST )
00223                                 {
00224                                         G_Throw( radiusEnt, pushDir, 65 );
00225                                         if ( BG_KnockDownable(&radiusEnt->client->ps) &&
00226                                                 radiusEnt->health > 0 && Q_irand( 0, 1 ) )
00227                                         {//do pain on enemy
00228                                                 radiusEnt->client->ps.forceHandExtend = HANDEXTEND_KNOCKDOWN;
00229                                                 radiusEnt->client->ps.forceDodgeAnim = 0;
00230                                                 radiusEnt->client->ps.forceHandExtendTime = level.time + 1100;
00231                                                 radiusEnt->client->ps.quickerGetup = qfalse;
00232                                         }
00233                                 }
00234                         }
00235                         else if ( radiusEnt->health <= 0 && radiusEnt->client )
00236                         {//killed them, chance of dismembering
00237                                 if ( !Q_irand( 0, 1 ) )
00238                                 {//bite something off
00239                                         int hitLoc = Q_irand( G2_MODELPART_HEAD, G2_MODELPART_RLEG );
00240                                         if ( hitLoc == G2_MODELPART_HEAD )
00241                                         {
00242                                                 NPC_SetAnim( radiusEnt, SETANIM_BOTH, BOTH_DEATH17, SETANIM_FLAG_OVERRIDE | SETANIM_FLAG_HOLD );
00243                                         }
00244                                         else if ( hitLoc == G2_MODELPART_WAIST )
00245                                         {
00246                                                 NPC_SetAnim( radiusEnt, SETANIM_BOTH, BOTH_DEATHBACKWARD2, SETANIM_FLAG_OVERRIDE | SETANIM_FLAG_HOLD );
00247                                         }
00248                                         G_Dismember( radiusEnt, NPC, radiusEnt->r.currentOrigin, hitLoc, 90, 0, radiusEnt->client->ps.torsoAnim, qtrue);
00249                                 }
00250                         }
00251                         else if ( !Q_irand( 0, 3 ) && radiusEnt->health > 0 )
00252                         {//one out of every 4 normal hits does a knockdown, too
00253                                 vec3_t pushDir;
00254                                 vec3_t angs;
00255                                 VectorCopy( NPC->client->ps.viewangles, angs );
00256                                 angs[YAW] += flrand( 25, 50 );
00257                                 angs[PITCH] = flrand( -25, -15 );
00258                                 AngleVectors( angs, pushDir, NULL, NULL );
00259                                 G_Knockdown( radiusEnt );
00260                         }
00261                         G_Sound( radiusEnt, CHAN_WEAPON, G_SoundIndex( "sound/chars/rancor/swipehit.wav" ) );
00262                 }
00263         }
00264 }
00265 
00266 //------------------------------
00267 void Wampa_Attack( float distance, qboolean doCharge )
00268 {
00269         if ( !TIMER_Exists( NPC, "attacking" ) )
00270         {
00271                 if ( Q_irand(0, 2) && !doCharge )
00272                 {//double slash
00273                         NPC_SetAnim( NPC, SETANIM_BOTH, BOTH_ATTACK1, SETANIM_FLAG_OVERRIDE | SETANIM_FLAG_HOLD );
00274                         TIMER_Set( NPC, "attack_dmg", 750 );
00275                 }
00276                 else if ( doCharge || (distance > 270 && distance < 430 && !Q_irand(0, 1)) )
00277                 {//leap
00278                         vec3_t  fwd, yawAng;
00279                         VectorSet( yawAng, 0, NPC->client->ps.viewangles[YAW], 0 );
00280                         NPC_SetAnim( NPC, SETANIM_BOTH, BOTH_ATTACK2, SETANIM_FLAG_OVERRIDE | SETANIM_FLAG_HOLD );
00281                         TIMER_Set( NPC, "attack_dmg", 500 );
00282                         AngleVectors( yawAng, fwd, NULL, NULL );
00283                         VectorScale( fwd, distance*1.5f, NPC->client->ps.velocity );
00284                         NPC->client->ps.velocity[2] = 150;
00285                         NPC->client->ps.groundEntityNum = ENTITYNUM_NONE;
00286                 }
00287                 else
00288                 {//backhand
00289                         NPC_SetAnim( NPC, SETANIM_BOTH, BOTH_ATTACK3, SETANIM_FLAG_OVERRIDE | SETANIM_FLAG_HOLD );
00290                         TIMER_Set( NPC, "attack_dmg", 250 );
00291                 }
00292 
00293                 TIMER_Set( NPC, "attacking", NPC->client->ps.legsTimer + random() * 200 );
00294                 //allow us to re-evaluate our running speed/anim
00295                 TIMER_Set( NPC, "runfar", -1 );
00296                 TIMER_Set( NPC, "runclose", -1 );
00297                 TIMER_Set( NPC, "walk", -1 );
00298         }
00299 
00300         // Need to do delayed damage since the attack animations encapsulate multiple mini-attacks
00301 
00302         if ( TIMER_Done2( NPC, "attack_dmg", qtrue ) )
00303         {
00304                 switch ( NPC->client->ps.legsAnim )
00305                 {
00306                 case BOTH_ATTACK1:
00307                         Wampa_Slash( NPC->client->renderInfo.handRBolt, qfalse );
00308                         //do second hit
00309                         TIMER_Set( NPC, "attack_dmg2", 100 );
00310                         break;
00311                 case BOTH_ATTACK2:
00312                         Wampa_Slash( NPC->client->renderInfo.handRBolt, qfalse );
00313                         TIMER_Set( NPC, "attack_dmg2", 100 );
00314                         break;
00315                 case BOTH_ATTACK3:
00316                         Wampa_Slash( NPC->client->renderInfo.handLBolt, qtrue );
00317                         break;
00318                 }
00319         }
00320         else if ( TIMER_Done2( NPC, "attack_dmg2", qtrue ) )
00321         {
00322                 switch ( NPC->client->ps.legsAnim )
00323                 {
00324                 case BOTH_ATTACK1:
00325                         Wampa_Slash( NPC->client->renderInfo.handLBolt, qfalse );
00326                         break;
00327                 case BOTH_ATTACK2:
00328                         Wampa_Slash( NPC->client->renderInfo.handLBolt, qfalse );
00329                         break;
00330                 }
00331         }
00332 
00333         // Just using this to remove the attacking flag at the right time
00334         TIMER_Done2( NPC, "attacking", qtrue );
00335 
00336         if ( NPC->client->ps.legsAnim == BOTH_ATTACK1 && distance > (NPC->r.maxs[0]+MIN_DISTANCE) )
00337         {//okay to keep moving
00338                 ucmd.buttons |= BUTTON_WALKING;
00339                 Wampa_Move( 1 );
00340         }
00341 }
00342 
00343 //----------------------------------
00344 void Wampa_Combat( void )
00345 {
00346         // If we cannot see our target or we have somewhere to go, then do that
00347         if ( !NPC_ClearLOS( NPC->r.currentOrigin, NPC->enemy->r.currentOrigin ) )
00348         {
00349                 if ( !Q_irand( 0, 10 ) )
00350                 {
00351                         if ( Wampa_CheckRoar( NPC ) )
00352                         {
00353                                 return;
00354                         }
00355                 }
00356                 NPCInfo->combatMove = qtrue;
00357                 NPCInfo->goalEntity = NPC->enemy;
00358                 NPCInfo->goalRadius = MAX_DISTANCE;     // just get us within combat range
00359 
00360                 Wampa_Move( 0 );
00361                 return;
00362         }
00363         else if ( UpdateGoal() )
00364         {
00365                 NPCInfo->combatMove = qtrue;
00366                 NPCInfo->goalEntity = NPC->enemy;
00367                 NPCInfo->goalRadius = MAX_DISTANCE;     // just get us within combat range
00368 
00369                 Wampa_Move( 1 );
00370                 return;
00371         }
00372         else
00373         {
00374                 float           distance = enemyDist = Distance( NPC->r.currentOrigin, NPC->enemy->r.currentOrigin );   
00375                 qboolean        advance = (qboolean)( distance > (NPC->r.maxs[0]+MIN_DISTANCE) ? qtrue : qfalse  );
00376                 qboolean        doCharge = qfalse;
00377 
00378                 // Sometimes I have problems with facing the enemy I'm attacking, so force the issue so I don't look dumb
00379                 //FIXME: always seems to face off to the left or right?!!!!
00380                 NPC_FaceEnemy( qtrue );
00381 
00382 
00383                 if ( advance )
00384                 {//have to get closer
00385                         vec3_t  yawOnlyAngles;
00386                         VectorSet( yawOnlyAngles, 0, NPC->r.currentAngles[YAW], 0 );
00387                         if ( NPC->enemy->health > 0//enemy still alive
00388                                 && fabs(distance-350) <= 80 //enemy anywhere from 270 to 430 away
00389                                 && InFOV3( NPC->enemy->r.currentOrigin, NPC->r.currentOrigin, yawOnlyAngles, 20, 20 ) )//enemy generally in front
00390                         {//10% chance of doing charge anim
00391                                 if ( !Q_irand( 0, 9 ) )
00392                                 {//go for the charge
00393                                         doCharge = qtrue;
00394                                         advance = qfalse;
00395                                 }
00396                         }
00397                 }
00398 
00399                 if (( advance || NPCInfo->localState == LSTATE_WAITING ) && TIMER_Done( NPC, "attacking" )) // waiting monsters can't attack
00400                 {
00401                         if ( TIMER_Done2( NPC, "takingPain", qtrue ))
00402                         {
00403                                 NPCInfo->localState = LSTATE_CLEAR;
00404                         }
00405                         else
00406                         {
00407                                 Wampa_Move( 1 );
00408                         }
00409                 }
00410                 else
00411                 {
00412                         if ( !Q_irand( 0, 20 ) )
00413                         {//FIXME: only do this if we just damaged them or vice-versa?
00414                                 if ( Wampa_CheckRoar( NPC ) )
00415                                 {
00416                                         return;
00417                                 }
00418                         }
00419                         if ( !Q_irand( 0, 1 ) )
00420                         {//FIXME: base on skill
00421                                 Wampa_Attack( distance, doCharge );
00422                         }
00423                 }
00424         }
00425 }
00426 
00427 /*
00428 -------------------------
00429 NPC_Wampa_Pain
00430 -------------------------
00431 */
00432 //void NPC_Wampa_Pain( gentity_t *self, gentity_t *inflictor, gentity_t *other, const vec3_t point, int damage, int mod,int hitLoc ) 
00433 void NPC_Wampa_Pain( gentity_t *self, gentity_t *attacker, int damage ) 
00434 {
00435         qboolean hitByWampa = qfalse;
00436         if ( attacker&&attacker->client&&attacker->client->NPC_class==CLASS_WAMPA )
00437         {
00438                 hitByWampa = qtrue;
00439         }
00440         if ( attacker 
00441                 && attacker->inuse 
00442                 && attacker != self->enemy
00443                 && !(attacker->flags&FL_NOTARGET) )
00444         {
00445                 if ( (!attacker->s.number&&!Q_irand(0,3))
00446                         || !self->enemy
00447                         || self->enemy->health == 0
00448                         || (self->enemy->client&&self->enemy->client->NPC_class == CLASS_WAMPA)
00449                         || (!Q_irand(0, 4 ) && DistanceSquared( attacker->r.currentOrigin, self->r.currentOrigin ) < DistanceSquared( self->enemy->r.currentOrigin, self->r.currentOrigin )) ) 
00450                 {//if my enemy is dead (or attacked by player) and I'm not still holding/eating someone, turn on the attacker
00451                         //FIXME: if can't nav to my enemy, take this guy if I can nav to him
00452                         G_SetEnemy( self, attacker );
00453                         TIMER_Set( self, "lookForNewEnemy", Q_irand( 5000, 15000 ) );
00454                         if ( hitByWampa )
00455                         {//stay mad at this Wampa for 2-5 secs before looking for attacker enemies
00456                                 TIMER_Set( self, "wampaInfight", Q_irand( 2000, 5000 ) );
00457                         }
00458                 }
00459         }
00460         if ( (hitByWampa|| Q_irand( 0, 100 ) < damage )//hit by wampa, hit while holding live victim, or took a lot of damage
00461                 && self->client->ps.legsAnim != BOTH_GESTURE1
00462                 && self->client->ps.legsAnim != BOTH_GESTURE2
00463                 && TIMER_Done( self, "takingPain" ) )
00464         {
00465                 if ( !Wampa_CheckRoar( self ) )
00466                 {
00467                         if ( self->client->ps.legsAnim != BOTH_ATTACK1
00468                                 && self->client->ps.legsAnim != BOTH_ATTACK2
00469                                 && self->client->ps.legsAnim != BOTH_ATTACK3 )
00470                         {//cant interrupt one of the big attack anims
00471                                 if ( self->health > 100 || hitByWampa )
00472                                 {
00473                                         TIMER_Remove( self, "attacking" );
00474 
00475                                         VectorCopy( self->NPC->lastPathAngles, self->s.angles );
00476 
00477                                         if ( !Q_irand( 0, 1 ) )
00478                                         {
00479                                                 NPC_SetAnim( self, SETANIM_BOTH, BOTH_PAIN2, SETANIM_FLAG_OVERRIDE | SETANIM_FLAG_HOLD );
00480                                         }
00481                                         else
00482                                         {
00483                                                 NPC_SetAnim( self, SETANIM_BOTH, BOTH_PAIN1, SETANIM_FLAG_OVERRIDE | SETANIM_FLAG_HOLD );
00484                                         }
00485                                         TIMER_Set( self, "takingPain", self->client->ps.legsTimer+Q_irand(0, 500) );
00486                                         //allow us to re-evaluate our running speed/anim
00487                                         TIMER_Set( self, "runfar", -1 );
00488                                         TIMER_Set( self, "runclose", -1 );
00489                                         TIMER_Set( self, "walk", -1 );
00490 
00491                                         if ( self->NPC )
00492                                         {
00493                                                 self->NPC->localState = LSTATE_WAITING;
00494                                         }
00495                                 }
00496                         }
00497                 }
00498         }
00499 }
00500 
00501 /*
00502 -------------------------
00503 NPC_BSWampa_Default
00504 -------------------------
00505 */
00506 void NPC_BSWampa_Default( void )
00507 {
00508         NPC->client->ps.eFlags2 &= ~EF2_USE_ALT_ANIM;
00509         //NORMAL ANIMS
00510         //      stand1 = normal stand
00511         //      walk1 = normal, non-angry walk
00512         //      walk2 = injured
00513         //      run1 = far away run
00514         //      run2 = close run
00515         //VICTIM ANIMS
00516         //      grabswipe = melee1 - sweep out and grab
00517         //      stand2 attack = attack4 - while holding victim, swipe at him
00518         //      walk3_drag = walk5 - walk with drag
00519         //      stand2 = hold victim
00520         //      stand2to1 = drop victim
00521         if ( !TIMER_Done( NPC, "rageTime" ) )
00522         {//do nothing but roar first time we see an enemy
00523                 NPC_FaceEnemy( qtrue );
00524                 return;
00525         }
00526         if ( NPC->enemy )
00527         {
00528                 if ( !TIMER_Done(NPC,"attacking") )
00529                 {//in middle of attack
00530                         //face enemy
00531                         NPC_FaceEnemy( qtrue );
00532                         //continue attack logic
00533                         enemyDist = Distance( NPC->r.currentOrigin, NPC->enemy->r.currentOrigin );
00534                         Wampa_Attack( enemyDist, qfalse );
00535                         return;
00536                 }
00537                 else
00538                 {
00539                         if ( TIMER_Done(NPC,"angrynoise") )
00540                         {
00541                                 G_Sound( NPC, CHAN_VOICE, G_SoundIndex( va("sound/chars/wampa/misc/anger%d.wav", Q_irand(1, 2)) ) );
00542 
00543                                 TIMER_Set( NPC, "angrynoise", Q_irand( 5000, 10000 ) );
00544                         }
00545                         //else, if he's in our hand, we eat, else if he's on the ground, we keep attacking his dead body for a while
00546                         if( NPC->enemy->client && NPC->enemy->client->NPC_class == CLASS_WAMPA )
00547                         {//got mad at another Wampa, look for a valid enemy
00548                                 if ( TIMER_Done( NPC, "wampaInfight" ) )
00549                                 {
00550                                         NPC_CheckEnemyExt( qtrue );
00551                                 }
00552                         }
00553                         else
00554                         {
00555                                 if ( ValidEnemy( NPC->enemy ) == qfalse )
00556                                 {
00557                                         TIMER_Remove( NPC, "lookForNewEnemy" );//make them look again right now
00558                                         if ( !NPC->enemy->inuse || level.time - NPC->enemy->s.time > Q_irand( 10000, 15000 ) )
00559                                         {//it's been a while since the enemy died, or enemy is completely gone, get bored with him
00560                                                 NPC->enemy = NULL;
00561                                                 Wampa_Patrol();
00562                                                 NPC_UpdateAngles( qtrue, qtrue );
00563                                                 //just lost my enemy
00564                                                 if ( (NPC->spawnflags&2) )
00565                                                 {//search around me if I don't have an enemy
00566                                                         NPC_BSSearchStart( NPC->waypoint, BS_SEARCH );
00567                                                         NPCInfo->tempBehavior = BS_DEFAULT;
00568                                                 }
00569                                                 else if ( (NPC->spawnflags&1) )
00570                                                 {//wander if I don't have an enemy
00571                                                         NPC_BSSearchStart( NPC->waypoint, BS_WANDER );
00572                                                         NPCInfo->tempBehavior = BS_DEFAULT;
00573                                                 }
00574                                                 return;
00575                                         }
00576                                 }
00577                                 if ( TIMER_Done( NPC, "lookForNewEnemy" ) )
00578                                 {
00579                                         gentity_t *newEnemy, *sav_enemy = NPC->enemy;//FIXME: what about NPC->lastEnemy?
00580                                         NPC->enemy = NULL;
00581                                         newEnemy = NPC_CheckEnemy( NPCInfo->confusionTime < level.time, qfalse, qfalse );
00582                                         NPC->enemy = sav_enemy;
00583                                         if ( newEnemy && newEnemy != sav_enemy )
00584                                         {//picked up a new enemy!
00585                                                 NPC->lastEnemy = NPC->enemy;
00586                                                 G_SetEnemy( NPC, newEnemy );
00587                                                 //hold this one for at least 5-15 seconds
00588                                                 TIMER_Set( NPC, "lookForNewEnemy", Q_irand( 5000, 15000 ) );
00589                                         }
00590                                         else
00591                                         {//look again in 2-5 secs
00592                                                 TIMER_Set( NPC, "lookForNewEnemy", Q_irand( 2000, 5000 ) );
00593                                         }
00594                                 }
00595                         }
00596                         Wampa_Combat();
00597                         return;
00598                 }
00599         }
00600         else 
00601         {
00602                 if ( TIMER_Done(NPC,"idlenoise") )
00603                 {
00604                         G_Sound( NPC, CHAN_AUTO, G_SoundIndex( "sound/chars/wampa/misc/anger3.wav" ) );
00605 
00606                         TIMER_Set( NPC, "idlenoise", Q_irand( 2000, 4000 ) );
00607                 }
00608                 if ( (NPC->spawnflags&2) )
00609                 {//search around me if I don't have an enemy
00610                         if ( NPCInfo->homeWp == WAYPOINT_NONE )
00611                         {//no homewap, initialize the search behavior
00612                                 NPC_BSSearchStart( WAYPOINT_NONE, BS_SEARCH );
00613                                 NPCInfo->tempBehavior = BS_DEFAULT;
00614                         }
00615                         ucmd.buttons |= BUTTON_WALKING;
00616                         NPC_BSSearch();//this automatically looks for enemies
00617                 }
00618                 else if ( (NPC->spawnflags&1) )
00619                 {//wander if I don't have an enemy
00620                         if ( NPCInfo->homeWp == WAYPOINT_NONE )
00621                         {//no homewap, initialize the wander behavior
00622                                 NPC_BSSearchStart( WAYPOINT_NONE, BS_WANDER );
00623                                 NPCInfo->tempBehavior = BS_DEFAULT;
00624                         }
00625                         ucmd.buttons |= BUTTON_WALKING;
00626                         NPC_BSWander();
00627                         if ( NPCInfo->scriptFlags & SCF_LOOK_FOR_ENEMIES )
00628                         {
00629                                 if ( NPC_CheckEnemyExt( qtrue ) == qfalse )
00630                                 {
00631                                         Wampa_Idle();
00632                                 }
00633                                 else
00634                                 {
00635                                         Wampa_CheckRoar( NPC );
00636                                         TIMER_Set( NPC, "lookForNewEnemy", Q_irand( 5000, 15000 ) );
00637                                 }
00638                         }
00639                 }
00640                 else
00641                 {
00642                         if ( NPCInfo->scriptFlags & SCF_LOOK_FOR_ENEMIES )
00643                         {
00644                                 Wampa_Patrol();
00645                         }
00646                         else
00647                         {
00648                                 Wampa_Idle();
00649                         }
00650                 }
00651         }
00652 
00653         NPC_UpdateAngles( qtrue, qtrue );
00654 }