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
00015 turretStats_t *turretStats,
00016 vehWeaponInfo_t *vehWeapon,
00017 int turretNum, int curMuzzle )
00018 {
00019
00020 if ( pVeh->m_iMuzzleTag[curMuzzle] == -1 )
00021 {
00022 return;
00023 }
00024
00025 if ( pVeh->m_iMuzzleWait[curMuzzle] >= level.time )
00026 {
00027 return;
00028 }
00029
00030 if ( pVeh->turretStatus[turretNum].ammo < vehWeapon->iAmmoPerShot )
00031 {
00032 return;
00033 }
00034
00035
00036 {
00037
00038 int nextMuzzle = 0, muzzlesFired = (1<<curMuzzle);
00039 gentity_t *missile;
00040 WP_CalcVehMuzzle( parent, curMuzzle );
00041
00042
00043 missile = WP_FireVehicleWeapon( parent, pVeh->m_vMuzzlePos[curMuzzle], pVeh->m_vMuzzleDir[curMuzzle], vehWeapon, (turretNum!=0), qtrue );
00044
00045
00046 G_VehMuzzleFireFX(parent, missile, muzzlesFired );
00047
00048
00049 pVeh->turretStatus[turretNum].ammo -= vehWeapon->iAmmoPerShot;
00050
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 {
00054 pVeh->turretStatus[turretNum].nextMuzzle = nextMuzzle-1;
00055 }
00056
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 {
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
00083 VectorSubtract( org, pVeh->m_vMuzzlePos[curMuzzle], enemyDir );
00084
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
00102 vectoangles( pVeh->m_vMuzzleDir[curMuzzle], curAngles );
00103
00104 AnglesSubtract( curAngles, pVeh->m_vOrientation, curAngles );
00105
00106 if ( turretEnemy )
00107 {
00108 aimCorrect = qtrue;
00109
00110
00111 VEH_TurretAnglesToEnemy( pVeh, curMuzzle, vehWeapon->fSpeed, turretEnemy, turretStats->bAILead, desiredAngles );
00112 }
00113
00114 AnglesSubtract( desiredAngles, pVeh->m_vOrientation, desiredAngles );
00115
00116
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
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
00145 AnglesSubtract( desiredAngles, curAngles, addAngles );
00146
00147 if ( addAngles[PITCH] > turretStats->fTurnSpeed )
00148 {
00149
00150 addAngles[PITCH] = turretStats->fTurnSpeed;
00151 }
00152 else if ( addAngles[PITCH] < -turretStats->fTurnSpeed )
00153 {
00154
00155 addAngles[PITCH] = -turretStats->fTurnSpeed;
00156 }
00157 if ( addAngles[YAW] > turretStats->fTurnSpeed )
00158 {
00159
00160 addAngles[YAW] = turretStats->fTurnSpeed;
00161 }
00162 else if ( addAngles[YAW] < -turretStats->fTurnSpeed )
00163 {
00164
00165 addAngles[YAW] = -turretStats->fTurnSpeed;
00166 }
00167
00168
00169 newAngles[PITCH] = AngleNormalize180( curAngles[PITCH]+addAngles[PITCH] );
00170 newAngles[YAW] = AngleNormalize180( curAngles[YAW]+addAngles[YAW] );
00171
00172
00173 if ( turretStats->yawBone )
00174 {
00175 VectorClear( yawAngles );
00176 yawAngles[turretStats->yawAxis] = newAngles[YAW];
00177 NPC_SetBoneAngles( parent, turretStats->yawBone, yawAngles );
00178 }
00179
00180 if ( turretStats->pitchBone )
00181 {
00182 VectorClear( pitchAngles );
00183 pitchAngles[turretStats->pitchAxis] = newAngles[PITCH];
00184 NPC_SetBoneAngles( parent, turretStats->pitchBone, pitchAngles );
00185 }
00186
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 {
00226 if ( !(target->flags&FL_BBRUSH)
00227 || !target->takedamage
00228 || (target->NPC_targetname&&parent->targetname&&Q_stricmp(target->NPC_targetname,parent->targetname)!=0) )
00229 {
00230 if ( target->s.weapon == WP_TURRET
00231 && target->classname
00232 && Q_strncmp( "misc_turret", target->classname, 11 ) == 0 )
00233 {
00234 }
00235 else
00236 {
00237 continue;
00238 }
00239 }
00240
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 {
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
00259 continue;
00260 }
00261 }
00262 else if ( target->teamnodmg == parent->client->sess.sessionTeam )
00263 {
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
00280 VectorSubtract( target->r.currentOrigin, org2, enemyDir );
00281 enemyDist = VectorLengthSquared( enemyDir );
00282
00283 if ( enemyDist < bestDist || (target->client && !foundClient))
00284 {
00285 bestTarget = target;
00286 bestDist = enemyDist;
00287 found = qtrue;
00288 if ( target->client )
00289 {
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 {
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 {
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 {
00338 return;
00339 }
00340
00341 if ( turretStats->passengerNum
00342 && pVeh->m_iNumPassengers >= turretStats->passengerNum )
00343 {
00344 VEH_TurretObeyPassengerControl( pVeh, parent, turretNum );
00345 return;
00346 }
00347 else if ( !turretStats->bAI )
00348 {
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)
00362 || turretEnemy == parent
00363 || turretEnemy->r.ownerNum == parent->s.number
00364 || ( turretEnemy->client && turretEnemy->client->sess.sessionTeam == TEAM_SPECTATOR ) )
00365 {
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 {
00385 if ( turretEnemy->client )
00386 {
00387 pVeh->turretStatus[turretNum].enemyHoldTime = level.time + 3000;
00388 }
00389 else
00390 {
00391 pVeh->turretStatus[turretNum].enemyHoldTime = level.time + 500;
00392 }
00393 }
00394 }
00395 if ( turretEnemy != NULL )
00396 {
00397 if ( turretEnemy->health > 0 )
00398 {
00399
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
00407 if ( trap_InPVS( pVeh->m_vMuzzlePos[curMuzzle], turretEnemy->r.currentOrigin ) )
00408 {
00409
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;
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, turretStats, vehWeapon, turretNum, curMuzzle );
00433 }
00434 }
00435 }