00001
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
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
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
00161 distance = GetDistanceOnGlobe(projectile->pos[0], newTarget);
00162
00163
00164
00165
00166
00167
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
00199 if (!slot->item || slot->installationTime != 0)
00200 return AIRFIGHT_WEAPON_CAN_NEVER_SHOOT;
00201
00202
00203 if (!slot->ammo || (slot->ammoLeft <= 0))
00204 return AIRFIGHT_WEAPON_CAN_NEVER_SHOOT;
00205
00206
00207 if (distance > slot->ammo->craftitem.stats[AIR_STATS_WRANGE])
00208 return AIRFIGHT_WEAPON_CAN_NOT_SHOOT_AT_THE_MOMENT;
00209
00210
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
00236 for (i = 0; i < maxSlot; i++) {
00237 assert(slot);
00238 weaponStatus = AIRFIGHT_CheckWeapon(slot + i, distance);
00239
00240
00241
00242
00243 if (weaponStatus > slotIdx)
00244 slotIdx = AIRFIGHT_WEAPON_CAN_NOT_SHOOT_AT_THE_MOMENT;
00245
00246
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
00283 probability = slot->ammo->craftitem.stats[AIR_STATS_ACCURACY];
00284 Com_DPrintf(DEBUG_CLIENT, "AIRFIGHT_ProbabilityToHit: Base probablity: %f\n", probability);
00285
00286
00287 if (shooter)
00288 probability *= shooter->stats[AIR_STATS_ACCURACY] / 100.0f;
00289
00290
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
00309 assert(shooter);
00310 assert(target);
00311
00312
00313 slotIdx = AIRFIGHT_ChooseWeapon(shooter->weapons, shooter->maxWeapons, shooter->pos, target->pos);
00314
00315
00316 if (slotIdx >= AIRFIGHT_WEAPON_CAN_SHOOT) {
00317 const objDef_t *ammo = shooter->weapons[slotIdx].ammo;
00318
00319
00320 if (AIRFIGHT_AddProjectile(NULL, NULL, shooter, target, &(shooter->weapons[slotIdx]))) {
00321
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
00329 UFO_CheckShootBack(target, shooter);
00330 } else {
00331
00332 if (!shooter->detected && RADAR_CheckRadarSensored(shooter->pos)) {
00333
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
00342 if (shooter->type == AIRCRAFT_UFO) {
00345 UFO_SendPursuingAircraft(shooter, target);
00346 } else
00347 AIR_SendAircraftPursuingUFO(shooter, target);
00348 } else {
00349
00350 if (shooter->type == AIRCRAFT_UFO) {
00351 shooter->aircraftTarget = NULL;
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
00417 AIRFIGHT_RemoveProjectileAimingAircraft(aircraft);
00418
00419 AIRFIGHT_UpdateProjectileForDestroyedAircraft(aircraft);
00420
00421
00422
00423
00424
00425
00426 color = MAP_GetColor(aircraft->pos, MAPTYPE_TERRAIN);
00427
00428
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
00438 AIRFIGHT_RemoveProjectileAimingAircraft(aircraft);
00439
00440
00441
00442
00443 AIRFIGHT_UpdateProjectileForDestroyedAircraft(aircraft);
00444
00445
00446 UFO_NotifyPhalanxAircraftRemoved(aircraft);
00447
00448 if (!MapIsWater(MAP_GetColor(aircraft->pos, MAPTYPE_TERRAIN)))
00449 CP_SpawnRescueMission(aircraft, shooter);
00450 else {
00451
00452
00453 AIR_DestroyAircraft(aircraft);
00454 }
00455
00456
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
00477 distance = GetDistanceOnGlobe(projectile->idleTarget, projectile->pos[0]);
00478 else {
00479
00480 distance = GetDistanceOnGlobe(projectile->aimedAircraft->pos, projectile->pos[0]);
00481 }
00482
00483
00484 if (distance < movement)
00485 return qtrue;
00486
00487 assert(projectile->aircraftItem);
00488
00489
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
00512 if (target->damage <= 0)
00513 return 0;
00514
00515
00516 damage = od->craftitem.weaponDamage;
00517
00518
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
00539 if (AIR_IsAircraftInBase(target))
00540 return;
00541
00542 damage = AIRFIGHT_GetDamage(projectile->aircraftItem, target);
00543
00544
00545
00546
00547 if (damage > 0) {
00548 assert(target->damage > 0);
00549 target->damage -= damage;
00550 if (target->damage <= 0)
00551
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
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
00605 if (AIRFIGHT_ProjectileReachedTarget(projectile, movement)) {
00606
00607 if (projectile->aimedAircraft)
00608 AIRFIGHT_ProjectileHits(projectile);
00609
00610
00611 AIRFIGHT_RemoveProjectile(projectile);
00612 } else {
00613 float angle;
00614 vec3_t ortogonalVector, finalPoint, projectedPoint;
00615
00616
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
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
00648 if (!target)
00649 continue;
00650
00651
00652 if (slot->installationTime > 0)
00653 continue;
00654
00655
00656 if (slot->delayNextShot > 0)
00657 continue;
00658
00659
00660 if (!UFO_IsUFOSeenOnGeoscape(target)) {
00661 weapons[i].target = NULL;
00662 continue;
00663 }
00664
00665
00666 distance = GetDistanceOnGlobe(base->pos, target->pos);
00667 test = AIRFIGHT_CheckWeapon(slot, distance);
00668
00669 if (test == AIRFIGHT_WEAPON_CAN_NEVER_SHOOT) {
00670 weapons[i].target = NULL;
00671 continue;
00672 }
00673
00674 else if (test == AIRFIGHT_WEAPON_CAN_NOT_SHOOT_AT_THE_MOMENT)
00675 continue;
00676
00677 else if (distance > slot->ammo->craftitem.stats[AIR_STATS_WRANGE])
00678 continue;
00679
00680
00681 if (AIRFIGHT_AddProjectile(base, NULL, NULL, target, slot)) {
00682 slot->delayNextShot = slot->ammo->craftitem.weaponDelay;
00683
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
00705 if (!target)
00706 continue;
00707
00708
00709 if (slot->installationTime > 0)
00710 continue;
00711
00712
00713 if (slot->delayNextShot > 0)
00714 continue;
00715
00716
00717 if (!UFO_IsUFOSeenOnGeoscape(target)) {
00718 weapons[i].target = NULL;
00719 continue;
00720 }
00721
00722
00723 distance = GetDistanceOnGlobe(installation->pos, target->pos);
00724 test = AIRFIGHT_CheckWeapon(slot, distance);
00725
00726 if (test == AIRFIGHT_WEAPON_CAN_NEVER_SHOOT) {
00727 weapons[i].target = NULL;
00728 continue;
00729 }
00730
00731 else if (test == AIRFIGHT_WEAPON_CAN_NOT_SHOOT_AT_THE_MOMENT)
00732 continue;
00733
00734 else if (distance > slot->ammo->craftitem.stats[AIR_STATS_WRANGE])
00735 continue;
00736
00737
00738 if (AIRFIGHT_AddProjectile(NULL, installation, NULL, target, slot)) {
00739 slot->delayNextShot = slot->ammo->craftitem.weaponDelay;
00740
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