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