cp_airfight.c

Go to the documentation of this file.
00001 
00008 /*
00009 Copyright (C) 2002-2010 UFO: Alien Invasion.
00010 
00011 This program is free software; you can redistribute it and/or
00012 modify it under the terms of the GNU General Public License
00013 as published by the Free Software Foundation; either version 2
00014 of the License, or (at your option) any later version.
00015 
00016 This program is distributed in the hope that it will be useful,
00017 but WITHOUT ANY WARRANTY; without even the implied warranty of
00018 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
00019 
00020 See the GNU General Public License for more details.
00021 
00022 You should have received a copy of the GNU General Public License
00023 along with this program; if not, write to the Free Software
00024 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
00025 
00026 */
00027 
00028 #include "../cl_shared.h"
00029 #include "cp_campaign.h"
00030 #include "cp_mapfightequip.h"
00031 #include "cp_map.h"
00032 #include "cp_ufo.h"
00033 #include "cp_missions.h"
00034 #include "save/save_airfight.h"
00035 
00041 static qboolean AIRFIGHT_RemoveProjectile (aircraftProjectile_t *projectile)
00042 {
00043     const ptrdiff_t num = (ptrdiff_t)(projectile - ccs.projectiles);
00044     REMOVE_ELEM_ADJUST_IDX(ccs.projectiles, num, ccs.numProjectiles);
00045     return qtrue;
00046 }
00047 
00059 static qboolean AIRFIGHT_AddProjectile (const base_t* attackingBase, const installation_t* attackingInstallation, aircraft_t *attacker, aircraft_t *target, aircraftSlot_t *weaponSlot)
00060 {
00061     aircraftProjectile_t *projectile;
00062 
00063     if (ccs.numProjectiles >= MAX_PROJECTILESONGEOSCAPE) {
00064         Com_DPrintf(DEBUG_CLIENT, "Too many projectiles on map\n");
00065         return qfalse;
00066     }
00067 
00068     projectile = &ccs.projectiles[ccs.numProjectiles];
00069 
00070     if (!weaponSlot->ammo) {
00071         Com_Printf("AIRFIGHT_AddProjectile: Error - no ammo assigned\n");
00072         return qfalse;
00073     }
00074 
00075     assert(weaponSlot->item);
00076 
00077     projectile->aircraftItem = weaponSlot->ammo;
00078     if (attackingBase) {
00079         projectile->attackingAircraft = NULL;
00080         VectorSet(projectile->pos[0], attackingBase->pos[0], attackingBase->pos[1], 0);
00081         VectorSet(projectile->attackerPos, attackingBase->pos[0], attackingBase->pos[1], 0);
00082     } else if (attackingInstallation) {
00083         projectile->attackingAircraft = NULL;
00084         VectorSet(projectile->pos[0], attackingInstallation->pos[0], attackingInstallation->pos[1], 0);
00085         VectorSet(projectile->attackerPos, attackingInstallation->pos[0], attackingInstallation->pos[1], 0);
00086     } else {
00087         assert(attacker);
00088         projectile->attackingAircraft = attacker;
00089         VectorSet(projectile->pos[0], attacker->pos[0], attacker->pos[1], 0);
00090         /* attacker may move, use attackingAircraft->pos */
00091         VectorSet(projectile->attackerPos, 0, 0, 0);
00092     }
00093 
00094     projectile->numProjectiles++;
00095 
00096     assert(target);
00097     projectile->aimedAircraft = target;
00098     VectorSet(projectile->idleTarget, 0, 0, 0);
00099 
00100     projectile->time = 0;
00101     projectile->angle = 0.0f;
00102 
00103     projectile->bullets = (weaponSlot->item->craftitem.bullets) ? qtrue : qfalse;
00104     projectile->beam = (weaponSlot->item->craftitem.beam) ? qtrue : qfalse;
00105 
00106     weaponSlot->ammoLeft--;
00107     if (weaponSlot->ammoLeft <= 0)
00108         AII_ReloadWeapon(weaponSlot);
00109 
00110     ccs.numProjectiles++;
00111 
00112     return qtrue;
00113 }
00114 
00115 #ifdef DEBUG
00116 
00120 static void AIRFIGHT_ProjectileList_f (void)
00121 {
00122     int i;
00123 
00124     for (i = 0; i < ccs.numProjectiles; i++) {
00125         Com_Printf("%i. (idx: %i)\n", i, ccs.projectiles[i].idx);
00126         Com_Printf("... type '%s'\n", ccs.projectiles[i].aircraftItem->id);
00127         if (ccs.projectiles[i].attackingAircraft)
00128             Com_Printf("... shooting aircraft '%s'\n", ccs.projectiles[i].attackingAircraft->id);
00129         else
00130             Com_Printf("... base is shooting, or shooting aircraft is destroyed\n");
00131         if (ccs.projectiles[i].aimedAircraft)
00132             Com_Printf("... aiming aircraft '%s'\n", ccs.projectiles[i].aimedAircraft->id);
00133         else
00134             Com_Printf("... aiming iddle target at (%.02f, %.02f)\n",
00135                 ccs.projectiles[i].idleTarget[0], ccs.projectiles[i].idleTarget[1]);
00136     }
00137 }
00138 #endif
00139 
00145 static void AIRFIGHT_MissTarget (aircraftProjectile_t *projectile, qboolean returnToBase)
00146 {
00147     vec3_t newTarget;
00148     float distance;
00149     float offset;
00150 
00151     assert(projectile);
00152 
00153     if (projectile->aimedAircraft) {
00154         VectorCopy(projectile->aimedAircraft->pos, newTarget);
00155         projectile->aimedAircraft = NULL;
00156     } else {
00157         VectorCopy(projectile->idleTarget, newTarget);
00158     }
00159 
00160     /* get the distance between the projectile and target */
00161     distance = GetDistanceOnGlobe(projectile->pos[0], newTarget);
00162 
00163     /* Work out how much the projectile should miss the target by.  We dont want it too close
00164      * or too far from the original target.
00165      * * 1/3 distance between target and projectile * random (range -0.5 to 0.5)
00166      * * Then make sure the value is at least greater than 0.1 or less than -0.1 so that
00167      *   the projectile doesn't land too close to the target. */
00168     offset = (distance / 3) * (frand() - 0.5f);
00169 
00170     if (abs(offset) < 0.1f)
00171         offset = 0.1f;
00172 
00173     newTarget[0] = newTarget[0] + offset;
00174     newTarget[1] = newTarget[1] + offset;
00175 
00176     VectorCopy(newTarget, projectile->idleTarget);
00177 
00178     if (returnToBase && projectile->attackingAircraft) {
00179         if (projectile->attackingAircraft->homebase) {
00180             assert(!AIR_IsUFO(projectile->attackingAircraft));
00181             AIR_AircraftReturnToBase(projectile->attackingAircraft);
00182         }
00183     }
00184 }
00185 
00194 int AIRFIGHT_CheckWeapon (const aircraftSlot_t *slot, float distance)
00195 {
00196     assert(slot);
00197 
00198     /* check if there is a functional weapon in this slot */
00199     if (!slot->item || slot->installationTime != 0)
00200         return AIRFIGHT_WEAPON_CAN_NEVER_SHOOT;
00201 
00202     /* check if there is still ammo in this weapon */
00203     if (!slot->ammo || (slot->ammoLeft <= 0))
00204         return AIRFIGHT_WEAPON_CAN_NEVER_SHOOT;
00205 
00206     /* check if the target is within range of this weapon */
00207     if (distance > slot->ammo->craftitem.stats[AIR_STATS_WRANGE])
00208         return AIRFIGHT_WEAPON_CAN_NOT_SHOOT_AT_THE_MOMENT;
00209 
00210     /* check if weapon is reloaded */
00211     if (slot->delayNextShot > 0)
00212         return AIRFIGHT_WEAPON_CAN_NOT_SHOOT_AT_THE_MOMENT;
00213 
00214     return AIRFIGHT_WEAPON_CAN_SHOOT;
00215 }
00216 
00228 int AIRFIGHT_ChooseWeapon (const aircraftSlot_t const *slot, int maxSlot, const vec3_t pos, const vec3_t targetPos)
00229 {
00230     int slotIdx = AIRFIGHT_WEAPON_CAN_NEVER_SHOOT;
00231     int i, weaponStatus;
00232     float distance0 = 99999.9f;
00233     const float distance = GetDistanceOnGlobe(pos, targetPos);
00234 
00235     /* We choose the usable weapon with the smallest range */
00236     for (i = 0; i < maxSlot; i++) {
00237         assert(slot);
00238         weaponStatus = AIRFIGHT_CheckWeapon(slot + i, distance);
00239 
00240         /* set slotIdx to AIRFIGHT_WEAPON_CAN_NOT_SHOOT_AT_THE_MOMENT if needed */
00241         /* this will only happen if weapon_state is AIRFIGHT_WEAPON_CAN_NOT_SHOOT_AT_THE_MOMENT
00242          * and no weapon has been found that can shoot. */
00243         if (weaponStatus > slotIdx)
00244             slotIdx = AIRFIGHT_WEAPON_CAN_NOT_SHOOT_AT_THE_MOMENT;
00245 
00246         /* select this weapon if this is the one with the shortest range */
00247         if (weaponStatus >= AIRFIGHT_WEAPON_CAN_SHOOT && distance < distance0) {
00248             slotIdx = i;
00249             distance0 = distance;
00250         }
00251     }
00252     return slotIdx;
00253 }
00254 
00268 static float AIRFIGHT_ProbabilityToHit (const aircraft_t *shooter, const aircraft_t *target, const aircraftSlot_t *slot)
00269 {
00270     float probability = 0.0f;
00271 
00272     if (!slot->item) {
00273         Com_Printf("AIRFIGHT_ProbabilityToHit: no weapon assigned to attacking aircraft\n");
00274         return probability;
00275     }
00276 
00277     if (!slot->ammo) {
00278         Com_Printf("AIRFIGHT_ProbabilityToHit: no ammo in weapon of attacking aircraft\n");
00279         return probability;
00280     }
00281 
00282     /* Take Base probability from the ammo of the attacking aircraft */
00283     probability = slot->ammo->craftitem.stats[AIR_STATS_ACCURACY];
00284     Com_DPrintf(DEBUG_CLIENT, "AIRFIGHT_ProbabilityToHit: Base probablity: %f\n", probability);
00285 
00286     /* Modify this probability by items of the attacking aircraft (stats is in percent) */
00287     if (shooter)
00288         probability *= shooter->stats[AIR_STATS_ACCURACY] / 100.0f;
00289 
00290     /* Modify this probability by items of the aimed aircraft (stats is in percent) */
00291     if (target)
00292         probability /= target->stats[AIR_STATS_ECM] / 100.0f;
00293 
00294     Com_DPrintf(DEBUG_CLIENT, "AIRFIGHT_ProbabilityToHit: Probability to hit: %f\n", probability);
00295     return probability;
00296 }
00297 
00304 void AIRFIGHT_ExecuteActions (aircraft_t* shooter, aircraft_t* target)
00305 {
00306     int slotIdx;
00307 
00308     /* some asserts */
00309     assert(shooter);
00310     assert(target);
00311 
00312     /* Check if the attacking aircraft can shoot */
00313     slotIdx = AIRFIGHT_ChooseWeapon(shooter->weapons, shooter->maxWeapons, shooter->pos, target->pos);
00314 
00315     /* if weapon found that can shoot */
00316     if (slotIdx >= AIRFIGHT_WEAPON_CAN_SHOOT) {
00317         const objDef_t *ammo = shooter->weapons[slotIdx].ammo;
00318 
00319         /* shoot */
00320         if (AIRFIGHT_AddProjectile(NULL, NULL, shooter, target, &(shooter->weapons[slotIdx]))) {
00321             /* will we miss the target ? */
00322             const float probability = frand();
00323             shooter->weapons[slotIdx].delayNextShot = ammo->craftitem.weaponDelay;
00324             if (probability > AIRFIGHT_ProbabilityToHit(shooter, target, shooter->weapons + slotIdx))
00325                 AIRFIGHT_MissTarget(&ccs.projectiles[ccs.numProjectiles - 1], qfalse);
00326 
00327             if (shooter->type != AIRCRAFT_UFO) {
00328                 /* Maybe UFO is going to shoot back ? */
00329                 UFO_CheckShootBack(target, shooter);
00330             } else {
00331                 /* an undetected UFO within radar range and firing should become detected */
00332                 if (!shooter->detected && RADAR_CheckRadarSensored(shooter->pos)) {
00333                     /* stop time and notify */
00334                     MSO_CheckAddNewMessage(NT_UFO_ATTACKING,_("Notice"), va(_("A UFO is shooting at %s"), target->name), qfalse, MSG_STANDARD, NULL);
00335                     RADAR_AddDetectedUFOToEveryRadar(shooter);
00336                     UFO_DetectNewUFO(shooter);
00337                 }
00338             }
00339         }
00340     } else if (slotIdx == AIRFIGHT_WEAPON_CAN_NOT_SHOOT_AT_THE_MOMENT) {
00341         /* no ammo to fire atm (too far or reloading), pursue target */
00342         if (shooter->type == AIRCRAFT_UFO) {
00345             UFO_SendPursuingAircraft(shooter, target);
00346         } else
00347             AIR_SendAircraftPursuingUFO(shooter, target);
00348     } else {
00349         /* no ammo left, or no weapon, proceed with mission */
00350         if (shooter->type == AIRCRAFT_UFO) {
00351             shooter->aircraftTarget = NULL;     /* reset target */
00352             CP_UFOProceedMission(shooter);
00353         } else {
00354             MS_AddNewMessage(_("Notice"), _("Our aircraft has no more ammo left - returning to home base now."), qfalse, MSG_STANDARD, NULL);
00355             AIR_AircraftReturnToBase(shooter);
00356         }
00357     }
00358 }
00359 
00366 static void AIRFIGHT_RemoveProjectileAimingAircraft (const aircraft_t * aircraft)
00367 {
00368     aircraftProjectile_t *projectile;
00369     int idx = 0;
00370 
00371     if (!aircraft)
00372         return;
00373 
00374     for (projectile = ccs.projectiles; idx < ccs.numProjectiles; projectile++, idx++) {
00375         if (projectile->aimedAircraft == aircraft)
00376             AIRFIGHT_MissTarget(projectile, qtrue);
00377     }
00378 }
00379 
00385 static void AIRFIGHT_UpdateProjectileForDestroyedAircraft (const aircraft_t * aircraft)
00386 {
00387     aircraftProjectile_t *projectile;
00388     int idx;
00389 
00390     for (idx = 0, projectile = ccs.projectiles; idx < ccs.numProjectiles; projectile++, idx++) {
00391         const aircraft_t *attacker = projectile->attackingAircraft;
00392 
00393         if (attacker == aircraft)
00394             projectile->attackingAircraft = NULL;
00395     }
00396 }
00397 
00409 void AIRFIGHT_ActionsAfterAirfight (aircraft_t *shooter, aircraft_t* aircraft, qboolean phalanxWon)
00410 {
00411     if (phalanxWon) {
00412         byte *color;
00413 
00414         assert(aircraft);
00415 
00416         /* change destination of other projectiles aiming aircraft */
00417         AIRFIGHT_RemoveProjectileAimingAircraft(aircraft);
00418         /* now update the projectile for the destroyed aircraft, too */
00419         AIRFIGHT_UpdateProjectileForDestroyedAircraft(aircraft);
00420 
00421         /* don't remove ufo from global array: the mission is not over yet
00422          * UFO are removed from game only at the end of the mission
00423          * (in case we need to know what item to collect e.g.) */
00424 
00425         /* get the color value of the map at the crash position */
00426         color = MAP_GetColor(aircraft->pos, MAPTYPE_TERRAIN);
00427         /* if this color value is not the value for water ...
00428          * and we hit the probability to spawn a crashsite mission */
00429         if (!MapIsWater(color)) {
00430             CP_SpawnCrashSiteMission(aircraft);
00431         } else {
00432             Com_DPrintf(DEBUG_CLIENT, "AIRFIGHT_ActionsAfterAirfight: zone: %s (%i:%i:%i)\n", MAP_GetTerrainType(color), color[0], color[1], color[2]);
00433             MS_AddNewMessage(_("Interception"), _("UFO interception successful -- UFO lost to sea."), qfalse, MSG_STANDARD, NULL);
00434             CP_MissionIsOverByUFO(aircraft);
00435         }
00436     } else {
00437         /* change destination of other projectiles aiming aircraft */
00438         AIRFIGHT_RemoveProjectileAimingAircraft(aircraft);
00439 
00440         /* and now update the projectile pointers (there still might be some in the air
00441          * of the current destroyed aircraft) - this is needed not send the aircraft
00442          * back to base as soon as the projectiles will hit their target */
00443         AIRFIGHT_UpdateProjectileForDestroyedAircraft(aircraft);
00444 
00445         /* notify UFOs that a phalanx aircraft has been destroyed */
00446         UFO_NotifyPhalanxAircraftRemoved(aircraft);
00447 
00448         if (!MapIsWater(MAP_GetColor(aircraft->pos, MAPTYPE_TERRAIN)))
00449             CP_SpawnRescueMission(aircraft, shooter);
00450         else {
00451             /* Destroy the aircraft and everything onboard - the aircraft pointer
00452              * is no longer valid after this point */
00453             AIR_DestroyAircraft(aircraft);
00454         }
00455 
00456         /* Make UFO proceed with its mission, if it has not been already destroyed */
00457         if (shooter)
00458             CP_UFOProceedMission(shooter);
00459 
00460         MS_AddNewMessage(_("Interception"), _("You've lost the battle"), qfalse, MSG_DEATH, NULL);
00461     }
00462 }
00463 
00471 static qboolean AIRFIGHT_ProjectileReachedTarget (const aircraftProjectile_t *projectile, float movement)
00472 {
00473     float distance;
00474 
00475     if (!projectile->aimedAircraft)
00476         /* the target is idle, its position is in idleTarget*/
00477         distance = GetDistanceOnGlobe(projectile->idleTarget, projectile->pos[0]);
00478     else {
00479         /* the target is moving, pointer to the other aircraft is aimedAircraft */
00480         distance = GetDistanceOnGlobe(projectile->aimedAircraft->pos, projectile->pos[0]);
00481     }
00482 
00483     /* projectile reaches its target */
00484     if (distance < movement)
00485         return qtrue;
00486 
00487     assert(projectile->aircraftItem);
00488 
00489     /* check if the projectile went farther than it's range */
00490     distance = (float) projectile->time * projectile->aircraftItem->craftitem.weaponSpeed / (float)SECONDS_PER_HOUR;
00491     if (distance > projectile->aircraftItem->craftitem.stats[AIR_STATS_WRANGE])
00492         return qtrue;
00493 
00494     return qfalse;
00495 }
00496 
00505 static int AIRFIGHT_GetDamage (const objDef_t *od, const aircraft_t* target)
00506 {
00507     int damage;
00508 
00509     assert(od);
00510 
00511     /* already destroyed - do nothing */
00512     if (target->damage <= 0)
00513         return 0;
00514 
00515     /* base damage is given by the ammo */
00516     damage = od->craftitem.weaponDamage;
00517 
00518     /* reduce damages with shield target */
00519     damage -= target->stats[AIR_STATS_SHIELD];
00520 
00521     return damage;
00522 }
00523 
00529 static void AIRFIGHT_ProjectileHits (aircraftProjectile_t *projectile)
00530 {
00531     aircraft_t *target;
00532     int damage = 0;
00533 
00534     assert(projectile);
00535     target = projectile->aimedAircraft;
00536     assert(target);
00537 
00538     /* if the aircraft is not on geoscape anymore, do nothing (returned to base) */
00539     if (AIR_IsAircraftInBase(target))
00540         return;
00541 
00542     damage = AIRFIGHT_GetDamage(projectile->aircraftItem, target);
00543 
00544     /* apply resulting damages - but only if damage > 0 - because the target might
00545      * already be destroyed, and we don't want to execute the actions after airfight
00546      * for every projectile */
00547     if (damage > 0) {
00548         assert(target->damage > 0);
00549         target->damage -= damage;
00550         if (target->damage <= 0)
00551             /* Target is destroyed */
00552             AIRFIGHT_ActionsAfterAirfight(projectile->attackingAircraft, target, target->type == AIRCRAFT_UFO);
00553     }
00554 }
00555 
00564 static void AIRFIGHT_GetNextPointInPathFromVector (const float *movement, const vec2_t originalPoint, const vec3_t orthogonalVector, vec2_t finalPoint)
00565 {
00566     vec3_t startPoint, finalVectorPoint;
00567 
00568     PolarToVec(originalPoint, startPoint);
00569     RotatePointAroundVector(finalVectorPoint, orthogonalVector, startPoint, *movement);
00570     VecToPolar(finalVectorPoint, finalPoint);
00571 }
00572 
00582 static void AIRFIGHT_GetNextPointInPath (const float *movement, const vec2_t originalPoint, const vec2_t targetPoint, float *angle, vec2_t finalPoint, vec3_t orthogonalVector)
00583 {
00584     *angle = MAP_AngleOfPath(originalPoint, targetPoint, NULL, orthogonalVector);
00585     AIRFIGHT_GetNextPointInPathFromVector(movement, originalPoint, orthogonalVector, finalPoint);
00586 }
00587 
00592 void AIRFIGHT_CampaignRunProjectiles (int dt)
00593 {
00594     int idx;
00595 
00596     /* ccs.numProjectiles is changed in AIRFIGHT_RemoveProjectile */
00597     for (idx = ccs.numProjectiles - 1; idx >= 0; idx--) {
00598         aircraftProjectile_t *projectile = &ccs.projectiles[idx];
00599         const float movement = (float) dt * projectile->aircraftItem->craftitem.weaponSpeed / (float)SECONDS_PER_HOUR;
00600         projectile->time += dt;
00601         projectile->hasMoved = qtrue;
00602         projectile->numInterpolationPoints = 0;
00603 
00604         /* Check if the projectile reached its destination (aircraft or idle point) */
00605         if (AIRFIGHT_ProjectileReachedTarget(projectile, movement)) {
00606             /* check if it got the ennemy */
00607             if (projectile->aimedAircraft)
00608                 AIRFIGHT_ProjectileHits(projectile);
00609 
00610             /* remove the missile from ccs.projectiles[] */
00611             AIRFIGHT_RemoveProjectile(projectile);
00612         } else {
00613             float angle;
00614             vec3_t ortogonalVector, finalPoint, projectedPoint;
00615 
00616             /* missile is moving towards its target */
00617             if (projectile->aimedAircraft) {
00618                 AIRFIGHT_GetNextPointInPath(&movement, projectile->pos[0], projectile->aimedAircraft->pos, &angle, finalPoint, ortogonalVector);
00619                 AIRFIGHT_GetNextPointInPath(&movement, finalPoint, projectile->aimedAircraft->pos, &angle, projectedPoint, ortogonalVector);
00620             } else {
00621                 AIRFIGHT_GetNextPointInPath(&movement, projectile->pos[0], projectile->idleTarget, &angle, finalPoint, ortogonalVector);
00622                 AIRFIGHT_GetNextPointInPath(&movement, finalPoint, projectile->idleTarget, &angle, projectedPoint, ortogonalVector);
00623             }
00624 
00625             /* update angle of the projectile */
00626             projectile->angle = angle;
00627             VectorCopy(finalPoint, projectile->pos[0]);
00628             VectorCopy(projectedPoint, projectile->projectedPos[0]);
00629         }
00630     }
00631 }
00632 
00639 static void AIRFIGHT_BaseShoot (const base_t *base, baseWeapon_t *weapons, int maxWeapons)
00640 {
00641     int i, test;
00642     float distance;
00643 
00644     for (i = 0; i < maxWeapons; i++) {
00645         aircraft_t *target = weapons[i].target;
00646         aircraftSlot_t *slot = &(weapons[i].slot);
00647         /* if no target, can't shoot */
00648         if (!target)
00649             continue;
00650 
00651         /* If the weapon is not ready in base, can't shoot. */
00652         if (slot->installationTime > 0)
00653             continue;
00654 
00655         /* if weapon is reloading, can't shoot */
00656         if (slot->delayNextShot > 0)
00657             continue;
00658 
00659         /* check that the ufo is still visible */
00660         if (!UFO_IsUFOSeenOnGeoscape(target)) {
00661             weapons[i].target = NULL;
00662             continue;
00663         }
00664 
00665         /* Check if we can still fire on this target. */
00666         distance = GetDistanceOnGlobe(base->pos, target->pos);
00667         test = AIRFIGHT_CheckWeapon(slot, distance);
00668         /* weapon unable to shoot, reset target */
00669         if (test == AIRFIGHT_WEAPON_CAN_NEVER_SHOOT) {
00670             weapons[i].target = NULL;
00671             continue;
00672         }
00673         /* we can't shoot with this weapon atm, wait to see if UFO comes closer */
00674         else if (test == AIRFIGHT_WEAPON_CAN_NOT_SHOOT_AT_THE_MOMENT)
00675             continue;
00676         /* target is too far, wait to see if UFO comes closer */
00677         else if (distance > slot->ammo->craftitem.stats[AIR_STATS_WRANGE])
00678             continue;
00679 
00680         /* shoot */
00681         if (AIRFIGHT_AddProjectile(base, NULL, NULL, target, slot)) {
00682             slot->delayNextShot = slot->ammo->craftitem.weaponDelay;
00683             /* will we miss the target ? */
00684             if (frand() > AIRFIGHT_ProbabilityToHit(NULL, target, slot))
00685                 AIRFIGHT_MissTarget(&ccs.projectiles[ccs.numProjectiles - 1], qfalse);
00686         }
00687     }
00688 }
00689 
00696 static void AIRFIGHT_InstallationShoot (const installation_t *installation, baseWeapon_t *weapons, int maxWeapons)
00697 {
00698     int i, test;
00699     float distance;
00700 
00701     for (i = 0; i < maxWeapons; i++) {
00702         aircraft_t *target = weapons[i].target;
00703         aircraftSlot_t *slot = &(weapons[i].slot);
00704         /* if no target, can't shoot */
00705         if (!target)
00706             continue;
00707 
00708         /* If the weapon is not ready in base, can't shoot. */
00709         if (slot->installationTime > 0)
00710             continue;
00711 
00712         /* if weapon is reloading, can't shoot */
00713         if (slot->delayNextShot > 0)
00714             continue;
00715 
00716         /* check that the ufo is still visible */
00717         if (!UFO_IsUFOSeenOnGeoscape(target)) {
00718             weapons[i].target = NULL;
00719             continue;
00720         }
00721 
00722         /* Check if we can still fire on this target. */
00723         distance = GetDistanceOnGlobe(installation->pos, target->pos);
00724         test = AIRFIGHT_CheckWeapon(slot, distance);
00725         /* weapon unable to shoot, reset target */
00726         if (test == AIRFIGHT_WEAPON_CAN_NEVER_SHOOT) {
00727             weapons[i].target = NULL;
00728             continue;
00729         }
00730         /* we can't shoot with this weapon atm, wait to see if UFO comes closer */
00731         else if (test == AIRFIGHT_WEAPON_CAN_NOT_SHOOT_AT_THE_MOMENT)
00732             continue;
00733         /* target is too far, wait to see if UFO comes closer */
00734         else if (distance > slot->ammo->craftitem.stats[AIR_STATS_WRANGE])
00735             continue;
00736 
00737         /* shoot */
00738         if (AIRFIGHT_AddProjectile(NULL, installation, NULL, target, slot)) {
00739             slot->delayNextShot = slot->ammo->craftitem.weaponDelay;
00740             /* will we miss the target ? */
00741             if (frand() > AIRFIGHT_ProbabilityToHit(NULL, target, slot))
00742                 AIRFIGHT_MissTarget(&ccs.projectiles[ccs.numProjectiles - 1], qfalse);
00743         }
00744     }
00745 }
00746 
00751 void AIRFIGHT_CampaignRunBaseDefence (int dt)
00752 {
00753     int baseIdx;
00754     int installationIdx;
00755 
00756     for (baseIdx = 0; baseIdx < MAX_BASES; baseIdx++) {
00757         int idx;
00758         base_t *base = B_GetFoundedBaseByIDX(baseIdx);
00759         if (!base)
00760             continue;
00761 
00762         if (base->baseStatus == BASE_UNDER_ATTACK)
00763             continue;
00764 
00765         for (idx = 0; idx < base->numBatteries; idx++) {
00766             baseWeapon_t *battery = &base->batteries[idx];
00767             aircraftSlot_t *slot = &battery->slot;
00768             if (slot->delayNextShot > 0)
00769                 slot->delayNextShot -= dt;
00770             if (slot->ammoLeft <= 0)
00771                 AII_ReloadWeapon(slot);
00772         }
00773 
00774         for (idx = 0; idx < base->numLasers; idx++) {
00775             baseWeapon_t *battery = &base->lasers[idx];
00776             aircraftSlot_t *slot = &battery->slot;
00777             if (slot->delayNextShot > 0)
00778                 slot->delayNextShot -= dt;
00779             if (slot->ammoLeft <= 0)
00780                 AII_ReloadWeapon(slot);
00781         }
00782 
00783         if (AII_BaseCanShoot(base)) {
00784             if (B_GetBuildingStatus(base, B_DEFENCE_MISSILE))
00785                 AIRFIGHT_BaseShoot(base, base->batteries, base->numBatteries);
00786             if (B_GetBuildingStatus(base, B_DEFENCE_LASER))
00787                 AIRFIGHT_BaseShoot(base, base->lasers, base->numLasers);
00788         }
00789     }
00790 
00791     for (installationIdx = 0; installationIdx < MAX_INSTALLATIONS; installationIdx++) {
00792         int idx;
00793         installation_t *installation = INS_GetFoundedInstallationByIDX(installationIdx);
00794         if (!installation)
00795             continue;
00796 
00797         if (installation->installationTemplate->maxBatteries <= 0)
00798             continue;
00799 
00800         for (idx = 0; idx < installation->installationTemplate->maxBatteries; idx++) {
00801             baseWeapon_t *battery = &installation->batteries[idx];
00802             aircraftSlot_t *slot = &battery->slot;
00803             if (slot->delayNextShot > 0)
00804                 slot->delayNextShot -= dt;
00805             if (slot->ammoLeft <= 0)
00806                 AII_ReloadWeapon(slot);
00807         }
00808 
00809         if (AII_InstallationCanShoot(installation)) {
00810             AIRFIGHT_InstallationShoot(installation, installation->batteries, installation->installationTemplate->maxBatteries);
00811         }
00812     }
00813 }
00814 
00819 qboolean AIRFIGHT_SaveXML (mxml_node_t *parent)
00820 {
00821     int i;
00822 
00823     for (i = 0; i < ccs.numProjectiles; i++) {
00824         int j;
00825         aircraftProjectile_t *projectile = &ccs.projectiles[i];
00826         mxml_node_t *node = mxml_AddNode(parent, SAVE_AIRFIGHT_PROJECTILE);
00827 
00828         mxml_AddString(node, SAVE_AIRFIGHT_ITEMID, projectile->aircraftItem->id);
00829         for (j = 0; j < projectile->numProjectiles; j++)
00830             mxml_AddPos2(node, SAVE_AIRFIGHT_POS, projectile->pos[j]);
00831         mxml_AddPos3(node, SAVE_AIRFIGHT_IDLETARGET, projectile->idleTarget);
00832 
00833         mxml_AddInt(node, SAVE_AIRFIGHT_TIME, projectile->time);
00834         mxml_AddFloat(node, SAVE_AIRFIGHT_ANGLE, projectile->angle);
00835         mxml_AddBoolValue(node, SAVE_AIRFIGHT_BULLET, projectile->bullets);
00836         mxml_AddBoolValue(node, SAVE_AIRFIGHT_BEAM, projectile->beam);
00837 
00838         if (projectile->attackingAircraft) {
00839             mxml_node_t *attacking =  mxml_AddNode(node, SAVE_AIRFIGHT_ATTACKINGAIRCRAFT);
00840 
00841             mxml_AddBoolValue(attacking, SAVE_AIRFIGHT_ISUFO, projectile->attackingAircraft->type == AIRCRAFT_UFO);
00842             if (projectile->attackingAircraft->type == AIRCRAFT_UFO)
00843                 mxml_AddInt(attacking, SAVE_AIRFIGHT_AIRCRAFTIDX, projectile->attackingAircraft - ccs.ufos);
00844             else
00845                 mxml_AddInt(attacking, SAVE_AIRFIGHT_AIRCRAFTIDX, projectile->attackingAircraft->idx);
00846         }
00847 
00848         if (projectile->aimedAircraft) {
00849             mxml_node_t *aimed =  mxml_AddNode(node, SAVE_AIRFIGHT_AIMEDAIRCRAFT);
00850 
00851             mxml_AddBoolValue(aimed, SAVE_AIRFIGHT_ISUFO, projectile->aimedAircraft->type == AIRCRAFT_UFO);
00852             if (projectile->aimedAircraft->type == AIRCRAFT_UFO)
00853                 mxml_AddInt(aimed, SAVE_AIRFIGHT_AIRCRAFTIDX, projectile->aimedAircraft - ccs.ufos);
00854             else
00855                 mxml_AddInt(aimed, SAVE_AIRFIGHT_AIRCRAFTIDX, projectile->aimedAircraft->idx);
00856         }
00857     }
00858 
00859     return qtrue;
00860 }
00861 
00866 qboolean AIRFIGHT_LoadXML (mxml_node_t *parent)
00867 {
00868     int i;
00869     mxml_node_t *node;
00870 
00871     for (i = 0, node = mxml_GetNode(parent, SAVE_AIRFIGHT_PROJECTILE); i < MAX_PROJECTILESONGEOSCAPE && node;
00872             node = mxml_GetNextNode(node, parent, SAVE_AIRFIGHT_PROJECTILE), i++) {
00873         technology_t *tech = RS_GetTechByProvided(mxml_GetString(node, SAVE_AIRFIGHT_ITEMID));
00874         int j;
00875         mxml_node_t *positions;
00876         mxml_node_t *attackingAircraft;
00877         mxml_node_t *aimedAircraft;
00878         aircraftProjectile_t *projectile = &ccs.projectiles[i];
00879 
00880         if (!tech) {
00881             Com_Printf("AIR_Load: Could not get technology of projectile %i\n", i);
00882             return qfalse;
00883         }
00884 
00885         projectile->aircraftItem = INVSH_GetItemByID(tech->provides);
00886 
00887         for (j = 0, positions = mxml_GetPos2(node, SAVE_AIRFIGHT_POS, projectile->pos[0]); j < MAX_MULTIPLE_PROJECTILES && positions;
00888              j++, positions = mxml_GetNextPos2(positions, node, SAVE_AIRFIGHT_POS, projectile->pos[j]))
00889             ;
00890         projectile->numProjectiles = j;
00891         mxml_GetPos3(node, SAVE_AIRFIGHT_IDLETARGET, projectile->idleTarget);
00892 
00893         projectile->time = mxml_GetInt(node, SAVE_AIRFIGHT_TIME, 0);
00894         projectile->angle = mxml_GetFloat(node, SAVE_AIRFIGHT_ANGLE, 0.0);
00895         projectile->bullets = mxml_GetBool(node, SAVE_AIRFIGHT_BULLET, qfalse);
00896         projectile->beam = mxml_GetBool(node, SAVE_AIRFIGHT_BEAM, qfalse);
00897 
00898         if ((attackingAircraft = mxml_GetNode(node, SAVE_AIRFIGHT_ATTACKINGAIRCRAFT))) {
00899             if (mxml_GetBool(attackingAircraft, SAVE_AIRFIGHT_ISUFO, qfalse))
00900                 projectile->attackingAircraft = ccs.ufos + mxml_GetInt(attackingAircraft, SAVE_AIRFIGHT_AIRCRAFTIDX, 0);
00901             else
00902                 projectile->attackingAircraft = AIR_AircraftGetFromIDX(mxml_GetInt(attackingAircraft, SAVE_AIRFIGHT_AIRCRAFTIDX, AIRCRAFT_INVALID));
00903         } else {
00904             projectile->attackingAircraft = NULL;
00905         }
00906         if ((aimedAircraft = mxml_GetNode(node, SAVE_AIRFIGHT_AIMEDAIRCRAFT))) {
00907             if (mxml_GetBool(aimedAircraft, SAVE_AIRFIGHT_ISUFO, qfalse))
00908                 projectile->aimedAircraft = ccs.ufos + mxml_GetInt(aimedAircraft, SAVE_AIRFIGHT_AIRCRAFTIDX, 0);
00909             else
00910                 projectile->aimedAircraft = AIR_AircraftGetFromIDX(mxml_GetInt(aimedAircraft, SAVE_AIRFIGHT_AIRCRAFTIDX, AIRCRAFT_INVALID));
00911         } else {
00912             projectile->aimedAircraft = NULL;
00913         }
00914     }
00915     ccs.numProjectiles = i;
00916 
00917     return qtrue;
00918 }
00919 
00923 void AIRFIGHT_InitStartup (void)
00924 {
00925 #ifdef DEBUG
00926     Cmd_AddCommand("debug_listprojectile", AIRFIGHT_ProjectileList_f, "Print Projectiles information to game console");
00927 #endif
00928 }
00929 

Generated by  doxygen 1.6.2