codemp/game/g_vehicleTurret.c

Go to the documentation of this file.
00001 #include "g_headers.h"
00002 #include "bg_vehicles.h"
00003 #include "b_local.h"
00004 #include "../ghoul2/G2.h"
00005 
00006 extern void G_SetEnemy( gentity_t *self, gentity_t *enemy );
00007 extern void WP_CalcVehMuzzle(gentity_t *ent, int muzzleNum);
00008 extern gentity_t *WP_FireVehicleWeapon( gentity_t *ent, vec3_t start, vec3_t dir, vehWeaponInfo_t *vehWeapon, qboolean alt_fire, qboolean isTurretWeap );
00009 
00010 extern void G_VehMuzzleFireFX( gentity_t *ent, gentity_t *broadcaster, int muzzlesFired );
00011 //-----------------------------------------------------
00012 void VEH_TurretCheckFire( Vehicle_t *pVeh, 
00013                                                  gentity_t *parent,
00014                                                  //gentity_t *turretEnemy,
00015                                                  turretStats_t *turretStats, 
00016                                                  vehWeaponInfo_t *vehWeapon, 
00017                                                  int turretNum, int curMuzzle )
00018 {
00019         // if it's time to fire and we have an enemy, then gun 'em down!  pushDebounce time controls next fire time
00020         if ( pVeh->m_iMuzzleTag[curMuzzle] == -1 )
00021         {//invalid muzzle?
00022                 return;
00023         }
00024         
00025         if ( pVeh->m_iMuzzleWait[curMuzzle] >= level.time )
00026         {//can't fire yet
00027                 return;
00028         }
00029 
00030         if ( pVeh->turretStatus[turretNum].ammo < vehWeapon->iAmmoPerShot )
00031         {//no ammo, can't fire
00032                 return;
00033         }
00034         
00035         //if ( turretEnemy )
00036         {
00037                 //FIXME: check to see if I'm aiming generally where I want to
00038                 int nextMuzzle = 0, muzzlesFired = (1<<curMuzzle);
00039                 gentity_t *missile;
00040                 WP_CalcVehMuzzle( parent, curMuzzle );
00041 
00042                 //FIXME: some variation in fire dir
00043                 missile = WP_FireVehicleWeapon( parent, pVeh->m_vMuzzlePos[curMuzzle], pVeh->m_vMuzzleDir[curMuzzle], vehWeapon, (turretNum!=0), qtrue );
00044 
00045                 //play the weapon's muzzle effect if we have one
00046                 G_VehMuzzleFireFX(parent, missile, muzzlesFired );
00047 
00048                 //take the ammo away
00049                 pVeh->turretStatus[turretNum].ammo -= vehWeapon->iAmmoPerShot;
00050                 //toggle to the next muzzle on this turret, if there is one
00051                 nextMuzzle = ((curMuzzle+1)==pVeh->m_pVehicleInfo->turret[turretNum].iMuzzle[0])?pVeh->m_pVehicleInfo->turret[turretNum].iMuzzle[1]:pVeh->m_pVehicleInfo->turret[turretNum].iMuzzle[0];
00052                 if ( nextMuzzle )
00053                 {//a valid muzzle to toggle to
00054                         pVeh->turretStatus[turretNum].nextMuzzle = nextMuzzle-1;//-1 because you type muzzles 1-10 in the .veh file
00055                 }
00056                 //add delay to the next muzzle so it doesn't fire right away on the next frame
00057                 pVeh->m_iMuzzleWait[pVeh->turretStatus[turretNum].nextMuzzle] = level.time + turretStats->iDelay;
00058         }
00059 }
00060 
00061 void VEH_TurretAnglesToEnemy( Vehicle_t *pVeh, int curMuzzle, float fSpeed, gentity_t *turretEnemy, qboolean bAILead, vec3_t desiredAngles )
00062 {
00063         vec3_t  enemyDir, org;
00064         VectorCopy( turretEnemy->r.currentOrigin, org );
00065         if ( bAILead )
00066         {//we want to lead them a bit
00067                 vec3_t diff, velocity;
00068                 float dist;
00069                 VectorSubtract( org, pVeh->m_vMuzzlePos[curMuzzle], diff );
00070                 dist = VectorNormalize( diff );
00071                 if ( turretEnemy->client )
00072                 {
00073                         VectorCopy( turretEnemy->client->ps.velocity, velocity );
00074                 }
00075                 else
00076                 {
00077                         VectorCopy( turretEnemy->s.pos.trDelta, velocity );
00078                 }
00079                 VectorMA( org, (dist/fSpeed), velocity, org );
00080         }
00081 
00082         //FIXME: this isn't quite right, it's aiming from the muzzle, not the center of the turret...
00083         VectorSubtract( org, pVeh->m_vMuzzlePos[curMuzzle], enemyDir );
00084         //Get the desired absolute, world angles to our target
00085         vectoangles( enemyDir, desiredAngles );
00086 }
00087 
00088 //-----------------------------------------------------
00089 qboolean VEH_TurretAim( Vehicle_t *pVeh, 
00090                                                  gentity_t *parent, 
00091                                                  gentity_t *turretEnemy,
00092                                                  turretStats_t *turretStats, 
00093                                                  vehWeaponInfo_t *vehWeapon, 
00094                                                  int turretNum, int curMuzzle, vec3_t desiredAngles )
00095 //-----------------------------------------------------
00096 {
00097         vec3_t  curAngles, addAngles, newAngles, yawAngles, pitchAngles;
00098         float   aimCorrect = qfalse;
00099 
00100         WP_CalcVehMuzzle( parent, curMuzzle );
00101         //get the current absolute angles of the turret right now
00102         vectoangles( pVeh->m_vMuzzleDir[curMuzzle], curAngles );
00103         //subtract out the vehicle's angles to get the relative alignment
00104         AnglesSubtract( curAngles, pVeh->m_vOrientation, curAngles );
00105 
00106         if ( turretEnemy )
00107         {
00108                 aimCorrect = qtrue;
00109                 // ...then we'll calculate what new aim adjustments we should attempt to make this frame
00110                 // Aim at enemy
00111                 VEH_TurretAnglesToEnemy( pVeh, curMuzzle, vehWeapon->fSpeed, turretEnemy, turretStats->bAILead, desiredAngles ); 
00112         }
00113         //subtract out the vehicle's angles to get the relative desired alignment
00114         AnglesSubtract( desiredAngles, pVeh->m_vOrientation, desiredAngles );
00115         //Now clamp the desired relative angles
00116         //clamp yaw
00117         desiredAngles[YAW] = AngleNormalize180( desiredAngles[YAW] );
00118         if ( pVeh->m_pVehicleInfo->turret[turretNum].yawClampLeft
00119                 && desiredAngles[YAW] > pVeh->m_pVehicleInfo->turret[turretNum].yawClampLeft )
00120         {
00121                 aimCorrect = qfalse;
00122                 desiredAngles[YAW] = pVeh->m_pVehicleInfo->turret[turretNum].yawClampLeft;
00123         }
00124         if ( pVeh->m_pVehicleInfo->turret[turretNum].yawClampRight
00125                 && desiredAngles[YAW] < pVeh->m_pVehicleInfo->turret[turretNum].yawClampRight )
00126         {
00127                 aimCorrect = qfalse;
00128                 desiredAngles[YAW] = pVeh->m_pVehicleInfo->turret[turretNum].yawClampRight;
00129         }
00130         //clamp pitch
00131         desiredAngles[PITCH] = AngleNormalize180( desiredAngles[PITCH] );
00132         if ( pVeh->m_pVehicleInfo->turret[turretNum].pitchClampDown
00133                 && desiredAngles[PITCH] > pVeh->m_pVehicleInfo->turret[turretNum].pitchClampDown )
00134         {
00135                 aimCorrect = qfalse;
00136                 desiredAngles[PITCH] = pVeh->m_pVehicleInfo->turret[turretNum].pitchClampDown;
00137         }
00138         if ( pVeh->m_pVehicleInfo->turret[turretNum].pitchClampUp
00139                 && desiredAngles[PITCH] < pVeh->m_pVehicleInfo->turret[turretNum].pitchClampUp )
00140         {
00141                 aimCorrect = qfalse;
00142                 desiredAngles[PITCH] = pVeh->m_pVehicleInfo->turret[turretNum].pitchClampUp;
00143         }
00144         //Now get the offset we want from our current relative angles
00145         AnglesSubtract( desiredAngles, curAngles, addAngles );
00146         //Now cap the addAngles for our fTurnSpeed
00147         if ( addAngles[PITCH] > turretStats->fTurnSpeed )
00148         {
00149                 //aimCorrect = qfalse;//???
00150                 addAngles[PITCH] = turretStats->fTurnSpeed;
00151         }
00152         else if ( addAngles[PITCH] < -turretStats->fTurnSpeed )
00153         {
00154                 //aimCorrect = qfalse;//???
00155                 addAngles[PITCH] = -turretStats->fTurnSpeed;
00156         }
00157         if ( addAngles[YAW] > turretStats->fTurnSpeed )
00158         {
00159                 //aimCorrect = qfalse;//???
00160                 addAngles[YAW] = turretStats->fTurnSpeed;
00161         }
00162         else if ( addAngles[YAW] < -turretStats->fTurnSpeed )
00163         {
00164                 //aimCorrect = qfalse;//???
00165                 addAngles[YAW] = -turretStats->fTurnSpeed;
00166         }
00167         //Now add the additional angles back in to our current relative angles
00168         //FIXME: add some AI aim error randomness...?
00169         newAngles[PITCH] = AngleNormalize180( curAngles[PITCH]+addAngles[PITCH] );
00170         newAngles[YAW] = AngleNormalize180( curAngles[YAW]+addAngles[YAW] );
00171         //Now set the bone angles to the new angles
00172         //set yaw
00173         if ( turretStats->yawBone )
00174         {
00175                 VectorClear( yawAngles );
00176                 yawAngles[turretStats->yawAxis] = newAngles[YAW];
00177                 NPC_SetBoneAngles( parent, turretStats->yawBone, yawAngles );
00178         }
00179         //set pitch
00180         if ( turretStats->pitchBone )
00181         {
00182                 VectorClear( pitchAngles );
00183                 pitchAngles[turretStats->pitchAxis] = newAngles[PITCH];
00184                 NPC_SetBoneAngles( parent, turretStats->pitchBone, pitchAngles );
00185         }
00186         //force muzzle to recalc next check
00187         pVeh->m_iMuzzleTime[curMuzzle] = 0;
00188 
00189         return aimCorrect;
00190 }
00191 
00192 //-----------------------------------------------------
00193 static qboolean VEH_TurretFindEnemies( Vehicle_t *pVeh, 
00194                                                  gentity_t *parent, 
00195                                                  turretStats_t *turretStats, 
00196                                                  int turretNum, int curMuzzle )
00197 //-----------------------------------------------------
00198 {
00199         qboolean        found = qfalse;
00200         int                     i, count;
00201         float           bestDist = turretStats->fAIRange * turretStats->fAIRange;
00202         float           enemyDist;
00203         vec3_t          enemyDir, org, org2;
00204         qboolean        foundClient = qfalse;
00205         gentity_t       *entity_list[MAX_GENTITIES], *target, *bestTarget = NULL;
00206 
00207         WP_CalcVehMuzzle( parent, curMuzzle );
00208         VectorCopy( pVeh->m_vMuzzlePos[curMuzzle], org2 );
00209 
00210         count = G_RadiusList( org2, turretStats->fAIRange, parent, qtrue, entity_list );
00211 
00212         for ( i = 0; i < count; i++ )
00213         {
00214                 trace_t tr;
00215                 target = entity_list[i];
00216 
00217                 if ( target == parent 
00218                         || !target->takedamage 
00219                         || target->health <= 0 
00220                         || ( target->flags & FL_NOTARGET ))
00221                 {
00222                         continue;
00223                 }
00224                 if ( !target->client )
00225                 {// only attack clients
00226                         if ( !(target->flags&FL_BBRUSH)//not a breakable brush
00227                                 || !target->takedamage//is a bbrush, but invincible
00228                                 || (target->NPC_targetname&&parent->targetname&&Q_stricmp(target->NPC_targetname,parent->targetname)!=0) )//not in invicible bbrush, but can only be broken by an NPC that is not me
00229                         {
00230                                 if ( target->s.weapon == WP_TURRET
00231                                         && target->classname
00232                                         && Q_strncmp( "misc_turret", target->classname, 11 ) == 0 )
00233                                 {//these guys we want to shoot at
00234                                 }
00235                                 else
00236                                 {
00237                                         continue;
00238                                 }
00239                         }
00240                         //else: we will shoot at bbrushes!
00241                 }
00242                 else if ( target->client->sess.sessionTeam == TEAM_SPECTATOR )
00243                 {
00244                         continue;
00245                 }
00246                 if ( target == ((gentity_t*)pVeh->m_pPilot)
00247                         || target->r.ownerNum == parent->s.number )
00248                 {//don't get angry at my pilot or passengers?
00249                         continue;
00250                 }
00251                 if ( parent->client
00252                         && parent->client->sess.sessionTeam )
00253                 {
00254                         if ( target->client )
00255                         {
00256                                 if ( target->client->sess.sessionTeam == parent->client->sess.sessionTeam )
00257                                 { 
00258                                         // A bot/client/NPC we don't want to shoot
00259                                         continue;
00260                                 }
00261                         }
00262                         else if ( target->teamnodmg == parent->client->sess.sessionTeam )
00263                         {//some other entity that's allied with us
00264                                 continue;
00265                         }
00266                 }
00267                 if ( !trap_InPVS( org2, target->r.currentOrigin ))
00268                 {
00269                         continue;
00270                 }
00271 
00272                 VectorCopy( target->r.currentOrigin, org );
00273 
00274                 trap_Trace( &tr, org2, NULL, NULL, org, parent->s.number, MASK_SHOT );
00275 
00276                 if ( tr.entityNum == target->s.number
00277                         || (!tr.allsolid && !tr.startsolid && tr.fraction == 1.0 ) )
00278                 {
00279                         // Only acquire if have a clear shot, Is it in range and closer than our best?
00280                         VectorSubtract( target->r.currentOrigin, org2, enemyDir );
00281                         enemyDist = VectorLengthSquared( enemyDir );
00282 
00283                         if ( enemyDist < bestDist || (target->client && !foundClient))// all things equal, keep current
00284                         {
00285                                 bestTarget = target;
00286                                 bestDist = enemyDist;
00287                                 found = qtrue;
00288                                 if ( target->client )
00289                                 {//prefer clients over non-clients
00290                                         foundClient = qtrue;
00291                                 }
00292                         }
00293                 }
00294         }
00295 
00296         if ( found )
00297         {
00298                 pVeh->turretStatus[turretNum].enemyEntNum = bestTarget->s.number;
00299         }
00300 
00301         return found;
00302 }
00303 
00304 void VEH_TurretObeyPassengerControl( Vehicle_t *pVeh, gentity_t *parent, int turretNum )
00305 {
00306         turretStats_t *turretStats = &pVeh->m_pVehicleInfo->turret[turretNum];
00307         gentity_t *passenger = (gentity_t *)pVeh->m_ppPassengers[turretStats->passengerNum-1];
00308 
00309         if ( passenger && passenger->client && passenger->health > 0 )
00310         {//a valid, living passenger client
00311                 vehWeaponInfo_t *vehWeapon = &g_vehWeaponInfo[turretStats->iWeapon];
00312                 int     curMuzzle = pVeh->turretStatus[turretNum].nextMuzzle;
00313                 vec3_t aimAngles;
00314                 VectorCopy( passenger->client->ps.viewangles, aimAngles );
00315 
00316                 VEH_TurretAim( pVeh, parent, NULL, turretStats, vehWeapon, turretNum, curMuzzle, aimAngles );
00317                 if ( (passenger->client->pers.cmd.buttons&(BUTTON_ATTACK|BUTTON_ALT_ATTACK)) )
00318                 {//he's pressing an attack button, so fire!
00319                         VEH_TurretCheckFire( pVeh, parent, turretStats, vehWeapon, turretNum, curMuzzle );
00320                 }
00321         }
00322 }
00323 
00324 void VEH_TurretThink( Vehicle_t *pVeh, gentity_t *parent, int turretNum )
00325 //-----------------------------------------------------
00326 {
00327         qboolean        doAim = qfalse;
00328         float           enemyDist, rangeSq;
00329         vec3_t          enemyDir;
00330         turretStats_t *turretStats = &pVeh->m_pVehicleInfo->turret[turretNum];
00331         vehWeaponInfo_t *vehWeapon = NULL;
00332         gentity_t       *turretEnemy = NULL;
00333         int                     curMuzzle = 0;//?
00334         
00335 
00336         if ( !turretStats || !turretStats->iAmmoMax )
00337         {//not a valid turret
00338                 return;
00339         }
00340         
00341         if ( turretStats->passengerNum 
00342                 && pVeh->m_iNumPassengers >= turretStats->passengerNum )
00343         {//the passenger that has control of this turret is on the ship
00344                 VEH_TurretObeyPassengerControl( pVeh, parent, turretNum );
00345                 return;
00346         }
00347         else if ( !turretStats->bAI )//try AI
00348         {//this turret does not think on its own.
00349                 return;
00350         }
00351 
00352         vehWeapon = &g_vehWeaponInfo[turretStats->iWeapon];
00353         rangeSq = (turretStats->fAIRange*turretStats->fAIRange);
00354         curMuzzle = pVeh->turretStatus[turretNum].nextMuzzle;
00355 
00356         if ( pVeh->turretStatus[turretNum].enemyEntNum < ENTITYNUM_WORLD )
00357         {
00358                 turretEnemy = &g_entities[pVeh->turretStatus[turretNum].enemyEntNum];
00359                 if ( turretEnemy->health < 0 
00360                         || !turretEnemy->inuse
00361                         || turretEnemy == ((gentity_t*)pVeh->m_pPilot)//enemy became my pilot///?
00362                         || turretEnemy == parent
00363                         || turretEnemy->r.ownerNum == parent->s.number // a passenger?
00364                         || ( turretEnemy->client && turretEnemy->client->sess.sessionTeam == TEAM_SPECTATOR ) )
00365                 {//don't keep going after spectators, pilot, self, dead people, etc.
00366                         turretEnemy = NULL;
00367                         pVeh->turretStatus[turretNum].enemyEntNum = ENTITYNUM_NONE;
00368                 }
00369         }
00370 
00371         if ( pVeh->turretStatus[turretNum].enemyHoldTime < level.time )
00372         {
00373                 if ( VEH_TurretFindEnemies( pVeh, parent, turretStats, turretNum, curMuzzle ) )
00374                 {
00375                         turretEnemy = &g_entities[pVeh->turretStatus[turretNum].enemyEntNum];
00376                         doAim = qtrue;
00377                 }
00378                 else if ( parent->enemy && parent->enemy->s.number < ENTITYNUM_WORLD )
00379                 {
00380                         turretEnemy = parent->enemy;
00381                         doAim = qtrue;
00382                 }
00383                 if ( turretEnemy )
00384                 {//found one
00385                         if ( turretEnemy->client )
00386                         {//hold on to clients for a min of 3 seconds
00387                                 pVeh->turretStatus[turretNum].enemyHoldTime = level.time + 3000;
00388                         }
00389                         else
00390                         {//hold less
00391                                 pVeh->turretStatus[turretNum].enemyHoldTime = level.time + 500;
00392                         }
00393                 }
00394         }
00395         if ( turretEnemy != NULL )
00396         {
00397                 if ( turretEnemy->health > 0 )
00398                 {
00399                         // enemy is alive
00400                         WP_CalcVehMuzzle( parent, curMuzzle );
00401                         VectorSubtract( turretEnemy->r.currentOrigin, pVeh->m_vMuzzlePos[curMuzzle], enemyDir );
00402                         enemyDist = VectorLengthSquared( enemyDir );
00403 
00404                         if ( enemyDist < rangeSq )
00405                         {
00406                                 // was in valid radius
00407                                 if ( trap_InPVS( pVeh->m_vMuzzlePos[curMuzzle], turretEnemy->r.currentOrigin ) )
00408                                 {
00409                                         // Every now and again, check to see if we can even trace to the enemy
00410                                         trace_t tr;
00411                                         vec3_t start, end;
00412                                         VectorCopy( pVeh->m_vMuzzlePos[curMuzzle], start );
00413 
00414                                         VectorCopy( turretEnemy->r.currentOrigin, end );
00415                                         trap_Trace( &tr, start, NULL, NULL, end, parent->s.number, MASK_SHOT );
00416 
00417                                         if ( tr.entityNum == turretEnemy->s.number
00418                                                 || (!tr.allsolid && !tr.startsolid ) )
00419                                         {
00420                                                 doAim = qtrue;  // Can see our enemy
00421                                         }
00422                                 }
00423                         }
00424                 }
00425         }
00426 
00427         if ( doAim )
00428         {
00429                 vec3_t aimAngles;
00430                 if ( VEH_TurretAim( pVeh, parent, turretEnemy, turretStats, vehWeapon, turretNum, curMuzzle, aimAngles ) )
00431                 {
00432                         VEH_TurretCheckFire( pVeh, parent, /*turretEnemy,*/ turretStats, vehWeapon, turretNum, curMuzzle );
00433                 }
00434         }
00435 }