codemp/game/NPC_utils.c

Go to the documentation of this file.
00001 //NPC_utils.cpp
00002 
00003 #include "b_local.h"
00004 #include "../icarus/Q3_Interface.h"
00005 #include "../ghoul2/G2.h"
00006 
00007 int     teamNumbers[TEAM_NUM_TEAMS];
00008 int     teamStrength[TEAM_NUM_TEAMS];
00009 int     teamCounter[TEAM_NUM_TEAMS];
00010 
00011 #define VALID_ATTACK_CONE       2.0f    //Degrees
00012 extern void G_DebugPrint( int level, const char *format, ... );
00013 
00014 /*
00015 void CalcEntitySpot ( gentity_t *ent, spot_t spot, vec3_t point ) 
00016 
00017 Added: Uses shootAngles if a NPC has them
00018 
00019 */
00020 void CalcEntitySpot ( const gentity_t *ent, const spot_t spot, vec3_t point ) 
00021 {
00022         vec3_t  forward, up, right;
00023         vec3_t  start, end;
00024         trace_t tr;
00025 
00026         if ( !ent )
00027         {
00028                 return;
00029         }
00030         switch ( spot ) 
00031         {
00032         case SPOT_ORIGIN:
00033                 if(VectorCompare(ent->r.currentOrigin, vec3_origin))
00034                 {//brush
00035                         VectorSubtract(ent->r.absmax, ent->r.absmin, point);//size
00036                         VectorMA(ent->r.absmin, 0.5, point, point);
00037                 }
00038                 else
00039                 {
00040                         VectorCopy ( ent->r.currentOrigin, point );
00041                 }
00042                 break;
00043 
00044         case SPOT_CHEST:
00045         case SPOT_HEAD:
00046                 if ( ent->client && VectorLengthSquared( ent->client->renderInfo.eyePoint ) /*&& (ent->client->ps.viewEntity <= 0 || ent->client->ps.viewEntity >= ENTITYNUM_WORLD)*/ )
00047                 {//Actual tag_head eyespot!
00048                         //FIXME: Stasis aliens may have a problem here...
00049                         VectorCopy( ent->client->renderInfo.eyePoint, point );
00050                         if ( ent->client->NPC_class == CLASS_ATST )
00051                         {//adjust up some
00052                                 point[2] += 28;//magic number :)
00053                         }
00054                         if ( ent->NPC )
00055                         {//always aim from the center of my bbox, so we don't wiggle when we lean forward or backwards
00056                                 point[0] = ent->r.currentOrigin[0];
00057                                 point[1] = ent->r.currentOrigin[1];
00058                         }
00059                         /*
00060                         else if (ent->s.eType == ET_PLAYER )
00061                         {
00062                                 SubtractLeanOfs( ent, point );
00063                         }
00064                         */
00065                 }
00066                 else
00067                 {
00068                         VectorCopy ( ent->r.currentOrigin, point );
00069                         if ( ent->client ) 
00070                         {
00071                                 point[2] += ent->client->ps.viewheight;
00072                         }
00073                 }
00074                 if ( spot == SPOT_CHEST && ent->client )
00075                 {
00076                         if ( ent->client->NPC_class != CLASS_ATST )
00077                         {//adjust up some
00078                                 point[2] -= ent->r.maxs[2]*0.2f;
00079                         }
00080                 }
00081                 break;
00082 
00083         case SPOT_HEAD_LEAN:
00084                 if ( ent->client && VectorLengthSquared( ent->client->renderInfo.eyePoint ) /*&& (ent->client->ps.viewEntity <= 0 || ent->client->ps.viewEntity >= ENTITYNUM_WORLD*/ )
00085                 {//Actual tag_head eyespot!
00086                         //FIXME: Stasis aliens may have a problem here...
00087                         VectorCopy( ent->client->renderInfo.eyePoint, point );
00088                         if ( ent->client->NPC_class == CLASS_ATST )
00089                         {//adjust up some
00090                                 point[2] += 28;//magic number :)
00091                         }
00092                         if ( ent->NPC )
00093                         {//always aim from the center of my bbox, so we don't wiggle when we lean forward or backwards
00094                                 point[0] = ent->r.currentOrigin[0];
00095                                 point[1] = ent->r.currentOrigin[1];
00096                         }
00097                         /*
00098                         else if ( ent->s.eType == ET_PLAYER )
00099                         {
00100                                 SubtractLeanOfs( ent, point );
00101                         }
00102                         */
00103                         //NOTE: automatically takes leaning into account!
00104                 }
00105                 else
00106                 {
00107                         VectorCopy ( ent->r.currentOrigin, point );
00108                         if ( ent->client ) 
00109                         {
00110                                 point[2] += ent->client->ps.viewheight;
00111                         }
00112                         //AddLeanOfs ( ent, point );
00113                 }
00114                 break;
00115 
00116         //FIXME: implement...
00117         //case SPOT_CHEST:
00118                 //Returns point 3/4 from tag_torso to tag_head?
00119                 //break;
00120 
00121         case SPOT_LEGS:
00122                 VectorCopy ( ent->r.currentOrigin, point );
00123                 point[2] += (ent->r.mins[2] * 0.5);
00124                 break;
00125 
00126         case SPOT_WEAPON:
00127                 if( ent->NPC && !VectorCompare( ent->NPC->shootAngles, vec3_origin ) && !VectorCompare( ent->NPC->shootAngles, ent->client->ps.viewangles ))
00128                 {
00129                         AngleVectors( ent->NPC->shootAngles, forward, right, up );
00130                 }
00131                 else
00132                 {
00133                         AngleVectors( ent->client->ps.viewangles, forward, right, up );
00134                 }
00135                 CalcMuzzlePoint( (gentity_t*)ent, forward, right, up, point );
00136                 //NOTE: automatically takes leaning into account!
00137                 break;
00138 
00139         case SPOT_GROUND:
00140                 // if entity is on the ground, just use it's absmin
00141                 if ( ent->s.groundEntityNum != -1 ) 
00142                 {
00143                         VectorCopy( ent->r.currentOrigin, point );
00144                         point[2] = ent->r.absmin[2];
00145                         break;
00146                 }
00147 
00148                 // if it is reasonably close to the ground, give the point underneath of it
00149                 VectorCopy( ent->r.currentOrigin, start );
00150                 start[2] = ent->r.absmin[2];
00151                 VectorCopy( start, end );
00152                 end[2] -= 64;
00153                 trap_Trace( &tr, start, ent->r.mins, ent->r.maxs, end, ent->s.number, MASK_PLAYERSOLID );
00154                 if ( tr.fraction < 1.0 ) 
00155                 {
00156                         VectorCopy( tr.endpos, point);
00157                         break;
00158                 }
00159 
00160                 // otherwise just use the origin
00161                 VectorCopy( ent->r.currentOrigin, point );
00162                 break;
00163 
00164         default:
00165                 VectorCopy ( ent->r.currentOrigin, point );
00166                 break;
00167         }
00168 }
00169 
00170 
00171 //===================================================================================
00172 
00173 /*
00174 qboolean NPC_UpdateAngles ( qboolean doPitch, qboolean doYaw ) 
00175 
00176 Added: option to do just pitch or just yaw
00177 
00178 Does not include "aim" in it's calculations
00179 
00180 FIXME: stop compressing angles into shorts!!!!
00181 */
00182 qboolean NPC_UpdateAngles ( qboolean doPitch, qboolean doYaw ) 
00183 {
00184 #if 1
00185 
00186         float           error;
00187         float           decay;
00188         float           targetPitch = 0;
00189         float           targetYaw = 0;
00190         float           yawSpeed;
00191         qboolean        exact = qtrue;
00192 
00193         // if angle changes are locked; just keep the current angles
00194         // aimTime isn't even set anymore... so this code was never reached, but I need a way to lock NPC's yaw, so instead of making a new SCF_ flag, just use the existing render flag... - dmv
00195         if ( !NPC->enemy && ( (level.time < NPCInfo->aimTime) /*|| NPC->client->renderInfo.renderFlags & RF_LOCKEDANGLE*/) ) 
00196         {
00197                 if(doPitch)
00198                         targetPitch = NPCInfo->lockedDesiredPitch;
00199 
00200                 if(doYaw)
00201                         targetYaw = NPCInfo->lockedDesiredYaw;
00202         }
00203         else 
00204         {
00205                 // we're changing the lockedDesired Pitch/Yaw below so it's lost it's original meaning, get rid of the lock flag
00206         //      NPC->client->renderInfo.renderFlags &= ~RF_LOCKEDANGLE;
00207 
00208                 if(doPitch)
00209                 {
00210                         targetPitch = NPCInfo->desiredPitch;
00211                         NPCInfo->lockedDesiredPitch = NPCInfo->desiredPitch;
00212                 }
00213 
00214                 if(doYaw)
00215                 {
00216                         targetYaw = NPCInfo->desiredYaw;
00217                         NPCInfo->lockedDesiredYaw = NPCInfo->desiredYaw;
00218                 }                       
00219         }
00220 
00221         if ( NPC->s.weapon == WP_EMPLACED_GUN )
00222         {
00223                 // FIXME: this seems to do nothing, actually...
00224                 yawSpeed = 20;
00225         }
00226         else
00227         {
00228                 yawSpeed = NPCInfo->stats.yawSpeed;
00229         }
00230 
00231         if ( NPC->s.weapon == WP_SABER && NPC->client->ps.fd.forcePowersActive&(1<<FP_SPEED) )
00232         {
00233                 char buf[128];
00234                 float tFVal = 0;
00235 
00236                 trap_Cvar_VariableStringBuffer("timescale", buf, sizeof(buf));
00237 
00238                 tFVal = atof(buf);
00239 
00240                 yawSpeed *= 1.0f/tFVal;
00241         }
00242         
00243         if( doYaw )
00244         {
00245                 // decay yaw error
00246                 error = AngleDelta ( NPC->client->ps.viewangles[YAW], targetYaw );
00247                 if( fabs(error) > MIN_ANGLE_ERROR )
00248                 {
00249                         if ( error ) 
00250                         {
00251                                 exact = qfalse;
00252 
00253                                 decay = 60.0 + yawSpeed * 3;
00254                                 decay *= 50.0f / 1000.0f;//msec
00255 
00256                                 if ( error < 0.0 ) 
00257                                 {
00258                                         error += decay;
00259                                         if ( error > 0.0 ) 
00260                                         {
00261                                                 error = 0.0;
00262                                         }
00263                                 }
00264                                 else 
00265                                 {
00266                                         error -= decay;
00267                                         if ( error < 0.0 ) 
00268                                         {
00269                                                 error = 0.0;
00270                                         }
00271                                 }
00272                         }
00273                 }
00274                 
00275                 ucmd.angles[YAW] = ANGLE2SHORT( targetYaw + error ) - client->ps.delta_angles[YAW];
00276         }
00277 
00278         //FIXME: have a pitchSpeed?
00279         if( doPitch )
00280         {
00281                 // decay pitch error
00282                 error = AngleDelta ( NPC->client->ps.viewangles[PITCH], targetPitch );
00283                 if ( fabs(error) > MIN_ANGLE_ERROR )
00284                 {
00285                         if ( error ) 
00286                         {
00287                                 exact = qfalse;
00288 
00289                                 decay = 60.0 + yawSpeed * 3;
00290                                 decay *= 50.0f / 1000.0f;//msec
00291 
00292                                 if ( error < 0.0 ) 
00293                                 {
00294                                         error += decay;
00295                                         if ( error > 0.0 ) 
00296                                         {
00297                                                 error = 0.0;
00298                                         }
00299                                 }
00300                                 else 
00301                                 {
00302                                         error -= decay;
00303                                         if ( error < 0.0 ) 
00304                                         {
00305                                                 error = 0.0;
00306                                         }
00307                                 }
00308                         }
00309                 }
00310 
00311                 ucmd.angles[PITCH] = ANGLE2SHORT( targetPitch + error ) - client->ps.delta_angles[PITCH];
00312         }
00313 
00314         ucmd.angles[ROLL] = ANGLE2SHORT ( NPC->client->ps.viewangles[ROLL] ) - client->ps.delta_angles[ROLL];
00315 
00316         if ( exact && trap_ICARUS_TaskIDPending( NPC, TID_ANGLE_FACE ) )
00317         {
00318                 trap_ICARUS_TaskIDComplete( NPC, TID_ANGLE_FACE );
00319         }
00320         return exact;
00321 
00322 #else
00323 
00324         float           error;
00325         float           decay;
00326         float           targetPitch = 0;
00327         float           targetYaw = 0;
00328         float           yawSpeed;
00329         //float         runningMod = NPCInfo->currentSpeed/100.0f;
00330         qboolean        exact = qtrue;
00331         qboolean        doSound = qfalse;
00332 
00333         // if angle changes are locked; just keep the current angles
00334         if ( level.time < NPCInfo->aimTime ) 
00335         {
00336                 if(doPitch)
00337                         targetPitch = NPCInfo->lockedDesiredPitch;
00338                 if(doYaw)
00339                         targetYaw = NPCInfo->lockedDesiredYaw;
00340         }
00341         else 
00342         {
00343                 if(doPitch)
00344                         targetPitch = NPCInfo->desiredPitch;
00345                 if(doYaw)
00346                         targetYaw = NPCInfo->desiredYaw;
00347 
00348 //              NPCInfo->aimTime = level.time + 250;
00349                 if(doPitch)
00350                         NPCInfo->lockedDesiredPitch = NPCInfo->desiredPitch;
00351                 if(doYaw)
00352                         NPCInfo->lockedDesiredYaw = NPCInfo->desiredYaw;
00353         }
00354 
00355         yawSpeed = NPCInfo->stats.yawSpeed;
00356 
00357         if(doYaw)
00358         {
00359                 // decay yaw error
00360                 error = AngleDelta ( NPC->client->ps.viewangles[YAW], targetYaw );
00361                 if( fabs(error) > MIN_ANGLE_ERROR )
00362                 {
00363                         /*
00364                         if(NPC->client->playerTeam == TEAM_BORG&&
00365                                 NPCInfo->behaviorState != BS_FACE&&NPCInfo->tempBehavior!= BS_FACE)
00366                         {//HACK - borg turn more jittery
00367                                 if ( error ) 
00368                                 {
00369                                         exact = qfalse;
00370 
00371                                         decay = 60.0 + yawSpeed * 3;
00372                                         decay *= 50.0 / 1000.0;//msec
00373                                         //Snap to
00374                                         if(fabs(error) > 10)
00375                                         {
00376                                                 if(random() > 0.6)
00377                                                 {
00378                                                         doSound = qtrue;
00379                                                 }
00380                                         }
00381 
00382                                         if ( error < 0.0)//-10.0 ) 
00383                                         {
00384                                                 error += decay;
00385                                                 if ( error > 0.0 ) 
00386                                                 {
00387                                                         error = 0.0;
00388                                                 }
00389                                         }
00390                                         else if ( error > 0.0)//10.0 ) 
00391                                         {
00392                                                 error -= decay;
00393                                                 if ( error < 0.0 ) 
00394                                                 {
00395                                                         error = 0.0;
00396                                                 }
00397                                         }
00398                                 }
00399                         }
00400                         else*/
00401                         
00402                         if ( error ) 
00403                         {
00404                                 exact = qfalse;
00405 
00406                                 decay = 60.0 + yawSpeed * 3;
00407                                 decay *= 50.0 / 1000.0;//msec
00408 
00409                                 if ( error < 0.0 ) 
00410                                 {
00411                                         error += decay;
00412                                         if ( error > 0.0 ) 
00413                                         {
00414                                                 error = 0.0;
00415                                         }
00416                                 }
00417                                 else 
00418                                 {
00419                                         error -= decay;
00420                                         if ( error < 0.0 ) 
00421                                         {
00422                                                 error = 0.0;
00423                                         }
00424                                 }
00425                         }
00426                 }
00427                 ucmd.angles[YAW] = ANGLE2SHORT( targetYaw + error ) - client->ps.delta_angles[YAW];
00428         }
00429 
00430         //FIXME: have a pitchSpeed?
00431         if(doPitch)
00432         {
00433                 // decay pitch error
00434                 error = AngleDelta ( NPC->client->ps.viewangles[PITCH], targetPitch );
00435                 if ( fabs(error) > MIN_ANGLE_ERROR )
00436                 {
00437                         /*
00438                         if(NPC->client->playerTeam == TEAM_BORG&&
00439                                 NPCInfo->behaviorState != BS_FACE&&NPCInfo->tempBehavior!= BS_FACE)
00440                         {//HACK - borg turn more jittery
00441                                 if ( error ) 
00442                                 {
00443                                         exact = qfalse;
00444 
00445                                         decay = 60.0 + yawSpeed * 3;
00446                                         decay *= 50.0 / 1000.0;//msec
00447                                         //Snap to
00448                                         if(fabs(error) > 10)
00449                                         {
00450                                                 if(random() > 0.6)
00451                                                 {
00452                                                         doSound = qtrue;
00453                                                 }
00454                                         }
00455 
00456                                         if ( error < 0.0)//-10.0 ) 
00457                                         {
00458                                                 error += decay;
00459                                                 if ( error > 0.0 ) 
00460                                                 {
00461                                                         error = 0.0;
00462                                                 }
00463                                         }
00464                                         else if ( error > 0.0)//10.0 ) 
00465                                         {
00466                                                 error -= decay;
00467                                                 if ( error < 0.0 ) 
00468                                                 {
00469                                                         error = 0.0;
00470                                                 }
00471                                         }
00472                                 }
00473                         }
00474                         else*/
00475                         
00476                         if ( error ) 
00477                         {
00478                                 exact = qfalse;
00479 
00480                                 decay = 60.0 + yawSpeed * 3;
00481                                 decay *= 50.0 / 1000.0;//msec
00482 
00483                                 if ( error < 0.0 ) 
00484                                 {
00485                                         error += decay;
00486                                         if ( error > 0.0 ) 
00487                                         {
00488                                                 error = 0.0;
00489                                         }
00490                                 }
00491                                 else 
00492                                 {
00493                                         error -= decay;
00494                                         if ( error < 0.0 ) 
00495                                         {
00496                                                 error = 0.0;
00497                                         }
00498                                 }
00499                         }
00500                 }
00501                 ucmd.angles[PITCH] = ANGLE2SHORT( targetPitch + error ) - client->ps.delta_angles[PITCH];
00502         }
00503 
00504         ucmd.angles[ROLL] = ANGLE2SHORT ( NPC->client->ps.viewangles[ROLL] ) - client->ps.delta_angles[ROLL];
00505 
00506         /*
00507         if(doSound)
00508         {
00509                 G_Sound(NPC, G_SoundIndex(va("sound/enemies/borg/borgservo%d.wav", Q_irand(1, 8))));
00510         }
00511         */
00512 
00513         return exact;
00514 
00515 #endif
00516 
00517 }
00518 
00519 void NPC_AimWiggle( vec3_t enemy_org )
00520 {
00521         //shoot for somewhere between the head and torso
00522         //NOTE: yes, I know this looks weird, but it works
00523         if ( NPCInfo->aimErrorDebounceTime < level.time )
00524         {
00525                 NPCInfo->aimOfs[0] = 0.3*flrand(NPC->enemy->r.mins[0], NPC->enemy->r.maxs[0]);
00526                 NPCInfo->aimOfs[1] = 0.3*flrand(NPC->enemy->r.mins[1], NPC->enemy->r.maxs[1]);
00527                 if ( NPC->enemy->r.maxs[2] > 0 )
00528                 {
00529                         NPCInfo->aimOfs[2] = NPC->enemy->r.maxs[2]*flrand(0.0f, -1.0f);
00530                 }
00531         }
00532         VectorAdd( enemy_org, NPCInfo->aimOfs, enemy_org );
00533 }
00534 
00535 /*
00536 qboolean NPC_UpdateFiringAngles ( qboolean doPitch, qboolean doYaw ) 
00537 
00538   Includes aim when determining angles - so they don't always hit...
00539   */
00540 qboolean NPC_UpdateFiringAngles ( qboolean doPitch, qboolean doYaw ) 
00541 {
00542 
00543 #if 0
00544 
00545         float           diff;
00546         float           error;
00547         float           targetPitch = 0;
00548         float           targetYaw = 0;
00549         qboolean        exact = qtrue;
00550 
00551         if ( level.time < NPCInfo->aimTime ) 
00552         {
00553                 if( doPitch )
00554                         targetPitch = NPCInfo->lockedDesiredPitch;
00555 
00556                 if( doYaw )
00557                         targetYaw = NPCInfo->lockedDesiredYaw;
00558         }
00559         else 
00560         {
00561                 if( doPitch )
00562                 {
00563                         targetPitch = NPCInfo->desiredPitch;
00564                         NPCInfo->lockedDesiredPitch = NPCInfo->desiredPitch;
00565                 }
00566 
00567                 if( doYaw )
00568                 {
00569                         targetYaw = NPCInfo->desiredYaw;
00570                         NPCInfo->lockedDesiredYaw = NPCInfo->desiredYaw;
00571                 }                       
00572         }
00573 
00574         if( doYaw )
00575         {
00576                 // add yaw error based on NPCInfo->aim value
00577                 error = ((float)(6 - NPCInfo->stats.aim)) * flrand(-1, 1);
00578 
00579                 if(Q_irand(0, 1))
00580                         error *= -1;
00581 
00582                 diff = AngleDelta ( NPC->client->ps.viewangles[YAW], targetYaw );
00583 
00584                 if ( diff )
00585                         exact = qfalse;
00586                 
00587                 ucmd.angles[YAW] = ANGLE2SHORT( targetYaw + diff + error ) - client->ps.delta_angles[YAW];
00588         }
00589 
00590         if( doPitch )
00591         {
00592                 // add pitch error based on NPCInfo->aim value
00593                 error = ((float)(6 - NPCInfo->stats.aim)) * flrand(-1, 1);
00594 
00595                 diff = AngleDelta ( NPC->client->ps.viewangles[PITCH], targetPitch );
00596 
00597                 if ( diff )
00598                         exact = qfalse;
00599                 
00600                 ucmd.angles[PITCH] = ANGLE2SHORT( targetPitch + diff + error ) - client->ps.delta_angles[PITCH];
00601         }
00602 
00603         ucmd.angles[ROLL] = ANGLE2SHORT ( NPC->client->ps.viewangles[ROLL] ) - client->ps.delta_angles[ROLL];
00604 
00605         return exact;
00606         
00607 #else
00608 
00609         float           error, diff;
00610         float           decay;
00611         float           targetPitch = 0;
00612         float           targetYaw = 0;
00613         qboolean        exact = qtrue;
00614 
00615         // if angle changes are locked; just keep the current angles
00616         if ( level.time < NPCInfo->aimTime ) 
00617         {
00618                 if(doPitch)
00619                         targetPitch = NPCInfo->lockedDesiredPitch;
00620                 if(doYaw)
00621                         targetYaw = NPCInfo->lockedDesiredYaw;
00622         }
00623         else 
00624         {
00625                 if(doPitch)
00626                         targetPitch = NPCInfo->desiredPitch;
00627                 if(doYaw)
00628                         targetYaw = NPCInfo->desiredYaw;
00629 
00630 //              NPCInfo->aimTime = level.time + 250;
00631                 if(doPitch)
00632                         NPCInfo->lockedDesiredPitch = NPCInfo->desiredPitch;
00633                 if(doYaw)
00634                         NPCInfo->lockedDesiredYaw = NPCInfo->desiredYaw;
00635         }
00636 
00637         if ( NPCInfo->aimErrorDebounceTime < level.time )
00638         {
00639                 if ( Q_irand(0, 1 ) )
00640                 {
00641                         NPCInfo->lastAimErrorYaw = ((float)(6 - NPCInfo->stats.aim)) * flrand(-1, 1);
00642                 }
00643                 if ( Q_irand(0, 1 ) )
00644                 {
00645                         NPCInfo->lastAimErrorPitch = ((float)(6 - NPCInfo->stats.aim)) * flrand(-1, 1);
00646                 }
00647                 NPCInfo->aimErrorDebounceTime = level.time + Q_irand(250, 2000);
00648         }
00649 
00650         if(doYaw)
00651         {
00652                 // decay yaw diff
00653                 diff = AngleDelta ( NPC->client->ps.viewangles[YAW], targetYaw );
00654                 
00655                 if ( diff) 
00656                 {
00657                         exact = qfalse;
00658 
00659                         decay = 60.0 + 80.0;
00660                         decay *= 50.0f / 1000.0f;//msec
00661                         if ( diff < 0.0 ) 
00662                         {
00663                                 diff += decay;
00664                                 if ( diff > 0.0 ) 
00665                                 {
00666                                         diff = 0.0;
00667                                 }
00668                         }
00669                         else 
00670                         {
00671                                 diff -= decay;
00672                                 if ( diff < 0.0 ) 
00673                                 {
00674                                         diff = 0.0;
00675                                 }
00676                         }
00677                 }
00678                                 
00679                 // add yaw error based on NPCInfo->aim value
00680                 error = NPCInfo->lastAimErrorYaw;
00681 
00682                 /*
00683                 if(Q_irand(0, 1))
00684                 {
00685                         error *= -1;
00686                 }
00687                 */
00688 
00689                 ucmd.angles[YAW] = ANGLE2SHORT( targetYaw + diff + error ) - client->ps.delta_angles[YAW];
00690         }
00691 
00692         if(doPitch)
00693         {
00694                 // decay pitch diff
00695                 diff = AngleDelta ( NPC->client->ps.viewangles[PITCH], targetPitch );
00696                 if ( diff) 
00697                 {
00698                         exact = qfalse;
00699 
00700                         decay = 60.0 + 80.0;
00701                         decay *= 50.0f / 1000.0f;//msec
00702                         if ( diff < 0.0 ) 
00703                         {
00704                                 diff += decay;
00705                                 if ( diff > 0.0 ) 
00706                                 {
00707                                         diff = 0.0;
00708                                 }
00709                         }
00710                         else 
00711                         {
00712                                 diff -= decay;
00713                                 if ( diff < 0.0 ) 
00714                                 {
00715                                         diff = 0.0;
00716                                 }
00717                         }
00718                 }
00719                 
00720                 error = NPCInfo->lastAimErrorPitch;
00721 
00722                 ucmd.angles[PITCH] = ANGLE2SHORT( targetPitch + diff + error ) - client->ps.delta_angles[PITCH];
00723         }
00724 
00725         ucmd.angles[ROLL] = ANGLE2SHORT ( NPC->client->ps.viewangles[ROLL] ) - client->ps.delta_angles[ROLL];
00726 
00727         return exact;
00728 
00729 #endif
00730 
00731 }
00732 //===================================================================================
00733 
00734 /*
00735 static void NPC_UpdateShootAngles (vec3_t angles, qboolean doPitch, qboolean doYaw ) 
00736 
00737 Does update angles on shootAngles
00738 */
00739 
00740 void NPC_UpdateShootAngles (vec3_t angles, qboolean doPitch, qboolean doYaw ) 
00741 {//FIXME: shoot angles either not set right or not used!
00742         float           error;
00743         float           decay;
00744         float           targetPitch = 0;
00745         float           targetYaw = 0;
00746 
00747         if(doPitch)
00748                 targetPitch = angles[PITCH];
00749         if(doYaw)
00750                 targetYaw = angles[YAW];
00751 
00752 
00753         if(doYaw)
00754         {
00755                 // decay yaw error
00756                 error = AngleDelta ( NPCInfo->shootAngles[YAW], targetYaw );
00757                 if ( error ) 
00758                 {
00759                         decay = 60.0 + 80.0 * NPCInfo->stats.aim;
00760                         decay *= 100.0f / 1000.0f;//msec
00761                         if ( error < 0.0 ) 
00762                         {
00763                                 error += decay;
00764                                 if ( error > 0.0 ) 
00765                                 {
00766                                         error = 0.0;
00767                                 }
00768                         }
00769                         else 
00770                         {
00771                                 error -= decay;
00772                                 if ( error < 0.0 ) 
00773                                 {
00774                                         error = 0.0;
00775                                 }
00776                         }
00777                 }
00778                 NPCInfo->shootAngles[YAW] = targetYaw + error;
00779         }
00780 
00781         if(doPitch)
00782         {
00783                 // decay pitch error
00784                 error = AngleDelta ( NPCInfo->shootAngles[PITCH], targetPitch );
00785                 if ( error ) 
00786                 {
00787                         decay = 60.0 + 80.0 * NPCInfo->stats.aim;
00788                         decay *= 100.0f / 1000.0f;//msec
00789                         if ( error < 0.0 ) 
00790                         {
00791                                 error += decay;
00792                                 if ( error > 0.0 ) 
00793                                 {
00794                                         error = 0.0;
00795                                 }
00796                         }
00797                         else 
00798                         {
00799                                 error -= decay;
00800                                 if ( error < 0.0 ) 
00801                                 {
00802                                         error = 0.0;
00803                                 }
00804                         }
00805                 }
00806                 NPCInfo->shootAngles[PITCH] = targetPitch + error;
00807         }
00808 }
00809 
00810 /*
00811 void SetTeamNumbers (void)
00812 
00813 Sets the number of living clients on each team
00814 
00815 FIXME: Does not account for non-respawned players!
00816 FIXME: Don't include medics?
00817 */
00818 void SetTeamNumbers (void)
00819 {
00820         gentity_t       *found;
00821         int                     i;
00822 
00823         for( i = 0; i < TEAM_NUM_TEAMS; i++ )
00824         {
00825                 teamNumbers[i] = 0;
00826                 teamStrength[i] = 0;
00827         }
00828 
00829         for( i = 0; i < 1 ; i++ )
00830         {
00831                 found = &g_entities[i];
00832 
00833                 if( found->client )
00834                 {
00835                         if( found->health > 0 )//FIXME: or if a player!
00836                         {
00837                                 teamNumbers[found->client->playerTeam]++;
00838                                 teamStrength[found->client->playerTeam] += found->health;
00839                         }
00840                 }
00841         }
00842 
00843         for( i = 0; i < TEAM_NUM_TEAMS; i++ )
00844         {//Get the average health
00845                 teamStrength[i] = floor( ((float)(teamStrength[i])) / ((float)(teamNumbers[i])) );
00846         }
00847 }
00848 
00849 extern stringID_table_t BSTable[];
00850 extern stringID_table_t BSETTable[];
00851 qboolean G_ActivateBehavior (gentity_t *self, int bset )
00852 {
00853         bState_t        bSID = (bState_t)-1;
00854         char *bs_name = NULL;
00855 
00856         if ( !self )
00857         {
00858                 return qfalse;
00859         }
00860 
00861         bs_name = self->behaviorSet[bset];
00862         
00863         if( !(VALIDSTRING( bs_name )) )
00864         {
00865                 return qfalse;
00866         }
00867 
00868         if ( self->NPC )
00869         {
00870                 bSID = (bState_t)(GetIDForString( BSTable, bs_name ));
00871         }
00872 
00873         if(bSID > -1)
00874         {
00875                 self->NPC->tempBehavior = BS_DEFAULT;
00876                 self->NPC->behaviorState = bSID;
00877         }
00878         else
00879         {
00880                 /*
00881                 char                    newname[MAX_FILENAME_LENGTH];           
00882                 sprintf((char *) &newname, "%s/%s", Q3_SCRIPT_DIR, bs_name );
00883                 */
00884                 
00885                 //FIXME: between here and actually getting into the ICARUS_RunScript function, the stack gets blown!
00886                 //if ( ( ICARUS_entFilter == -1 ) || ( ICARUS_entFilter == self->s.number ) )
00887                 if (0)
00888                 {
00889                         G_DebugPrint( WL_VERBOSE, "%s attempting to run bSet %s (%s)\n", self->targetname, GetStringForID( BSETTable, bset ), bs_name );
00890                 }
00891                 trap_ICARUS_RunScript( self, va( "%s/%s", Q3_SCRIPT_DIR, bs_name ) );
00892         }
00893         return qtrue;
00894 }
00895 
00896 
00897 /*
00898 =============================================================================
00899 
00900         Extended Functions
00901 
00902 =============================================================================
00903 */
00904 
00905 //rww - special system for sync'ing bone angles between client and server.
00906 void NPC_SetBoneAngles(gentity_t *ent, char *bone, vec3_t angles)
00907 {
00908 #ifdef _XBOX
00909         byte *thebone = &ent->s.boneIndex1;
00910         byte *firstFree = NULL;
00911 #else
00912         int *thebone = &ent->s.boneIndex1;
00913         int *firstFree = NULL;
00914 #endif
00915         int i = 0;
00916         int boneIndex = G_BoneIndex(bone);
00917         int flags, up, right, forward;
00918         vec3_t *boneVector = &ent->s.boneAngles1;
00919         vec3_t *freeBoneVec = NULL;
00920 
00921         while (thebone)
00922         {
00923                 if (!*thebone && !firstFree)
00924                 { //if the value is 0 then this index is clear, we can use it if we don't find the bone we want already existing.
00925                         firstFree = thebone;
00926                         freeBoneVec = boneVector;
00927                 }
00928                 else if (*thebone)
00929                 {
00930                         if (*thebone == boneIndex)
00931                         { //this is it
00932                                 break;
00933                         }
00934                 }
00935 
00936                 switch (i)
00937                 {
00938                 case 0:
00939                         thebone = &ent->s.boneIndex2;
00940                         boneVector = &ent->s.boneAngles2;
00941                         break;
00942                 case 1:
00943                         thebone = &ent->s.boneIndex3;
00944                         boneVector = &ent->s.boneAngles3;
00945                         break;
00946                 case 2:
00947                         thebone = &ent->s.boneIndex4;
00948                         boneVector = &ent->s.boneAngles4;
00949                         break;
00950                 default:
00951                         thebone = NULL;
00952                         boneVector = NULL;
00953                         break;
00954                 }
00955 
00956                 i++;
00957         }
00958 
00959         if (!thebone)
00960         { //didn't find it, create it
00961                 if (!firstFree)
00962                 { //no free bones.. can't do a thing then.
00963                         Com_Printf("WARNING: NPC has no free bone indexes\n");
00964                         return;
00965                 }
00966 
00967                 thebone = firstFree;
00968 
00969                 *thebone = boneIndex;
00970                 boneVector = freeBoneVec;
00971         }
00972 
00973         //If we got here then we have a vector and an index.
00974 
00975         //Copy the angles over the vector in the entitystate, so we can use the corresponding index
00976         //to set the bone angles on the client.
00977         VectorCopy(angles, *boneVector);
00978 
00979         //Now set the angles on our server instance if we have one.
00980 
00981         if (!ent->ghoul2)
00982         {
00983                 return;
00984         }
00985 
00986         flags = BONE_ANGLES_POSTMULT;
00987         up = POSITIVE_X;
00988         right = NEGATIVE_Y;
00989         forward = NEGATIVE_Z;
00990 
00991         //first 3 bits is forward, second 3 bits is right, third 3 bits is up
00992         ent->s.boneOrient = ((forward)|(right<<3)|(up<<6));
00993 
00994         trap_G2API_SetBoneAngles(ent->ghoul2, 0, bone, angles, flags, up, right, forward, NULL, 100, level.time);
00995 }
00996 
00997 //rww - and another method of automatically managing surface status for the client and server at once
00998 #define TURN_ON                         0x00000000
00999 #define TURN_OFF                        0x00000100
01000 
01001 void NPC_SetSurfaceOnOff(gentity_t *ent, const char *surfaceName, int surfaceFlags)
01002 {
01003         int i = 0;
01004         qboolean foundIt = qfalse;
01005 
01006         while (i < BG_NUM_TOGGLEABLE_SURFACES && bgToggleableSurfaces[i])
01007         {
01008                 if (!Q_stricmp(surfaceName, bgToggleableSurfaces[i]))
01009                 { //got it
01010                         foundIt = qtrue;
01011                         break;
01012                 }
01013                 i++;
01014         }
01015 
01016         if (!foundIt)
01017         {
01018                 Com_Printf("WARNING: Tried to toggle NPC surface that isn't in toggleable surface list (%s)\n", surfaceName);
01019                 return;
01020         }
01021 
01022         if (surfaceFlags == TURN_ON)
01023         { //Make sure the entitystate values reflect this surface as on now.
01024                 ent->s.surfacesOn |= (1 << i);
01025                 ent->s.surfacesOff &= ~(1 << i);
01026         }
01027         else
01028         { //Otherwise make sure they're off.
01029                 ent->s.surfacesOn &= ~(1 << i);
01030                 ent->s.surfacesOff |= (1 << i);
01031         }
01032 
01033         if (!ent->ghoul2)
01034         {
01035                 return;
01036         }
01037 
01038         trap_G2API_SetSurfaceOnOff(ent->ghoul2, surfaceName, surfaceFlags);
01039 }
01040 
01041 //rww - cheap check to see if an armed client is looking in our general direction
01042 qboolean NPC_SomeoneLookingAtMe(gentity_t *ent)
01043 {
01044         int i = 0;
01045         gentity_t *pEnt;
01046 
01047         while (i < MAX_CLIENTS)
01048         {
01049                 pEnt = &g_entities[i];
01050 
01051                 if (pEnt && pEnt->inuse && pEnt->client && pEnt->client->sess.sessionTeam != TEAM_SPECTATOR &&
01052                         !(pEnt->client->ps.pm_flags & PMF_FOLLOW) && pEnt->s.weapon != WP_NONE)
01053                 {
01054                         if (trap_InPVS(ent->r.currentOrigin, pEnt->r.currentOrigin))
01055                         {
01056                                 if (InFOV( ent, pEnt, 30, 30 ))
01057                                 { //I'm in a 30 fov or so cone from this player.. that's enough I guess.
01058                                         return qtrue;
01059                                 }
01060                         }
01061                 }
01062 
01063                 i++;
01064         }
01065 
01066         return qfalse;
01067 }
01068 
01069 qboolean NPC_ClearLOS( const vec3_t start, const vec3_t end )
01070 {
01071         return G_ClearLOS( NPC, start, end );
01072 }
01073 qboolean NPC_ClearLOS5( const vec3_t end )
01074 {
01075         return G_ClearLOS5( NPC, end );
01076 }
01077 qboolean NPC_ClearLOS4( gentity_t *ent ) 
01078 {
01079         return G_ClearLOS4( NPC, ent );
01080 }
01081 qboolean NPC_ClearLOS3( const vec3_t start, gentity_t *ent )
01082 {
01083         return G_ClearLOS3( NPC, start, ent );
01084 }
01085 qboolean NPC_ClearLOS2( gentity_t *ent, const vec3_t end )
01086 {
01087         return G_ClearLOS2( NPC, ent, end );
01088 }
01089 
01090 /*
01091 -------------------------
01092 NPC_ValidEnemy
01093 -------------------------
01094 */
01095 
01096 qboolean NPC_ValidEnemy( gentity_t *ent )
01097 {
01098         int entTeam = TEAM_FREE;
01099         //Must be a valid pointer
01100         if ( ent == NULL )
01101                 return qfalse;
01102 
01103         //Must not be me
01104         if ( ent == NPC )
01105                 return qfalse;
01106 
01107         //Must not be deleted
01108         if ( ent->inuse == qfalse )
01109                 return qfalse;
01110 
01111         //Must be alive
01112         if ( ent->health <= 0 )
01113                 return qfalse;
01114 
01115         //In case they're in notarget mode
01116         if ( ent->flags & FL_NOTARGET )
01117                 return qfalse;
01118 
01119         //Must be an NPC
01120         if ( ent->client == NULL )
01121         {
01122         //      if ( ent->svFlags&SVF_NONNPC_ENEMY )
01123                 if (ent->s.eType != ET_NPC)
01124                 {//still potentially valid
01125                         if ( ent->alliedTeam == NPC->client->playerTeam )
01126                         {
01127                                 return qfalse;
01128                         }
01129                         else
01130                         {
01131                                 return qtrue;
01132                         }
01133                 }
01134                 else
01135                 {
01136                         return qfalse;
01137                 }
01138         }
01139         else if ( ent->client && ent->client->sess.sessionTeam == TEAM_SPECTATOR )
01140         {//don't go after spectators
01141                 return qfalse;
01142         }
01143         if ( ent->NPC && ent->client )
01144         {
01145                 entTeam = ent->client->playerTeam;
01146         }
01147         else if ( ent->client )
01148         {
01149                 if (g_gametype.integer < GT_TEAM)
01150                 {
01151                         entTeam = NPCTEAM_PLAYER;
01152                 }
01153                 else
01154                 {
01155                         if ( ent->client->sess.sessionTeam == TEAM_BLUE )
01156                         {
01157                                 entTeam = NPCTEAM_PLAYER;
01158                         }
01159                         else if ( ent->client->sess.sessionTeam == TEAM_RED )
01160                         {
01161                                 entTeam = NPCTEAM_ENEMY;
01162                         }
01163                         else
01164                         {
01165                                 entTeam = NPCTEAM_NEUTRAL;
01166                         }
01167                 }
01168         }
01169         //Can't be on the same team
01170         if ( ent->client->playerTeam == NPC->client->playerTeam )
01171                 return qfalse;
01172 
01173         //if haven't seen him in a while, give up
01174         //if ( NPCInfo->enemyLastSeenTime != 0 && level.time - NPCInfo->enemyLastSeenTime > 7000 )//FIXME: make a stat?
01175         //      return qfalse;
01176         if ( entTeam == NPC->client->enemyTeam //simplest case: they're on my enemy team
01177                 || (NPC->client->enemyTeam == NPCTEAM_FREE && ent->client->NPC_class != NPC->client->NPC_class )//I get mad at anyone and this guy isn't the same class as me
01178                 || (ent->client->NPC_class == CLASS_WAMPA && ent->enemy )//a rampaging wampa
01179                 || (ent->client->NPC_class == CLASS_RANCOR && ent->enemy )//a rampaging rancor
01180                 || (entTeam == NPCTEAM_FREE && ent->client->enemyTeam == NPCTEAM_FREE && ent->enemy && ent->enemy->client && (ent->enemy->client->playerTeam == NPC->client->playerTeam||(ent->enemy->client->playerTeam != NPCTEAM_ENEMY&&NPC->client->playerTeam==NPCTEAM_PLAYER))) //enemy is a rampaging non-aligned creature who is attacking someone on our team or a non-enemy (this last condition is used only if we're a good guy - in effect, we protect the innocent)
01181                 )
01182         {
01183                 return qtrue;
01184         }
01185 
01186         return qfalse;
01187 }
01188 
01189 /*
01190 -------------------------
01191 NPC_TargetVisible
01192 -------------------------
01193 */
01194 
01195 qboolean NPC_TargetVisible( gentity_t *ent )
01196 {
01197         //Make sure we're in a valid range
01198         if ( DistanceSquared( ent->r.currentOrigin, NPC->r.currentOrigin ) > ( NPCInfo->stats.visrange * NPCInfo->stats.visrange ) )
01199                 return qfalse;
01200 
01201         //Check our FOV
01202         if ( InFOV( ent, NPC, NPCInfo->stats.hfov, NPCInfo->stats.vfov ) == qfalse )
01203                 return qfalse;
01204 
01205         //Check for sight
01206         if ( NPC_ClearLOS4( ent ) == qfalse )
01207                 return qfalse;
01208 
01209         return qtrue;
01210 }
01211 
01212 /*
01213 -------------------------
01214 NPC_GetCheckDelta
01215 -------------------------
01216 */
01217 
01218 /*
01219 #define CHECK_TIME_BASE                         250
01220 #define CHECK_TIME_BASE_SQUARED         ( CHECK_TIME_BASE * CHECK_TIME_BASE )
01221 
01222 static int NPC_GetCheckDelta( void )
01223 {
01224         if ( NPC_ValidEnemy( NPC->enemy ) == qfalse )
01225         {
01226                 int distance = DistanceSquared( NPC->r.currentOrigin, g_entities[0].currentOrigin );
01227 
01228                 distance /= CHECK_TIME_BASE_SQUARED;
01229 
01230                 return ( CHECK_TIME_BASE * distance );
01231         }
01232 
01233         return 0;
01234 }
01235 */
01236 
01237 /*
01238 -------------------------
01239 NPC_FindNearestEnemy
01240 -------------------------
01241 */
01242 
01243 #define MAX_RADIUS_ENTS                 256     //NOTE: This can cause entities to be lost
01244 #define NEAR_DEFAULT_RADIUS             256
01245 
01246 int NPC_FindNearestEnemy( gentity_t *ent )
01247 {
01248         int                     iradiusEnts[ MAX_RADIUS_ENTS ];
01249         gentity_t       *radEnt;
01250         vec3_t          mins, maxs;
01251         int                     nearestEntID = -1;
01252         float           nearestDist = (float)WORLD_SIZE*(float)WORLD_SIZE;
01253         float           distance;
01254         int                     numEnts, numChecks = 0;
01255         int                     i;
01256 
01257         //Setup the bbox to search in
01258         for ( i = 0; i < 3; i++ )
01259         {
01260                 mins[i] = ent->r.currentOrigin[i] - NPCInfo->stats.visrange;
01261                 maxs[i] = ent->r.currentOrigin[i] + NPCInfo->stats.visrange;
01262         }
01263 
01264         //Get a number of entities in a given space
01265         numEnts = trap_EntitiesInBox( mins, maxs, iradiusEnts, MAX_RADIUS_ENTS );
01266 
01267         for ( i = 0; i < numEnts; i++ )
01268         {
01269                 radEnt = &g_entities[iradiusEnts[i]];
01270                 //Don't consider self
01271                 if ( radEnt == ent )
01272                         continue;
01273 
01274                 //Must be valid
01275                 if ( NPC_ValidEnemy( radEnt ) == qfalse )
01276                         continue;
01277 
01278                 numChecks++;
01279                 //Must be visible
01280                 if ( NPC_TargetVisible( radEnt ) == qfalse )
01281                         continue;
01282 
01283                 distance = DistanceSquared( ent->r.currentOrigin, radEnt->r.currentOrigin );
01284 
01285                 //Found one closer to us
01286                 if ( distance < nearestDist )
01287                 {
01288                         nearestEntID = radEnt->s.number;
01289                         nearestDist = distance;
01290                 }
01291         }
01292 
01293         return nearestEntID;
01294 }
01295 
01296 /*
01297 -------------------------
01298 NPC_PickEnemyExt
01299 -------------------------
01300 */
01301 
01302 gentity_t *NPC_PickEnemyExt( qboolean checkAlerts )
01303 {
01304         //Check for Hazard Team status and remove this check
01305         /*
01306         if ( NPC->client->playerTeam != TEAM_STARFLEET )
01307         {
01308                 //If we've found the player, return it
01309                 if ( NPC_FindPlayer() )
01310                         return &g_entities[0];
01311         }
01312         */
01313 
01314         //If we've asked for the closest enemy
01315         int entID = NPC_FindNearestEnemy( NPC );
01316 
01317         //If we have a valid enemy, use it
01318         if ( entID >= 0 )
01319                 return &g_entities[entID];
01320 
01321         if ( checkAlerts )
01322         {
01323                 int alertEvent = NPC_CheckAlertEvents( qtrue, qtrue, -1, qtrue, AEL_DISCOVERED );
01324 
01325                 //There is an event to look at
01326                 if ( alertEvent >= 0 )
01327                 {
01328                         alertEvent_t *event = &level.alertEvents[alertEvent];
01329 
01330                         //Don't pay attention to our own alerts
01331                         if ( event->owner == NPC )
01332                                 return NULL;
01333 
01334                         if ( event->level >= AEL_DISCOVERED )
01335                         {
01336                                 //If it's the player, attack him
01337                                 if ( event->owner == &g_entities[0] )
01338                                         return event->owner;
01339 
01340                                 //If it's on our team, then take its enemy as well
01341                                 if ( ( event->owner->client ) && ( event->owner->client->playerTeam == NPC->client->playerTeam ) )
01342                                         return event->owner->enemy;
01343                         }
01344                 }
01345         }
01346 
01347         return NULL;
01348 }
01349 
01350 /*
01351 -------------------------
01352 NPC_FindPlayer
01353 -------------------------
01354 */
01355 
01356 qboolean NPC_FindPlayer( void )
01357 {
01358         return NPC_TargetVisible( &g_entities[0] );
01359 }
01360 
01361 /*
01362 -------------------------
01363 NPC_CheckPlayerDistance
01364 -------------------------
01365 */
01366 
01367 static qboolean NPC_CheckPlayerDistance( void )
01368 {
01369         return qfalse;//MOOT in MP
01370         /*
01371         float distance;
01372 
01373         //Make sure we have an enemy
01374         if ( NPC->enemy == NULL )
01375                 return qfalse;
01376 
01377         //Only do this for non-players
01378         if ( NPC->enemy->s.number == 0 )
01379                 return qfalse;
01380 
01381         //must be set up to get mad at player
01382         if ( !NPC->client || NPC->client->enemyTeam != NPCTEAM_PLAYER )
01383                 return qfalse;
01384 
01385         //Must be within our FOV
01386         if ( InFOV( &g_entities[0], NPC, NPCInfo->stats.hfov, NPCInfo->stats.vfov ) == qfalse )
01387                 return qfalse;
01388 
01389         distance = DistanceSquared( NPC->r.currentOrigin, NPC->enemy->r.currentOrigin );
01390 
01391         if ( distance > DistanceSquared( NPC->r.currentOrigin, g_entities[0].r.currentOrigin ) )
01392         { //rwwFIXMEFIXME: care about all clients not just client 0
01393                 G_SetEnemy( NPC, &g_entities[0] );
01394                 return qtrue;
01395         }
01396 
01397         return qfalse;
01398         */
01399 }
01400 
01401 /*
01402 -------------------------
01403 NPC_FindEnemy
01404 -------------------------
01405 */
01406 
01407 qboolean NPC_FindEnemy( qboolean checkAlerts )
01408 {
01409         gentity_t *newenemy;
01410 
01411         //We're ignoring all enemies for now
01412         //if( NPC->svFlags & SVF_IGNORE_ENEMIES )
01413         if (0) //rwwFIXMEFIXME: support for flag
01414         {
01415                 G_ClearEnemy( NPC );
01416                 return qfalse;
01417         }
01418 
01419         //we can't pick up any enemies for now
01420         if( NPCInfo->confusionTime > level.time )
01421         {
01422                 return qfalse;
01423         }
01424 
01425         //Don't want a new enemy
01426         //rwwFIXMEFIXME: support for locked enemy
01427         //if ( ( ValidEnemy( NPC->enemy ) ) && ( NPC->svFlags & SVF_LOCKEDENEMY ) )
01428         //      return qtrue;
01429 
01430         //See if the player is closer than our current enemy
01431         if ( NPC_CheckPlayerDistance() )
01432         {
01433                 return qtrue;
01434         }
01435 
01436         //Otherwise, turn off the flag
01437 //      NPC->svFlags &= ~SVF_LOCKEDENEMY;
01438         //See if the player is closer than our current enemy
01439         if ( NPC->client->NPC_class != CLASS_RANCOR 
01440                 && NPC->client->NPC_class != CLASS_WAMPA
01441                 //&& NPC->client->NPC_class != CLASS_SAND_CREATURE
01442                 && NPC_CheckPlayerDistance() )
01443         {//rancors, wampas & sand creatures don't care if player is closer, they always go with closest
01444                 return qtrue;
01445         }
01446 
01447         //If we've gotten here alright, then our target it still valid
01448         if ( NPC_ValidEnemy( NPC->enemy ) )
01449                 return qtrue;
01450 
01451         newenemy = NPC_PickEnemyExt( checkAlerts );
01452 
01453         //if we found one, take it as the enemy
01454         if( NPC_ValidEnemy( newenemy ) )
01455         {
01456                 G_SetEnemy( NPC, newenemy );
01457                 return qtrue;
01458         }
01459 
01460         return qfalse;
01461 }
01462 
01463 /*
01464 -------------------------
01465 NPC_CheckEnemyExt
01466 -------------------------
01467 */
01468 
01469 qboolean NPC_CheckEnemyExt( qboolean checkAlerts )
01470 {
01471         //Make sure we're ready to think again
01472 /*
01473         if ( NPCInfo->enemyCheckDebounceTime > level.time )
01474                 return qfalse;
01475 
01476         //Get our next think time
01477         NPCInfo->enemyCheckDebounceTime = level.time + NPC_GetCheckDelta();
01478 
01479         //Attempt to find an enemy
01480         return NPC_FindEnemy();
01481 */
01482         return NPC_FindEnemy( checkAlerts );
01483 }
01484 
01485 /*
01486 -------------------------
01487 NPC_FacePosition
01488 -------------------------
01489 */
01490 
01491 qboolean NPC_FacePosition( vec3_t position, qboolean doPitch )
01492 {
01493         vec3_t          muzzle;
01494         vec3_t          angles;
01495         float           yawDelta;
01496         qboolean        facing = qtrue;
01497 
01498         //Get the positions
01499         if ( NPC->client && (NPC->client->NPC_class == CLASS_RANCOR || NPC->client->NPC_class == CLASS_WAMPA) )// || NPC->client->NPC_class == CLASS_SAND_CREATURE) )
01500         {
01501                 CalcEntitySpot( NPC, SPOT_ORIGIN, muzzle );
01502                 muzzle[2] += NPC->r.maxs[2] * 0.75f;
01503         }
01504         else if ( NPC->client && NPC->client->NPC_class == CLASS_GALAKMECH )
01505         {
01506                 CalcEntitySpot( NPC, SPOT_WEAPON, muzzle );
01507         }
01508         else
01509         {
01510                 CalcEntitySpot( NPC, SPOT_HEAD_LEAN, muzzle );//SPOT_HEAD
01511         }
01512 
01513         //Find the desired angles
01514         GetAnglesForDirection( muzzle, position, angles );
01515 
01516         NPCInfo->desiredYaw             = AngleNormalize360( angles[YAW] );
01517         NPCInfo->desiredPitch   = AngleNormalize360( angles[PITCH] );
01518 
01519         if ( NPC->enemy && NPC->enemy->client && NPC->enemy->client->NPC_class == CLASS_ATST )
01520         {
01521                 // FIXME: this is kind of dumb, but it was the easiest way to get it to look sort of ok
01522                 NPCInfo->desiredYaw     += flrand( -5, 5 ) + sin( level.time * 0.004f ) * 7;
01523                 NPCInfo->desiredPitch += flrand( -2, 2 );
01524         }
01525         //Face that yaw
01526         NPC_UpdateAngles( qtrue, qtrue );
01527 
01528         //Find the delta between our goal and our current facing
01529         yawDelta = AngleNormalize360( NPCInfo->desiredYaw - ( SHORT2ANGLE( ucmd.angles[YAW] + client->ps.delta_angles[YAW] ) ) );
01530         
01531         //See if we are facing properly
01532         if ( fabs( yawDelta ) > VALID_ATTACK_CONE )
01533                 facing = qfalse;
01534 
01535         if ( doPitch )
01536         {
01537                 //Find the delta between our goal and our current facing
01538                 float currentAngles = ( SHORT2ANGLE( ucmd.angles[PITCH] + client->ps.delta_angles[PITCH] ) );
01539                 float pitchDelta = NPCInfo->desiredPitch - currentAngles;
01540                 
01541                 //See if we are facing properly
01542                 if ( fabs( pitchDelta ) > VALID_ATTACK_CONE )
01543                         facing = qfalse;
01544         }
01545 
01546         return facing;
01547 }
01548 
01549 /*
01550 -------------------------
01551 NPC_FaceEntity
01552 -------------------------
01553 */
01554 
01555 qboolean NPC_FaceEntity( gentity_t *ent, qboolean doPitch )
01556 {
01557         vec3_t          entPos;
01558 
01559         //Get the positions
01560         CalcEntitySpot( ent, SPOT_HEAD_LEAN, entPos );
01561 
01562         return NPC_FacePosition( entPos, doPitch );
01563 }
01564 
01565 /*
01566 -------------------------
01567 NPC_FaceEnemy
01568 -------------------------
01569 */
01570 
01571 qboolean NPC_FaceEnemy( qboolean doPitch )
01572 {
01573         if ( NPC == NULL )
01574                 return qfalse;
01575 
01576         if ( NPC->enemy == NULL )
01577                 return qfalse;
01578 
01579         return NPC_FaceEntity( NPC->enemy, doPitch );
01580 }
01581 
01582 /*
01583 -------------------------
01584 NPC_CheckCanAttackExt
01585 -------------------------
01586 */
01587 
01588 qboolean NPC_CheckCanAttackExt( void )
01589 {
01590         //We don't want them to shoot
01591         if( NPCInfo->scriptFlags & SCF_DONT_FIRE )
01592                 return qfalse;
01593 
01594         //Turn to face
01595         if ( NPC_FaceEnemy( qtrue ) == qfalse )
01596                 return qfalse;
01597 
01598         //Must have a clear line of sight to the target
01599         if ( NPC_ClearShot( NPC->enemy ) == qfalse )
01600                 return qfalse;
01601 
01602         return qtrue;
01603 }
01604 
01605 /*
01606 -------------------------
01607 NPC_ClearLookTarget
01608 -------------------------
01609 */
01610 
01611 void NPC_ClearLookTarget( gentity_t *self )
01612 {
01613         if ( !self->client )
01614         {
01615                 return;
01616         }
01617 
01618         if ( (self->client->ps.eFlags2&EF2_HELD_BY_MONSTER) )
01619         {//lookTarget is set by and to the monster that's holding you, no other operations can change that
01620                 return;
01621         }
01622 
01623         self->client->renderInfo.lookTarget = ENTITYNUM_NONE;//ENTITYNUM_WORLD;
01624         self->client->renderInfo.lookTargetClearTime = 0;
01625 }
01626 
01627 /*
01628 -------------------------
01629 NPC_SetLookTarget
01630 -------------------------
01631 */
01632 void NPC_SetLookTarget( gentity_t *self, int entNum, int clearTime )
01633 {
01634         if ( !self->client )
01635         {
01636                 return;
01637         }
01638 
01639         if ( (self->client->ps.eFlags2&EF2_HELD_BY_MONSTER) )
01640         {//lookTarget is set by and to the monster that's holding you, no other operations can change that
01641                 return;
01642         }
01643 
01644         self->client->renderInfo.lookTarget = entNum;
01645         self->client->renderInfo.lookTargetClearTime = clearTime;
01646 }
01647 
01648 /*
01649 -------------------------
01650 NPC_CheckLookTarget
01651 -------------------------
01652 */
01653 qboolean NPC_CheckLookTarget( gentity_t *self )
01654 {
01655         if ( self->client )
01656         {
01657                 if ( self->client->renderInfo.lookTarget >= 0 && self->client->renderInfo.lookTarget < ENTITYNUM_WORLD )
01658                 {//within valid range
01659                         if ( (&g_entities[self->client->renderInfo.lookTarget] == NULL) || !g_entities[self->client->renderInfo.lookTarget].inuse )
01660                         {//lookTarget not inuse or not valid anymore
01661                                 NPC_ClearLookTarget( self );
01662                         }
01663                         else if ( self->client->renderInfo.lookTargetClearTime && self->client->renderInfo.lookTargetClearTime < level.time )
01664                         {//Time to clear lookTarget
01665                                 NPC_ClearLookTarget( self );
01666                         }
01667                         else if ( g_entities[self->client->renderInfo.lookTarget].client && self->enemy && (&g_entities[self->client->renderInfo.lookTarget] != self->enemy) )
01668                         {//should always look at current enemy if engaged in battle... FIXME: this could override certain scripted lookTargets...???
01669                                 NPC_ClearLookTarget( self );
01670                         }
01671                         else
01672                         {
01673                                 return qtrue;
01674                         }
01675                 }
01676         }
01677 
01678         return qfalse;
01679 }
01680 
01681 /*
01682 -------------------------
01683 NPC_CheckCharmed
01684 -------------------------
01685 */
01686 extern void G_AddVoiceEvent( gentity_t *self, int event, int speakDebounceTime );
01687 void NPC_CheckCharmed( void )
01688 {
01689         if ( NPCInfo->charmedTime && NPCInfo->charmedTime < level.time && NPC->client )
01690         {//we were charmed, set us back!
01691                 NPC->client->playerTeam = NPC->genericValue1;
01692                 NPC->client->enemyTeam = NPC->genericValue2;
01693                 NPC->s.teamowner = NPC->genericValue3;
01694 
01695                 NPC->client->leader = NULL;
01696                 if ( NPCInfo->tempBehavior == BS_FOLLOW_LEADER )
01697                 {
01698                         NPCInfo->tempBehavior = BS_DEFAULT;
01699                 }
01700                 G_ClearEnemy( NPC );
01701                 NPCInfo->charmedTime = 0;
01702                 //say something to let player know you've snapped out of it
01703                 G_AddVoiceEvent( NPC, Q_irand(EV_CONFUSE1, EV_CONFUSE3), 2000 );
01704         }
01705 }
01706 
01707 void G_GetBoltPosition( gentity_t *self, int boltIndex, vec3_t pos, int modelIndex )
01708 {
01709         mdxaBone_t      boltMatrix;
01710         vec3_t          result, angles;
01711         
01712         if (!self || !self->inuse)
01713         {
01714                 return;
01715         }
01716 
01717         if (self->client)
01718         { //clients don't actually even keep r.currentAngles maintained
01719                 VectorSet(angles, 0, self->client->ps.viewangles[YAW], 0);
01720         }
01721         else
01722         {
01723                 VectorSet(angles, 0, self->r.currentAngles[YAW], 0);
01724         }
01725 
01726         if (  !self->ghoul2 )
01727         {
01728                 return;
01729         }
01730 
01731         trap_G2API_GetBoltMatrix( self->ghoul2, modelIndex, 
01732                                 boltIndex,
01733                                 &boltMatrix, angles, self->r.currentOrigin, level.time,
01734                                 NULL, self->modelScale );
01735         if ( pos )
01736         {
01737                 BG_GiveMeVectorFromMatrix( &boltMatrix, ORIGIN, result );
01738                 VectorCopy( result, pos );
01739         }
01740 }
01741 
01742 float NPC_EntRangeFromBolt( gentity_t *targEnt, int boltIndex )
01743 {
01744         vec3_t  org;
01745 
01746         if ( !targEnt )
01747         {
01748                 return Q3_INFINITE;
01749         }
01750 
01751         G_GetBoltPosition( NPC, boltIndex, org, 0 );
01752 
01753         return (Distance( targEnt->r.currentOrigin, org ));
01754 }
01755 
01756 float NPC_EnemyRangeFromBolt( int boltIndex )
01757 {
01758         return (NPC_EntRangeFromBolt( NPC->enemy, boltIndex ));
01759 }
01760 
01761 int NPC_GetEntsNearBolt( int *radiusEnts, float radius, int boltIndex, vec3_t boltOrg )
01762 {
01763         vec3_t          mins, maxs;
01764         int                     i;
01765 
01766         //get my handRBolt's position
01767         vec3_t  org;
01768 
01769         G_GetBoltPosition( NPC, boltIndex, org, 0 );
01770 
01771         VectorCopy( org, boltOrg );
01772 
01773         //Setup the bbox to search in
01774         for ( i = 0; i < 3; i++ )
01775         {
01776                 mins[i] = boltOrg[i] - radius;
01777                 maxs[i] = boltOrg[i] + radius;
01778         }
01779 
01780         //Get the number of entities in a given space
01781         return (trap_EntitiesInBox( mins, maxs, radiusEnts, 128 ));
01782 }