UFO: Alien Invasion
 All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Modules Pages
cp_airfight.cpp
Go to the documentation of this file.
1 
8 /*
9 Copyright (C) 2002-2020 UFO: Alien Invasion.
10 
11 This program is free software; you can redistribute it and/or
12 modify it under the terms of the GNU General Public License
13 as published by the Free Software Foundation; either version 2
14 of the License, or (at your option) any later version.
15 
16 This program is distributed in the hope that it will be useful,
17 but WITHOUT ANY WARRANTY; without even the implied warranty of
18 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
19 
20 See the GNU General Public License for more details.
21 
22 You should have received a copy of the GNU General Public License
23 along with this program; if not, write to the Free Software
24 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
25 */
26 
27 #include "../../cl_shared.h"
28 #include "cp_campaign.h"
29 #include "cp_mapfightequip.h"
30 #include "cp_geoscape.h"
31 #include "cp_ufo.h"
32 #include "cp_missions.h"
33 #include "save/save_airfight.h"
34 #include "../../sound/s_main.h"
35 
37 #define AIRCRAFT_INVALID -1
38 
45 {
46  const ptrdiff_t num = (ptrdiff_t)(projectile - ccs.projectiles);
48 }
49 
61 static bool AIRFIGHT_AddProjectile (const base_t* attackingBase, const installation_t* attackingInstallation, aircraft_t* attacker, aircraft_t* target, aircraftSlot_t* weaponSlot)
62 {
63  aircraftProjectile_t* projectile;
64 
66  cgi->Com_DPrintf(DEBUG_CLIENT, "Too many projectiles on map\n");
67  return false;
68  }
69 
70  projectile = &ccs.projectiles[ccs.numProjectiles];
71 
72  if (!weaponSlot->ammo) {
73  cgi->Com_Printf("AIRFIGHT_AddProjectile: Error - no ammo assigned\n");
74  return false;
75  }
76 
77  assert(weaponSlot->item);
78 
79  projectile->aircraftItem = weaponSlot->ammo;
80  if (attackingBase) {
81  projectile->attackingAircraft = nullptr;
82  VectorSet(projectile->pos[0], attackingBase->pos[0], attackingBase->pos[1], 0);
83  VectorSet(projectile->attackerPos, attackingBase->pos[0], attackingBase->pos[1], 0);
84  } else if (attackingInstallation) {
85  projectile->attackingAircraft = nullptr;
86  VectorSet(projectile->pos[0], attackingInstallation->pos[0], attackingInstallation->pos[1], 0);
87  VectorSet(projectile->attackerPos, attackingInstallation->pos[0], attackingInstallation->pos[1], 0);
88  } else {
89  assert(attacker);
90  projectile->attackingAircraft = attacker;
91  VectorSet(projectile->pos[0], attacker->pos[0], attacker->pos[1], 0);
92  /* attacker may move, use attackingAircraft->pos */
93  VectorSet(projectile->attackerPos, 0, 0, 0);
94  }
95 
96  projectile->numProjectiles++;
97 
98  assert(target);
99  projectile->aimedAircraft = target;
100  VectorSet(projectile->idleTarget, 0, 0, 0);
101 
102  projectile->time = 0;
103  projectile->angle = 0.0f;
104 
105  projectile->bullets = weaponSlot->item->craftitem.bullets;
106  projectile->beam = weaponSlot->item->craftitem.beam;
107  projectile->rocket = !projectile->bullets && !projectile->beam;
108 
109  weaponSlot->ammoLeft--;
110  if (weaponSlot->ammoLeft <= 0)
111  AII_ReloadWeapon(weaponSlot);
112 
114 
115  const char* sound;
116  if (projectile->bullets) {
117  sound = "geoscape/combat-gun";
118  } else if (projectile->beam) {
119  sound = "geoscape/combat-airlaser";
120  } else if (projectile->rocket) {
121  sound = "geoscape/combat-rocket";
122  } else {
123  sound = nullptr;
124  }
125 
126  if (sound != nullptr)
127  cgi->S_StartLocalSample(sound, 1.0f);
128 
129  return true;
130 }
131 
132 #ifdef DEBUG
133 
137 static void AIRFIGHT_ProjectileList_f (void)
138 {
139  for (int i = 0; i < ccs.numProjectiles; i++) {
140  cgi->Com_Printf("%i. (idx: %i)\n", i, ccs.projectiles[i].idx);
141  cgi->Com_Printf("... type '%s'\n", ccs.projectiles[i].aircraftItem->id);
143  cgi->Com_Printf("... shooting aircraft '%s'\n", ccs.projectiles[i].attackingAircraft->id);
144  else
145  cgi->Com_Printf("... base is shooting, or shooting aircraft is destroyed\n");
147  cgi->Com_Printf("... aiming aircraft '%s'\n", ccs.projectiles[i].aimedAircraft->id);
148  else
149  cgi->Com_Printf("... aiming idle target at (%.02f, %.02f)\n",
151  }
152 }
153 #endif
154 
159 static void AIRFIGHT_MissTarget (aircraftProjectile_t* projectile)
160 {
161  vec3_t newTarget;
162  float offset;
163 
164  assert(projectile);
165 
166  if (projectile->aimedAircraft) {
167  VectorCopy(projectile->aimedAircraft->pos, newTarget);
168  projectile->aimedAircraft = nullptr;
169  } else {
170  VectorCopy(projectile->idleTarget, newTarget);
171  }
172 
173  /* get the distance between the projectile and target */
174  const float distance = GetDistanceOnGlobe(projectile->pos[0], newTarget);
175 
176  /* Work out how much the projectile should miss the target by. We dont want it too close
177  * or too far from the original target.
178  * * 1/3 distance between target and projectile * random (range -0.5 to 0.5)
179  * * Then make sure the value is at least greater than 0.1 or less than -0.1 so that
180  * the projectile doesn't land too close to the target. */
181  offset = (distance / 3) * (frand() - 0.5f);
182 
183  if (abs(offset) < 0.1f)
184  offset = 0.1f;
185 
186  newTarget[0] = newTarget[0] + offset;
187  newTarget[1] = newTarget[1] + offset;
188 
189  VectorCopy(newTarget, projectile->idleTarget);
190 }
191 
200 int AIRFIGHT_CheckWeapon (const aircraftSlot_t* slot, float distance)
201 {
202  assert(slot);
203 
204  /* check if there is a functional weapon in this slot */
205  if (!slot->item || slot->installationTime != 0)
207 
208  /* check if there is still ammo in this weapon */
209  if (!slot->ammo || (slot->ammoLeft <= 0))
211 
212  /* check if the target is within range of this weapon */
213  if (distance > slot->ammo->craftitem.stats[AIR_STATS_WRANGE])
215 
216  /* check if weapon is reloaded */
217  if (slot->delayNextShot > 0)
219 
221 }
222 
234 int AIRFIGHT_ChooseWeapon (const aircraftSlot_t* slot, int maxSlot, const vec2_t pos, const vec2_t targetPos)
235 {
236  int slotIdx = AIRFIGHT_WEAPON_CAN_NEVER_SHOOT;
237  float distance0 = 99999.9f;
238  const float distance = GetDistanceOnGlobe(pos, targetPos);
239 
240  /* We choose the usable weapon with the smallest range */
241  for (int i = 0; i < maxSlot; i++) {
242  const int weaponStatus = AIRFIGHT_CheckWeapon(slot + i, distance);
243 
244  /* set slotIdx to AIRFIGHT_WEAPON_CAN_NOT_SHOOT_AT_THE_MOMENT if needed */
245  /* this will only happen if weapon_state is AIRFIGHT_WEAPON_CAN_NOT_SHOOT_AT_THE_MOMENT
246  * and no weapon has been found that can shoot. */
247  if (weaponStatus > slotIdx)
249 
250  /* select this weapon if this is the one with the shortest range */
251  if (weaponStatus >= AIRFIGHT_WEAPON_CAN_SHOOT && distance < distance0) {
252  slotIdx = i;
253  distance0 = distance;
254  }
255  }
256  return slotIdx;
257 }
258 
272 static float AIRFIGHT_ProbabilityToHit (const aircraft_t* shooter, const aircraft_t* target, const aircraftSlot_t* slot)
273 {
274  float probability = 0.0f;
275 
276  if (!slot->item) {
277  cgi->Com_Printf("AIRFIGHT_ProbabilityToHit: no weapon assigned to attacking aircraft\n");
278  return probability;
279  }
280 
281  if (!slot->ammo) {
282  cgi->Com_Printf("AIRFIGHT_ProbabilityToHit: no ammo in weapon of attacking aircraft\n");
283  return probability;
284  }
285 
286  /* Take Base probability from the ammo of the attacking aircraft */
287  probability = slot->ammo->craftitem.stats[AIR_STATS_ACCURACY];
288  cgi->Com_DPrintf(DEBUG_CLIENT, "AIRFIGHT_ProbabilityToHit: Base probability: %f\n", probability);
289 
290  /* Modify this probability by items of the attacking aircraft (stats is in percent) */
291  if (shooter)
292  probability *= shooter->stats[AIR_STATS_ACCURACY] / 100.0f;
293 
294  cgi->Com_DPrintf(DEBUG_CLIENT, "AIRFIGHT_ProbabilityToHit: Probability after accounting items of attacker: %f\n", probability);
295 
296  /* Modify this probability by items of the aimed aircraft (stats is in percent) */
297  if (target)
298  probability /= target->stats[AIR_STATS_ECM] / 100.0f;
299 
300  cgi->Com_DPrintf(DEBUG_CLIENT, "AIRFIGHT_ProbabilityToHit: Probability after accounting ECM of target: %f\n", probability);
301 
302  /* If shooter is a PHALANX craft, check the targeting skills of the pilot */
303  if (shooter && !AIR_IsUFO(shooter)) {
304  if (shooter->pilot) {
310  probability += ( ( ( 1.4f - ( shooter->pilot->chr.score.skills[SKILL_TARGETING] / 100.0f ) ) * ( shooter->pilot->chr.score.skills[SKILL_TARGETING] / 100.0f ) ) - 0.2f );
311 
312  cgi->Com_DPrintf(DEBUG_CLIENT, "AIRFIGHT_ProbabilityToHit: Probability after accounting targeting skill of shooter: %f\n",
313  probability);
314  }
315  }
316 
317  /* If target is a PHALANX craft, check the evading skills of the pilot */
318  if (target && !AIR_IsUFO(target)) {
319  if (target->pilot) {
325  probability -= ( ( ( 1.4f - ( target->pilot->chr.score.skills[SKILL_EVADING] / 100.0f ) ) * ( target->pilot->chr.score.skills[SKILL_EVADING] / 100.0f ) ) - 0.2f );
326 
327  cgi->Com_DPrintf(DEBUG_CLIENT, "AIRFIGHT_ProbabilityToHit: Probability after accounting evasion skill of target: %f\n",
328  probability);
329  }
330  }
331 
332  /* Probability should not exceed 0.95 so there is always a chance to miss */
333  probability = std::min(probability, 0.95f);
334 
335  cgi->Com_DPrintf(DEBUG_CLIENT, "AIRFIGHT_ProbabilityToHit: Probability to hit: %f\n", probability);
336 
337  return probability;
338 }
339 
347 void AIRFIGHT_ExecuteActions (const campaign_t* campaign, aircraft_t* shooter, aircraft_t* target)
348 {
349  /* some asserts */
350  assert(shooter);
351  assert(target);
352 
353  /* Check if the attacking aircraft can shoot */
354  const int slotIdx = AIRFIGHT_ChooseWeapon(shooter->weapons, shooter->maxWeapons, shooter->pos, target->pos);
355 
356  /* if weapon found that can shoot */
357  if (slotIdx >= AIRFIGHT_WEAPON_CAN_SHOOT) {
358  aircraftSlot_t* weaponSlot = &shooter->weapons[slotIdx];
359  const objDef_t* ammo = weaponSlot->ammo;
360 
361  /* shoot */
362  if (AIRFIGHT_AddProjectile(nullptr, nullptr, shooter, target, weaponSlot)) {
363  /* will we miss the target ? */
364  const float probability = frand();
365  cgi->Com_DPrintf(DEBUG_CLIENT, "AIRFIGHT_ExecuteActions: %s - Random probability to hit: %f\n", shooter->name, probability);
366  weaponSlot->delayNextShot = ammo->craftitem.weaponDelay;
367 
368  const float calculatedProbability = AIRFIGHT_ProbabilityToHit(shooter, target, shooter->weapons + slotIdx);
369  cgi->Com_DPrintf(DEBUG_CLIENT, "AIRFIGHT_ExecuteActions: %s - Calculated probability to hit: %f\n", shooter->name, calculatedProbability);
370 
371  if (probability > calculatedProbability)
373 
374  if (!AIR_IsUFO(shooter)) {
375  /* Maybe UFO is going to shoot back ? */
376  UFO_CheckShootBack(campaign, target, shooter);
377  } else {
378  /* an undetected UFO within radar range and firing should become detected */
379  if (!shooter->detected && RADAR_CheckRadarSensored(shooter->pos)) {
380  /* stop time and notify */
381  MSO_CheckAddNewMessage(NT_UFO_ATTACKING, _("Notice"), va(_("A UFO is shooting at %s"), target->name));
383  UFO_DetectNewUFO(shooter);
384  }
385  }
386  }
387  } else if (slotIdx == AIRFIGHT_WEAPON_CAN_NOT_SHOOT_AT_THE_MOMENT) {
388  /* no ammo to fire atm (too far or reloading), pursue target */
389  if (AIR_IsUFO(shooter)) {
392  UFO_SendPursuingAircraft(shooter, target);
393  } else
394  AIR_SendAircraftPursuingUFO(shooter, target);
395  } else {
396  /* no ammo left, or no weapon, proceed with mission */
397  if (AIR_IsUFO(shooter)) {
398  shooter->aircraftTarget = nullptr; /* reset target */
399  CP_UFOProceedMission(campaign, shooter);
400  } else {
401  MS_AddNewMessage(_("Notice"), _("Our aircraft has no more ammo left - returning to home base now."));
402  AIR_AircraftReturnToBase(shooter);
403  }
404  }
405 }
406 
414 {
415  aircraftProjectile_t* projectile;
416  int idx = 0;
417 
418  if (!aircraft)
419  return;
420 
421  for (projectile = ccs.projectiles; idx < ccs.numProjectiles; projectile++, idx++) {
422  if (projectile->aimedAircraft != aircraft)
423  continue;
424 
425  AIRFIGHT_MissTarget(projectile);
426  if (projectile->attackingAircraft && projectile->attackingAircraft->homebase) {
427  assert(!AIR_IsUFO(projectile->attackingAircraft));
429  }
430  }
431 }
432 
439 {
440  aircraftProjectile_t* projectile;
441  int idx;
442 
443  for (idx = 0, projectile = ccs.projectiles; idx < ccs.numProjectiles; projectile++, idx++) {
444  const aircraft_t* attacker = projectile->attackingAircraft;
445 
446  if (attacker == aircraft)
447  projectile->attackingAircraft = nullptr;
448  }
449 }
450 
463 void AIRFIGHT_ActionsAfterAirfight (const campaign_t* campaign, aircraft_t* shooter, aircraft_t* aircraft, bool phalanxWon)
464 {
465  if (phalanxWon) {
466  const byte* color;
467 
468  assert(aircraft);
469 
470  /* change destination of other projectiles aiming aircraft */
472  /* now update the projectile for the destroyed aircraft, too */
474 
475  /* don't remove ufo from global array: the mission is not over yet
476  * UFO are removed from game only at the end of the mission
477  * (in case we need to know what item to collect e.g.) */
478 
479  /* get the color value of the map at the crash position */
480  color = GEO_GetColor(aircraft->pos, MAPTYPE_TERRAIN, nullptr);
481  /* if this color value is not the value for water ...
482  * and we hit the probability to spawn a crashsite mission */
483  if (!MapIsWater(color)) {
484  CP_SpawnCrashSiteMission(aircraft);
485  } else {
486  cgi->Com_DPrintf(DEBUG_CLIENT, "AIRFIGHT_ActionsAfterAirfight: zone: %s (%i:%i:%i)\n", cgi->csi->terrainDefs.getTerrainName(color), color[0], color[1], color[2]);
487  MS_AddNewMessage(_("Interception"), _("UFO interception successful -- UFO lost to sea."));
488  CP_MissionIsOverByUFO(aircraft);
489  }
490 
491  /* skill increase (for aircraft only, base defences skip) */
492  if (shooter) {
493  /* Increase targeting skill of pilot who destroyed UFO. Never more than 70, see AIRFIGHT_ProbabilityToHit() */
494  shooter->pilot->chr.score.skills[SKILL_TARGETING] += 1;
495  shooter->pilot->chr.score.skills[SKILL_TARGETING] = std::min(shooter->pilot->chr.score.skills[SKILL_TARGETING], 70);
496 
497  /* Increase evasion skill of pilot who destroyed UFO if the aircraft it attacked can carry weapons.
498  * Never more than 70, see AIRFIGHT_ProbabilityToHit() */
499  if (aircraft->maxWeapons > 0) {
500  shooter->pilot->chr.score.skills[SKILL_EVADING] += 1;
501  shooter->pilot->chr.score.skills[SKILL_EVADING] = std::min(shooter->pilot->chr.score.skills[SKILL_EVADING], 70);
502  }
503  }
504  } else {
505  /* change destination of other projectiles aiming aircraft */
507 
508  /* and now update the projectile pointers (there still might be some in the air
509  * of the current destroyed aircraft) - this is needed not send the aircraft
510  * back to base as soon as the projectiles will hit their target */
512 
513  /* notify UFOs that a phalanx aircraft has been destroyed */
515 
516  if (!MapIsWater(GEO_GetColor(aircraft->pos, MAPTYPE_TERRAIN, nullptr))) {
517  CP_SpawnRescueMission(aircraft, shooter);
518  } else {
519  /* Destroy the aircraft and everything onboard - the aircraft pointer
520  * is no longer valid after this point */
521  bool pilotSurvived = false;
522  if (AIR_PilotSurvivedCrash(aircraft))
523  pilotSurvived = true;
524 
525  AIR_DestroyAircraft(aircraft, pilotSurvived);
526 
527  if (pilotSurvived)
528  MS_AddNewMessage(_("Interception"), _("Pilot ejected from craft"), MSG_STANDARD);
529  else
530  MS_AddNewMessage(_("Interception"), _("Pilot killed in action"), MSG_STANDARD);
531  }
532 
533  /* Make UFO proceed with its mission, if it has not been already destroyed */
534  if (shooter)
535  CP_UFOProceedMission(campaign, shooter);
536 
537  MS_AddNewMessage(_("Interception"), _("A PHALANX craft has been destroyed"), MSG_DEATH);
538  }
539 }
540 
548 static bool AIRFIGHT_ProjectileReachedTarget (const aircraftProjectile_t* projectile, float movement)
549 {
550  float distance;
551 
552  if (!projectile->aimedAircraft)
553  /* the target is idle, its position is in idleTarget*/
554  distance = GetDistanceOnGlobe(projectile->idleTarget, projectile->pos[0]);
555  else {
556  /* the target is moving, pointer to the other aircraft is aimedAircraft */
557  distance = GetDistanceOnGlobe(projectile->aimedAircraft->pos, projectile->pos[0]);
558  }
559 
560  /* projectile reaches its target */
561  if (distance < movement)
562  return true;
563 
564  assert(projectile->aircraftItem);
565 
566  /* check if the projectile went farther than it's range */
567  distance = (float) projectile->time * projectile->aircraftItem->craftitem.weaponSpeed / (float)SECONDS_PER_HOUR;
568  if (distance > projectile->aircraftItem->craftitem.stats[AIR_STATS_WRANGE])
569  return true;
570 
571  return false;
572 }
573 
582 static int AIRFIGHT_GetDamage (const objDef_t* od, const aircraft_t* target)
583 {
584  int damage;
585 
586  assert(od);
587 
588  /* already destroyed - do nothing */
589  if (target->damage <= 0)
590  return 0;
591 
592  /* base damage is given by the ammo */
593  damage = od->craftitem.weaponDamage;
594 
595  /* reduce damages with shield target */
596  damage -= target->stats[AIR_STATS_SHIELD];
597 
598  return damage;
599 }
600 
607 static void AIRFIGHT_ProjectileHits (const campaign_t* campaign, aircraftProjectile_t* projectile)
608 {
609  aircraft_t* target;
610 
611  assert(projectile);
612  target = projectile->aimedAircraft;
613  assert(target);
614 
615  /* if the aircraft is not on geoscape anymore, do nothing (returned to base) */
616  if (AIR_IsAircraftInBase(target))
617  return;
618 
619  const int damage = AIRFIGHT_GetDamage(projectile->aircraftItem, target);
620 
621  /* apply resulting damages - but only if damage > 0 - because the target might
622  * already be destroyed, and we don't want to execute the actions after airfight
623  * for every projectile */
624  if (damage > 0) {
625  assert(target->damage > 0);
626  target->damage -= damage;
627  if (target->damage <= 0) {
628  /* Target is destroyed */
629  AIRFIGHT_ActionsAfterAirfight(campaign, projectile->attackingAircraft, target, AIR_IsUFO(target));
630  cgi->S_StartLocalSample("geoscape/combat-explosion", 1.0f);
631  } else {
632  if (projectile->rocket)
633  cgi->S_StartLocalSample("geoscape/combat-rocket-exp", 1.0f);
634  }
635  }
636 }
637 
646 static void AIRFIGHT_GetNextPointInPathFromVector (const float* movement, const vec2_t originalPoint, const vec3_t orthogonalVector, vec2_t finalPoint)
647 {
648  vec3_t startPoint, finalVectorPoint;
649 
650  PolarToVec(originalPoint, startPoint);
651  RotatePointAroundVector(finalVectorPoint, orthogonalVector, startPoint, *movement);
652  VecToPolar(finalVectorPoint, finalPoint);
653 }
654 
664 static void AIRFIGHT_GetNextPointInPath (const float* movement, const vec2_t originalPoint, const vec2_t targetPoint, float* angle, vec2_t finalPoint, vec3_t orthogonalVector)
665 {
666  *angle = GEO_AngleOfPath(originalPoint, targetPoint, nullptr, orthogonalVector);
667  AIRFIGHT_GetNextPointInPathFromVector(movement, originalPoint, orthogonalVector, finalPoint);
668 }
669 
675 void AIRFIGHT_CampaignRunProjectiles (const campaign_t* campaign, int dt)
676 {
677  int idx;
678 
679  /* ccs.numProjectiles is changed in AIRFIGHT_RemoveProjectile */
680  for (idx = ccs.numProjectiles - 1; idx >= 0; idx--) {
681  aircraftProjectile_t* projectile = &ccs.projectiles[idx];
682  const float movement = (float) dt * projectile->aircraftItem->craftitem.weaponSpeed / (float)SECONDS_PER_HOUR;
683  projectile->time += dt;
684  projectile->hasMoved = true;
685  projectile->numInterpolationPoints = 0;
686 
687  /* Check if the projectile reached its destination (aircraft or idle point) */
688  if (AIRFIGHT_ProjectileReachedTarget(projectile, movement)) {
689  /* check if it got the ennemy */
690  if (projectile->aimedAircraft)
691  AIRFIGHT_ProjectileHits(campaign, projectile);
692 
693  /* remove the missile from ccs.projectiles[] */
694  AIRFIGHT_RemoveProjectile(projectile);
695  } else {
696  float angle;
697  vec3_t ortogonalVector, finalPoint, projectedPoint;
698 
699  /* missile is moving towards its target */
700  if (projectile->aimedAircraft) {
701  AIRFIGHT_GetNextPointInPath(&movement, projectile->pos[0], projectile->aimedAircraft->pos, &angle, finalPoint, ortogonalVector);
702  AIRFIGHT_GetNextPointInPath(&movement, finalPoint, projectile->aimedAircraft->pos, &angle, projectedPoint, ortogonalVector);
703  } else {
704  AIRFIGHT_GetNextPointInPath(&movement, projectile->pos[0], projectile->idleTarget, &angle, finalPoint, ortogonalVector);
705  AIRFIGHT_GetNextPointInPath(&movement, finalPoint, projectile->idleTarget, &angle, projectedPoint, ortogonalVector);
706  }
707 
708  /* update angle of the projectile */
709  projectile->angle = angle;
710  VectorCopy(finalPoint, projectile->pos[0]);
711  VectorCopy(projectedPoint, projectile->projectedPos[0]);
712  }
713  }
714 }
715 
722 static void AIRFIGHT_BaseShoot (const base_t* base, baseWeapon_t* weapons, int maxWeapons)
723 {
724  for (int i = 0; i < maxWeapons; i++) {
725  aircraft_t* target = weapons[i].target;
726  aircraftSlot_t* slot = &(weapons[i].slot);
727  /* if no target, can't shoot */
728  if (!target)
729  continue;
730 
731  /* If the weapon is not ready in base, can't shoot. */
732  if (slot->installationTime > 0)
733  continue;
734 
735  /* if weapon is reloading, can't shoot */
736  if (slot->delayNextShot > 0)
737  continue;
738 
739  /* check that the ufo is still visible */
740  if (!UFO_IsUFOSeenOnGeoscape(target)) {
741  weapons[i].target = nullptr;
742  continue;
743  }
744 
745  /* Check if we can still fire on this target. */
746  const float distance = GetDistanceOnGlobe(base->pos, target->pos);
747  const int test = AIRFIGHT_CheckWeapon(slot, distance);
748  /* weapon unable to shoot, reset target */
749  if (test == AIRFIGHT_WEAPON_CAN_NEVER_SHOOT) {
750  weapons[i].target = nullptr;
751  continue;
752  }
753  /* we can't shoot with this weapon atm, wait to see if UFO comes closer */
755  continue;
756  /* target is too far, wait to see if UFO comes closer */
757  else if (distance > slot->ammo->craftitem.stats[AIR_STATS_WRANGE])
758  continue;
759 
760  /* shoot */
761  if (AIRFIGHT_AddProjectile(base, nullptr, nullptr, target, slot)) {
762  slot->delayNextShot = slot->ammo->craftitem.weaponDelay;
763  /* will we miss the target ? */
764  if (frand() > AIRFIGHT_ProbabilityToHit(nullptr, target, slot))
766  }
767  }
768 }
769 
776 static void AIRFIGHT_InstallationShoot (const installation_t* installation, baseWeapon_t* weapons, int maxWeapons)
777 {
778  for (int i = 0; i < maxWeapons; i++) {
779  aircraft_t* target = weapons[i].target;
780  aircraftSlot_t* slot = &(weapons[i].slot);
781  /* if no target, can't shoot */
782  if (!target)
783  continue;
784 
785  /* If the weapon is not ready in base, can't shoot. */
786  if (slot->installationTime > 0)
787  continue;
788 
789  /* if weapon is reloading, can't shoot */
790  if (slot->delayNextShot > 0)
791  continue;
792 
793  /* check that the ufo is still visible */
794  if (!UFO_IsUFOSeenOnGeoscape(target)) {
795  weapons[i].target = nullptr;
796  continue;
797  }
798 
799  /* Check if we can still fire on this target. */
800  const float distance = GetDistanceOnGlobe(installation->pos, target->pos);
801  const int test = AIRFIGHT_CheckWeapon(slot, distance);
802  /* weapon unable to shoot, reset target */
803  if (test == AIRFIGHT_WEAPON_CAN_NEVER_SHOOT) {
804  weapons[i].target = nullptr;
805  continue;
806  }
807  /* we can't shoot with this weapon atm, wait to see if UFO comes closer */
809  continue;
810  /* target is too far, wait to see if UFO comes closer */
811  else if (distance > slot->ammo->craftitem.stats[AIR_STATS_WRANGE])
812  continue;
813 
814  /* shoot */
815  if (AIRFIGHT_AddProjectile(nullptr, installation, nullptr, target, slot)) {
816  slot->delayNextShot = slot->ammo->craftitem.weaponDelay;
817  /* will we miss the target ? */
818  if (frand() > AIRFIGHT_ProbabilityToHit(nullptr, target, slot))
820  }
821  }
822 }
823 
829 {
830  base_t* base;
831 
832  base = nullptr;
833  while ((base = B_GetNext(base)) != nullptr) {
834  int idx;
835 
836  if (B_IsUnderAttack(base))
837  continue;
838 
839  for (idx = 0; idx < base->numBatteries; idx++) {
840  baseWeapon_t* battery = &base->batteries[idx];
841  aircraftSlot_t* slot = &battery->slot;
842  if (slot->delayNextShot > 0)
843  slot->delayNextShot -= dt;
844  if (slot->ammoLeft <= 0)
845  AII_ReloadWeapon(slot);
846  }
847 
848  for (idx = 0; idx < base->numLasers; idx++) {
849  baseWeapon_t* battery = &base->lasers[idx];
850  aircraftSlot_t* slot = &battery->slot;
851  if (slot->delayNextShot > 0)
852  slot->delayNextShot -= dt;
853  if (slot->ammoLeft <= 0)
854  AII_ReloadWeapon(slot);
855  }
856 
857  if (AII_BaseCanShoot(base)) {
859  AIRFIGHT_BaseShoot(base, base->batteries, base->numBatteries);
861  AIRFIGHT_BaseShoot(base, base->lasers, base->numLasers);
862  }
863  }
864 
865  INS_Foreach(installation) {
866  if (installation->installationStatus != INSTALLATION_WORKING)
867  continue;
868 
869  if (installation->installationTemplate->maxBatteries <= 0)
870  continue;
871 
872  for (int idx = 0; idx < installation->installationTemplate->maxBatteries; idx++) {
873  baseWeapon_t* battery = &installation->batteries[idx];
874  aircraftSlot_t* slot = &battery->slot;
875  if (slot->delayNextShot > 0)
876  slot->delayNextShot -= dt;
877  if (slot->ammoLeft <= 0)
878  AII_ReloadWeapon(slot);
879  }
880 
881  if (AII_InstallationCanShoot(installation)) {
882  AIRFIGHT_InstallationShoot(installation, installation->batteries, installation->installationTemplate->maxBatteries);
883  }
884  }
885 }
886 
892 {
893  for (int i = 0; i < ccs.numProjectiles; i++) {
894  aircraftProjectile_t* projectile = &ccs.projectiles[i];
896 
897  cgi->XML_AddString(node, SAVE_AIRFIGHT_ITEMID, projectile->aircraftItem->id);
898  for (int j = 0; j < projectile->numProjectiles; j++)
899  cgi->XML_AddPos2(node, SAVE_AIRFIGHT_POS, projectile->pos[j]);
900  cgi->XML_AddPos3(node, SAVE_AIRFIGHT_IDLETARGET, projectile->idleTarget);
901 
902  cgi->XML_AddInt(node, SAVE_AIRFIGHT_TIME, projectile->time);
903  cgi->XML_AddFloat(node, SAVE_AIRFIGHT_ANGLE, projectile->angle);
904  cgi->XML_AddBoolValue(node, SAVE_AIRFIGHT_BULLET, projectile->bullets);
905  cgi->XML_AddBoolValue(node, SAVE_AIRFIGHT_BEAM, projectile->beam);
906 
907  if (projectile->attackingAircraft) {
909 
910  cgi->XML_AddBoolValue(attacking, SAVE_AIRFIGHT_ISUFO, AIR_IsUFO(projectile->attackingAircraft));
911  if (AIR_IsUFO(projectile->attackingAircraft))
912  cgi->XML_AddInt(attacking, SAVE_AIRFIGHT_AIRCRAFTIDX, UFO_GetGeoscapeIDX(projectile->attackingAircraft));
913  else
914  cgi->XML_AddInt(attacking, SAVE_AIRFIGHT_AIRCRAFTIDX, projectile->attackingAircraft->idx);
915  }
916  cgi->XML_AddPos3(node, SAVE_AIRFIGHT_ATTACKERPOS, projectile->attackerPos);
917 
918  if (projectile->aimedAircraft) {
920 
921  cgi->XML_AddBoolValue(aimed, SAVE_AIRFIGHT_ISUFO, AIR_IsUFO(projectile->aimedAircraft));
922  if (AIR_IsUFO(projectile->aimedAircraft))
923  cgi->XML_AddInt(aimed, SAVE_AIRFIGHT_AIRCRAFTIDX, UFO_GetGeoscapeIDX(projectile->aimedAircraft));
924  else
925  cgi->XML_AddInt(aimed, SAVE_AIRFIGHT_AIRCRAFTIDX, projectile->aimedAircraft->idx);
926  }
927  }
928 
929  return true;
930 }
931 
937 {
938  int i;
939  xmlNode_t* node;
940 
941  for (i = 0, node = cgi->XML_GetNode(parent, SAVE_AIRFIGHT_PROJECTILE); i < MAX_PROJECTILESONGEOSCAPE && node;
942  node = cgi->XML_GetNextNode(node, parent, SAVE_AIRFIGHT_PROJECTILE), i++) {
944  int j;
945  xmlNode_t* positions;
946  xmlNode_t* attackingAircraft;
947  xmlNode_t* aimedAircraft;
948  aircraftProjectile_t* projectile = &ccs.projectiles[i];
949 
950  if (!tech) {
951  cgi->Com_Printf("AIR_Load: Could not get technology of projectile %i\n", i);
952  return false;
953  }
954 
955  projectile->aircraftItem = INVSH_GetItemByID(tech->provides);
956 
957  for (j = 0, positions = cgi->XML_GetPos2(node, SAVE_AIRFIGHT_POS, projectile->pos[0]); j < MAX_MULTIPLE_PROJECTILES && positions;
958  j++, positions = cgi->XML_GetNextPos2(positions, node, SAVE_AIRFIGHT_POS, projectile->pos[j]))
959  ;
960  projectile->numProjectiles = j;
961  cgi->XML_GetPos3(node, SAVE_AIRFIGHT_IDLETARGET, projectile->idleTarget);
962 
963  projectile->time = cgi->XML_GetInt(node, SAVE_AIRFIGHT_TIME, 0);
964  projectile->angle = cgi->XML_GetFloat(node, SAVE_AIRFIGHT_ANGLE, 0.0);
965  projectile->bullets = cgi->XML_GetBool(node, SAVE_AIRFIGHT_BULLET, false);
966  projectile->beam = cgi->XML_GetBool(node, SAVE_AIRFIGHT_BEAM, false);
967 
968  if ((attackingAircraft = cgi->XML_GetNode(node, SAVE_AIRFIGHT_ATTACKINGAIRCRAFT))) {
969  if (cgi->XML_GetBool(attackingAircraft, SAVE_AIRFIGHT_ISUFO, false))
971  projectile->attackingAircraft = UFO_GetByIDX(cgi->XML_GetInt(attackingAircraft, SAVE_AIRFIGHT_AIRCRAFTIDX, 0));
972  else
973  projectile->attackingAircraft = AIR_AircraftGetFromIDX(cgi->XML_GetInt(attackingAircraft, SAVE_AIRFIGHT_AIRCRAFTIDX, AIRCRAFT_INVALID));
974  } else {
975  projectile->attackingAircraft = nullptr;
976  }
978 
979  if ((aimedAircraft = cgi->XML_GetNode(node, SAVE_AIRFIGHT_AIMEDAIRCRAFT))) {
980  if (cgi->XML_GetBool(aimedAircraft, SAVE_AIRFIGHT_ISUFO, false))
982  projectile->aimedAircraft = UFO_GetByIDX(cgi->XML_GetInt(aimedAircraft, SAVE_AIRFIGHT_AIRCRAFTIDX, 0));
983  else
984  projectile->aimedAircraft = AIR_AircraftGetFromIDX(cgi->XML_GetInt(aimedAircraft, SAVE_AIRFIGHT_AIRCRAFTIDX, AIRCRAFT_INVALID));
985  } else {
986  projectile->aimedAircraft = nullptr;
987  }
988  }
989  ccs.numProjectiles = i;
990 
991  return true;
992 }
993 
998 {
999 #ifdef DEBUG
1000  cgi->Cmd_AddCommand("debug_listprojectile", AIRFIGHT_ProjectileList_f, "Print Projectiles information to game console");
1001 #endif
1002 }
#define AIRFIGHT_WEAPON_CAN_SHOOT
Definition: cp_airfight.h:36
static void AIRFIGHT_InstallationShoot(const installation_t *installation, baseWeapon_t *weapons, int maxWeapons)
Check if one type of battery (missile or laser) can shoot now.
static bool AIRFIGHT_ProjectileReachedTarget(const aircraftProjectile_t *projectile, float movement)
Check if some projectiles on geoscape reached their destination.
#define VectorCopy(src, dest)
Definition: vector.h:51
aircraftSlot_t slot
Definition: cp_base.h:78
#define AIRFIGHT_WEAPON_CAN_NEVER_SHOOT
Definition: cp_airfight.h:38
bool AIR_IsAircraftInBase(const aircraft_t *aircraft)
Checks whether given aircraft is in its homebase.
#define VectorSet(v, x, y, z)
Definition: vector.h:59
uiMessageListNodeMessage_t * MSO_CheckAddNewMessage(const notify_t messagecategory, const char *title, const char *text, messageType_t type, technology_t *pedia, bool popup)
Adds a new message to message stack. It uses message settings to verify whether sound should be playe...
chrScoreGlobal_t score
Definition: chr_shared.h:387
XML tag constants for savegame.
const objDef_t * INVSH_GetItemByID(const char *id)
Returns the item that belongs to the given id or nullptr if it wasn't found.
Definition: inv_shared.cpp:282
A installation with all it's data.
bool B_GetBuildingStatus(const base_t *const base, const buildingType_t buildingType)
Get the status associated to a building.
Definition: cp_base.cpp:477
#define SAVE_AIRFIGHT_POS
Definition: save_airfight.h:30
char * id
Definition: cp_aircraft.h:119
float GEO_AngleOfPath(const vec2_t start, const vec2_t end, vec3_t direction, vec3_t ortVector)
Select which function should be used for calculating the direction of model on 2D or 3D geoscape...
bool AIR_SendAircraftPursuingUFO(aircraft_t *aircraft, aircraft_t *ufo)
Make the specified aircraft purchasing a UFO.
int AIRFIGHT_CheckWeapon(const aircraftSlot_t *slot, float distance)
Check if the selected weapon can shoot.
const char * va(const char *format,...)
does a varargs printf into a temp buffer, so I don't need to have varargs versions of all text functi...
Definition: shared.cpp:410
void VecToPolar(const vec3_t v, vec2_t a)
Converts vector coordinates into polar coordinates.
Definition: mathlib.cpp:922
static void AIRFIGHT_GetNextPointInPath(const float *movement, const vec2_t originalPoint, const vec2_t targetPoint, float *angle, vec2_t finalPoint, vec3_t orthogonalVector)
Get the next point in the object path based on movement.
#define _(String)
Definition: cl_shared.h:43
bool UFO_IsUFOSeenOnGeoscape(const aircraft_t *ufo)
Check if an aircraft should be seen on geoscape.
Definition: cp_ufo.cpp:989
#define B_IsUnderAttack(base)
Definition: cp_base.h:53
static void AIRFIGHT_BaseShoot(const base_t *base, baseWeapon_t *weapons, int maxWeapons)
Check if one type of battery (missile or laser) can shoot now.
#define SAVE_AIRFIGHT_PROJECTILE
Definition: save_airfight.h:27
#define SAVE_AIRFIGHT_IDLETARGET
Definition: save_airfight.h:31
#define SAVE_AIRFIGHT_ATTACKINGAIRCRAFT
Definition: save_airfight.h:39
static float AIRFIGHT_ProbabilityToHit(const aircraft_t *shooter, const aircraft_t *target, const aircraftSlot_t *slot)
Calculate the probability to hit the enemy.
#define REMOVE_ELEM_ADJUST_IDX(array, index, n)
Definition: common.h:419
csi_t * csi
Definition: cgame.h:100
bool AII_InstallationCanShoot(const installation_t *installation)
Check if the installation has a weapon and ammo.
character_t chr
Definition: cp_employee.h:119
xmlNode_t *IMPORT * XML_GetPos3(xmlNode_t *parent, const char *name, vec3_t pos)
float weaponDamage
Definition: inv_shared.h:249
void AIRFIGHT_InitStartup(void)
aircraft_t * AIR_AircraftGetFromIDX(int aircraftIdx)
Returns aircraft for a given global index.
void RotatePointAroundVector(vec3_t dst, const vec3_t dir, const vec3_t point, float degrees)
Rotate a point around a given vector.
Definition: mathlib.cpp:849
const byte * GEO_GetColor(const vec2_t pos, mapType_t type, bool *coast)
Returns the color value from geoscape of a certain mask (terrain, culture or population) at a given p...
void CP_SpawnCrashSiteMission(aircraft_t *ufo)
Spawn a new crash site after a UFO has been destroyed.
char name[MAX_VAR]
Definition: cp_aircraft.h:120
#define SAVE_AIRFIGHT_ANGLE
Definition: save_airfight.h:33
float stats[AIR_STATS_MAX]
Definition: inv_shared.h:248
#define SAVE_AIRFIGHT_TIME
Definition: save_airfight.h:32
bool AIRFIGHT_SaveXML(xmlNode_t *parent)
Save callback for savegames in XML Format.
Defines all attributes of objects used in the inventory.
Definition: inv_shared.h:264
#define SECONDS_PER_HOUR
Definition: common.h:302
int numProjectiles
Definition: cp_campaign.h:304
void AIRFIGHT_ExecuteActions(const campaign_t *campaign, aircraft_t *shooter, aircraft_t *target)
Decide what an attacking aircraft can do.
bool RADAR_CheckRadarSensored(const vec2_t pos)
Check if the specified position is within base radar range.
Definition: cp_radar.cpp:377
uiMessageListNodeMessage_t * MS_AddNewMessage(const char *title, const char *text, messageType_t type, technology_t *pedia, bool popup, bool playSound)
Adds a new message to message stack.
Definition: cp_messages.cpp:61
#define SAVE_AIRFIGHT_ISUFO
Definition: save_airfight.h:43
A base with all it's data.
Definition: cp_base.h:84
char * provides
Definition: cp_research.h:154
float weaponDelay
Definition: inv_shared.h:251
#define INS_Foreach(var)
static bool AIRFIGHT_AddProjectile(const base_t *attackingBase, const installation_t *attackingInstallation, aircraft_t *attacker, aircraft_t *target, aircraftSlot_t *weaponSlot)
Add a projectile in ccs.projectiles.
Definition: cp_airfight.cpp:61
#define xmlNode_t
Definition: xml.h:24
bool UFO_SendPursuingAircraft(aircraft_t *ufo, aircraft_t *aircraft)
Make the specified UFO pursue a phalanx aircraft.
Definition: cp_ufo.cpp:532
#define SAVE_AIRFIGHT_AIMEDAIRCRAFT
Definition: save_airfight.h:40
#define AIRFIGHT_WEAPON_CAN_NOT_SHOOT_AT_THE_MOMENT
Definition: cp_airfight.h:37
int AIRFIGHT_ChooseWeapon(const aircraftSlot_t *slot, int maxSlot, const vec2_t pos, const vec2_t targetPos)
Choose the weapon an attacking aircraft will use to fire on a target.
#define DEBUG_CLIENT
Definition: defines.h:59
#define UFO_GetGeoscapeIDX(ufo)
Definition: cp_ufo.h:33
vec3_t pos
Definition: cp_aircraft.h:131
bool AIR_PilotSurvivedCrash(const aircraft_t *aircraft)
Determine if an aircraft's pilot survived a crash, based on his piloting skill (and a bit of randomne...
void AIR_AircraftReturnToBase(aircraft_t *aircraft)
Calculates the way back to homebase for given aircraft and returns it.
#define SAVE_AIRFIGHT_ATTACKERPOS
Definition: save_airfight.h:37
int numLasers
Definition: cp_base.h:120
Campaign missions headers.
const objDef_t * ammo
Definition: cp_aircraft.h:85
bool AIRFIGHT_LoadXML(xmlNode_t *parent)
Load callback for savegames in XML Format.
base_t * B_GetNext(base_t *lastBase)
Iterates through founded bases.
Definition: cp_base.cpp:285
#define MAX_MULTIPLE_PROJECTILES
Definition: cp_airfight.h:29
void PolarToVec(const vec2_t a, vec3_t v)
Converts longitude and latitude to a 3D vector in Euclidean coordinates.
Definition: mathlib.cpp:910
void AIRFIGHT_CampaignRunBaseDefence(int dt)
Run base defences.
const cgame_import_t * cgi
void UFO_NotifyPhalanxAircraftRemoved(const aircraft_t *const aircraft)
Notify to UFOs that a Phalanx aircraft has been destroyed.
Definition: cp_ufo.cpp:972
#define MAX_PROJECTILESONGEOSCAPE
Definition: cp_campaign.h:61
static void AIRFIGHT_UpdateProjectileForDestroyedAircraft(const aircraft_t *aircraft)
Set all projectile attackingAircraft pointers to nullptr.
struct aircraft_s * aircraftTarget
Definition: cp_aircraft.h:156
This is the technology parsed from research.ufo.
Definition: cp_research.h:137
ccs_t ccs
Definition: cp_campaign.cpp:62
void CP_UFOProceedMission(const campaign_t *campaign, aircraft_t *ufo)
Make UFO proceed with its mission when the fight with another aircraft is over (and UFO survived)...
#define SAVE_AIRFIGHT_AIRCRAFTIDX
Definition: save_airfight.h:42
aircraftSlot_t weapons[MAX_AIRCRAFTSLOT]
Definition: cp_aircraft.h:143
Header for Geoscape management.
slot of aircraft
Definition: cp_aircraft.h:77
static void AIRFIGHT_MissTarget(aircraftProjectile_t *projectile)
Change destination of projectile to an idle point of the map, close to its former target...
const objDef_t * aircraftItem
Definition: cp_airfight.h:44
QGL_EXTERN GLfloat f
Definition: r_gl.h:114
struct base_s * homebase
Definition: cp_aircraft.h:149
void CP_MissionIsOverByUFO(aircraft_t *ufocraft)
Mission is finished because Phalanx team ended it.
baseWeapon_t batteries[MAX_BASE_SLOT]
Definition: cp_base.h:116
void AIR_DestroyAircraft(aircraft_t *aircraft, bool killPilot)
Removes an aircraft from its base and the game.
void CP_SpawnRescueMission(aircraft_t *aircraft, aircraft_t *ufo)
Spawn a new rescue mission for a crashed (phalanx) aircraft.
aircraft_t * UFO_GetByIDX(const int idx)
returns the UFO on the geoscape with a certain index
Definition: cp_ufo.cpp:85
xmlNode_t *IMPORT * XML_GetPos2(xmlNode_t *parent, const char *name, vec2_t pos)
int maxWeapons
Definition: cp_aircraft.h:144
aircraft_t * attackingAircraft
Definition: cp_airfight.h:54
float frand(void)
Return random values between 0 and 1.
Definition: mathlib.cpp:506
QGL_EXTERN GLint i
Definition: r_gl.h:113
aircraftProjectile_t projectiles[MAX_PROJECTILESONGEOSCAPE]
Definition: cp_campaign.h:303
#define AIR_IsUFO(aircraft)
Definition: cp_aircraft.h:205
void RADAR_AddDetectedUFOToEveryRadar(const aircraft_t *ufo)
Adds detected UFO to any radar in range (if not already detected).
Definition: cp_radar.cpp:333
xmlNode_t *IMPORT * XML_GetNextNode(xmlNode_t *current, xmlNode_t *parent, const char *name)
vec3_t projectedPos[MAX_MULTIPLE_PROJECTILES]
Definition: cp_airfight.h:47
Header for slot management related stuff.
bool detected
Definition: cp_aircraft.h:166
#define SAVE_AIRFIGHT_ITEMID
Definition: save_airfight.h:29
TerrainDefs terrainDefs
Definition: q_shared.h:574
static int AIRFIGHT_GetDamage(const objDef_t *od, const aircraft_t *target)
Calculates the damage value for the airfight.
void UFO_DetectNewUFO(aircraft_t *ufocraft)
Perform actions when a new UFO is detected.
Definition: cp_ufo.cpp:842
#define MapIsWater(color)
Definition: cp_geoscape.h:32
vec_t vec3_t[3]
Definition: ufotypes.h:39
vec_t vec2_t[2]
Definition: ufotypes.h:38
Header file for single player campaign control.
void AIRFIGHT_ActionsAfterAirfight(const campaign_t *campaign, aircraft_t *shooter, aircraft_t *aircraft, bool phalanxWon)
Actions to execute when a fight is done.
bool bullets
Definition: inv_shared.h:253
#define SAVE_AIRFIGHT_BULLET
Definition: save_airfight.h:34
xmlNode_t *IMPORT * XML_AddNode(xmlNode_t *parent, const char *name)
baseWeapon_t lasers[MAX_BASE_SLOT]
Definition: cp_base.h:119
technology_t * RS_GetTechByProvided(const char *idProvided)
returns a pointer to the item tech (as listed in "provides")
int AII_BaseCanShoot(const base_t *base)
Check if the base has weapon and ammo.
#define SAVE_AIRFIGHT_BEAM
Definition: save_airfight.h:35
aircraft_t * aimedAircraft
Definition: cp_airfight.h:57
vec3_t pos
Definition: cp_base.h:91
int stats[AIR_STATS_MAX]
Definition: cp_aircraft.h:159
projectile used during fight between two or more aircraft
Definition: cp_airfight.h:43
An aircraft with all it's data.
Definition: cp_aircraft.h:114
float weaponSpeed
Definition: inv_shared.h:250
voidpf uLong offset
Definition: ioapi.h:45
vec3_t pos[MAX_MULTIPLE_PROJECTILES]
Definition: cp_airfight.h:46
static void AIRFIGHT_ProjectileHits(const campaign_t *campaign, aircraftProjectile_t *projectile)
Solve the result of one projectile hitting an aircraft.
xmlNode_t *IMPORT * XML_GetNextPos2(xmlNode_t *actual, xmlNode_t *parent, const char *name, vec2_t pos)
static void AIRFIGHT_GetNextPointInPathFromVector(const float *movement, const vec2_t originalPoint, const vec3_t orthogonalVector, vec2_t finalPoint)
Get the next point in the object path based on movement converting the positions from polar coordinat...
uint8_t byte
Definition: ufotypes.h:34
craftItem craftitem
Definition: inv_shared.h:331
static void AIRFIGHT_RemoveProjectile(aircraftProjectile_t *projectile)
Remove a projectile from ccs.projectiles.
Definition: cp_airfight.cpp:44
int skills[SKILL_NUM_TYPES]
Definition: chr_shared.h:122
aircraft_t * target
Definition: cp_base.h:79
class Employee * pilot
Definition: cp_aircraft.h:141
void AIRFIGHT_CampaignRunProjectiles(const campaign_t *campaign, int dt)
Update values of projectiles.
const objDef_t * item
Definition: cp_aircraft.h:84
const char * id
Definition: inv_shared.h:268
const char *IMPORT * XML_GetString(xmlNode_t *parent, const char *name)
xmlNode_t *IMPORT * XML_GetNode(xmlNode_t *parent, const char *name)
void AIRFIGHT_RemoveProjectileAimingAircraft(const aircraft_t *aircraft)
Set all projectile aiming a given aircraft to an idle destination.
int numBatteries
Definition: cp_base.h:117
int installationTime
Definition: cp_aircraft.h:89
void UFO_CheckShootBack(const campaign_t *campaign, aircraft_t *ufo, aircraft_t *phalanxAircraft)
Check if the ufo can shoot back at phalanx aircraft.
Definition: cp_ufo.cpp:578
bool AII_ReloadWeapon(aircraftSlot_t *slot)
Reloads an aircraft/defence-system weapon.
double GetDistanceOnGlobe(const vec2_t pos1, const vec2_t pos2)
Calculate distance on the geoscape.
Definition: mathlib.cpp:171
#define AIRCRAFT_INVALID
Definition: cp_airfight.cpp:37
const char * getTerrainName(const byte *const color) const
Translate color value to terrain type.
Definition: q_shared.h:455