UFO: Alien Invasion
Loading...
Searching...
No Matches
cp_airfight.cpp
Go to the documentation of this file.
1
7
8/*
9Copyright (C) 2002-2025 UFO: Alien Invasion.
10
11This program is free software; you can redistribute it and/or
12modify it under the terms of the GNU General Public License
13as published by the Free Software Foundation; either version 2
14of the License, or (at your option) any later version.
15
16This program is distributed in the hope that it will be useful,
17but WITHOUT ANY WARRANTY; without even the implied warranty of
18MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
19
20See the GNU General Public License for more details.
21
22You should have received a copy of the GNU General Public License
23along with this program; if not, write to the Free Software
24Foundation, 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);
48 REMOVE_ELEM_ADJUST_IDX(ccs.projectiles, num, ccs.numProjectiles);
49}
50
62static 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
66 if (ccs.numProjectiles >= MAX_PROJECTILESONGEOSCAPE) {
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
114 ccs.numProjectiles++;
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
138static 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);
143 if (ccs.projectiles[i].attackingAircraft)
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");
147 if (ccs.projectiles[i].aimedAircraft)
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",
151 ccs.projectiles[i].idleTarget[0], ccs.projectiles[i].idleTarget[1]);
152 }
153}
154#endif
155
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
201int 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
235int AIRFIGHT_ChooseWeapon (const aircraftSlot_t* slot, int maxSlot, const vec2_t pos, const vec2_t targetPos)
236{
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
273static 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
348void 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)
373 AIRFIGHT_MissTarget(&ccs.projectiles[ccs.numProjectiles - 1]);
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."));
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
464void 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
549static 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
583static 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
608static 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
647static 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
665static 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
676void 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
723static 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 */
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)) {
764 /* will we miss the target ? */
765 if (frand() > AIRFIGHT_ProbabilityToHit(nullptr, target, slot))
766 AIRFIGHT_MissTarget(&ccs.projectiles[ccs.numProjectiles - 1]);
767 }
768 }
769}
770
777static 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 */
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)) {
818 /* will we miss the target ? */
819 if (frand() > AIRFIGHT_ProbabilityToHit(nullptr, target, slot))
820 AIRFIGHT_MissTarget(&ccs.projectiles[ccs.numProjectiles - 1]);
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];
896 xmlNode_t* node = cgi->XML_AddNode(parent, SAVE_AIRFIGHT_PROJECTILE);
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) {
909 xmlNode_t* attacking = cgi->XML_AddNode(node, SAVE_AIRFIGHT_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) {
920 xmlNode_t* aimed = cgi->XML_AddNode(node, SAVE_AIRFIGHT_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++) {
944 technology_t* tech = RS_GetTechByProvided(cgi->XML_GetString(node, SAVE_AIRFIGHT_ITEMID));
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 }
978 cgi->XML_GetPos3(node, SAVE_AIRFIGHT_ATTACKERPOS, projectile->attackerPos);
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}
DateTime class definition.
@ SKILL_EVADING
Definition chr_shared.h:50
@ SKILL_TARGETING
Definition chr_shared.h:49
Share stuff between the different cgame implementations.
#define _(String)
Definition cl_shared.h:44
static const short SECONDS_PER_HOUR
Definition DateTime.h:42
character_t chr
#define REMOVE_ELEM_ADJUST_IDX(array, index, n)
Definition common.h:396
void AIR_AircraftReturnToBase(aircraft_t *aircraft)
Calculates the way back to homebase for given aircraft and returns it.
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...
bool AIR_SendAircraftPursuingUFO(aircraft_t *aircraft, aircraft_t *ufo)
Make the specified aircraft purchasing a UFO.
void AIR_DestroyAircraft(aircraft_t *aircraft, bool killPilot)
Removes an aircraft from its base and the game.
bool AIR_IsAircraftInBase(const aircraft_t *aircraft)
Checks whether given aircraft is in its homebase.
aircraft_t * AIR_AircraftGetFromIDX(int aircraftIdx)
Returns aircraft for a given global index.
#define AIR_IsUFO(aircraft)
void AIRFIGHT_CampaignRunBaseDefence(int dt)
Run base defences.
void AIRFIGHT_ExecuteActions(const campaign_t *campaign, aircraft_t *shooter, aircraft_t *target)
Decide what an attacking aircraft can do.
static bool AIRFIGHT_ProjectileReachedTarget(const aircraftProjectile_t *projectile, float movement)
Check if some projectiles on geoscape reached their destination.
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.
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 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.
static void AIRFIGHT_MissTarget(aircraftProjectile_t *projectile)
Change destination of projectile to an idle point of the map, close to its former target.
bool AIRFIGHT_SaveXML(xmlNode_t *parent)
Save callback for savegames in XML Format.
static float AIRFIGHT_ProbabilityToHit(const aircraft_t *shooter, const aircraft_t *target, const aircraftSlot_t *slot)
Calculate the probability to hit the enemy.
void AIRFIGHT_CampaignRunProjectiles(const campaign_t *campaign, int dt)
Update values of projectiles.
static void AIRFIGHT_RemoveProjectile(aircraftProjectile_t *projectile)
Remove a projectile from ccs.projectiles.
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.
void AIRFIGHT_RemoveProjectileAimingAircraft(const aircraft_t *aircraft)
Set all projectile aiming a given aircraft to an idle destination.
static int AIRFIGHT_GetDamage(const objDef_t *od, const aircraft_t *target)
Calculates the damage value for the airfight.
#define AIRCRAFT_INVALID
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...
static void AIRFIGHT_UpdateProjectileForDestroyedAircraft(const aircraft_t *aircraft)
Set all projectile attackingAircraft pointers to nullptr.
bool AIRFIGHT_LoadXML(xmlNode_t *parent)
Load callback for savegames in XML Format.
void AIRFIGHT_InitStartup(void)
static void AIRFIGHT_ProjectileHits(const campaign_t *campaign, aircraftProjectile_t *projectile)
Solve the result of one projectile hitting an aircraft.
int AIRFIGHT_CheckWeapon(const aircraftSlot_t *slot, float distance)
Check if the selected weapon can shoot.
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.
void AIRFIGHT_ActionsAfterAirfight(const campaign_t *campaign, aircraft_t *shooter, aircraft_t *aircraft, bool phalanxWon)
Actions to execute when a fight is done.
#define MAX_MULTIPLE_PROJECTILES
Definition cp_airfight.h:29
#define AIRFIGHT_WEAPON_CAN_NOT_SHOOT_AT_THE_MOMENT
Definition cp_airfight.h:37
#define AIRFIGHT_WEAPON_CAN_SHOOT
Definition cp_airfight.h:36
#define AIRFIGHT_WEAPON_CAN_NEVER_SHOOT
Definition cp_airfight.h:38
base_t * B_GetNext(base_t *lastBase)
Iterates through founded bases.
Definition cp_base.cpp:286
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 B_IsUnderAttack(base)
Definition cp_base.h:53
@ B_DEFENCE_LASER
Definition cp_building.h:66
@ B_DEFENCE_MISSILE
Definition cp_building.h:65
ccs_t ccs
Header file for single player campaign control.
const cgame_import_t * cgi
#define MAX_PROJECTILESONGEOSCAPE
Definition cp_campaign.h:62
@ MAPTYPE_TERRAIN
Definition cp_campaign.h:93
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...
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.
Header for Geoscape management.
#define MapIsWater(color)
Definition cp_geoscape.h:32
#define INS_Foreach(var)
@ INSTALLATION_WORKING
bool AII_ReloadWeapon(aircraftSlot_t *slot)
Reloads an aircraft/defence-system weapon.
int AII_BaseCanShoot(const base_t *base)
Check if the base has weapon and ammo.
bool AII_InstallationCanShoot(const installation_t *installation)
Check if the installation has a weapon and ammo.
Header for slot management related stuff.
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...
@ NT_UFO_ATTACKING
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.
@ MSG_DEATH
Definition cp_messages.h:48
@ MSG_STANDARD
Definition cp_messages.h:35
void CP_SpawnRescueMission(aircraft_t *aircraft, aircraft_t *ufo)
Spawn a new rescue mission for a crashed (phalanx) aircraft.
void CP_MissionIsOverByUFO(aircraft_t *ufocraft)
Mission is finished because Phalanx team ended it.
void CP_SpawnCrashSiteMission(aircraft_t *ufo)
Spawn a new crash site after a UFO has been destroyed.
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).
Campaign missions headers.
bool RADAR_CheckRadarSensored(const vec2_t pos)
Check if the specified position is within base radar range.
Definition cp_radar.cpp:377
void RADAR_AddDetectedUFOToEveryRadar(const aircraft_t *ufo)
Adds detected UFO to any radar in range (if not already detected).
Definition cp_radar.cpp:333
technology_t * RS_GetTechByProvided(const char *idProvided)
returns a pointer to the item tech (as listed in "provides")
void UFO_NotifyPhalanxAircraftRemoved(const aircraft_t *const aircraft)
Notify to UFOs that a Phalanx aircraft has been destroyed.
Definition cp_ufo.cpp:972
aircraft_t * UFO_GetByIDX(const int idx)
returns the UFO on the geoscape with a certain index
Definition cp_ufo.cpp:85
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
void UFO_DetectNewUFO(aircraft_t *ufocraft)
Perform actions when a new UFO is detected.
Definition cp_ufo.cpp:842
bool UFO_IsUFOSeenOnGeoscape(const aircraft_t *ufo)
Check if an aircraft should be seen on geoscape.
Definition cp_ufo.cpp:989
bool UFO_SendPursuingAircraft(aircraft_t *ufo, aircraft_t *aircraft)
Make the specified UFO pursue a phalanx aircraft.
Definition cp_ufo.cpp:532
#define UFO_GetGeoscapeIDX(ufo)
Definition cp_ufo.h:33
#define DEBUG_CLIENT
Definition defines.h:59
const objDef_t * INVSH_GetItemByID(const char *id)
Returns the item that belongs to the given id or nullptr if it wasn't found.
@ AIR_STATS_ECM
Definition inv_shared.h:229
@ AIR_STATS_WRANGE
Definition inv_shared.h:233
@ AIR_STATS_ACCURACY
Definition inv_shared.h:231
@ AIR_STATS_SHIELD
Definition inv_shared.h:228
voidpf uLong offset
Definition ioapi.h:45
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
void PolarToVec(const vec2_t a, vec3_t v)
Converts longitude and latitude to a 3D vector in Euclidean coordinates.
Definition mathlib.cpp:910
double GetDistanceOnGlobe(const vec2_t pos1, const vec2_t pos2)
Calculate distance on the geoscape.
Definition mathlib.cpp:171
void VecToPolar(const vec3_t v, vec2_t a)
Converts vector coordinates into polar coordinates.
Definition mathlib.cpp:922
float frand(void)
Return random values between 0 and 1.
Definition mathlib.cpp:506
QGL_EXTERN GLint i
Definition r_gl.h:113
Specifies sound API?
XML tag constants for savegame.
#define SAVE_AIRFIGHT_IDLETARGET
#define SAVE_AIRFIGHT_ANGLE
#define SAVE_AIRFIGHT_ISUFO
#define SAVE_AIRFIGHT_POS
#define SAVE_AIRFIGHT_BEAM
#define SAVE_AIRFIGHT_ATTACKERPOS
#define SAVE_AIRFIGHT_AIMEDAIRCRAFT
#define SAVE_AIRFIGHT_PROJECTILE
#define SAVE_AIRFIGHT_AIRCRAFTIDX
#define SAVE_AIRFIGHT_BULLET
#define SAVE_AIRFIGHT_ITEMID
#define SAVE_AIRFIGHT_ATTACKINGAIRCRAFT
#define SAVE_AIRFIGHT_TIME
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
An aircraft with all it's data.
aircraftSlot_t weapons[MAX_AIRCRAFTSLOT]
struct aircraft_s * aircraftTarget
struct base_s * homebase
int stats[AIR_STATS_MAX]
class Employee * pilot
char name[MAX_VAR]
projectile used during fight between two or more aircraft
Definition cp_airfight.h:43
const objDef_t * aircraftItem
Definition cp_airfight.h:44
aircraft_t * aimedAircraft
Definition cp_airfight.h:57
vec3_t projectedPos[MAX_MULTIPLE_PROJECTILES]
Definition cp_airfight.h:47
vec3_t pos[MAX_MULTIPLE_PROJECTILES]
Definition cp_airfight.h:46
aircraft_t * attackingAircraft
Definition cp_airfight.h:54
slot of aircraft
Definition cp_aircraft.h:78
const objDef_t * item
Definition cp_aircraft.h:85
const objDef_t * ammo
Definition cp_aircraft.h:86
A base with all it's data.
Definition cp_base.h:84
int numLasers
Definition cp_base.h:120
baseWeapon_t batteries[MAX_BASE_SLOT]
Definition cp_base.h:116
baseWeapon_t lasers[MAX_BASE_SLOT]
Definition cp_base.h:119
vec3_t pos
Definition cp_base.h:91
int numBatteries
Definition cp_base.h:117
aircraftSlot_t slot
Definition cp_base.h:78
aircraft_t * target
Definition cp_base.h:79
chrScoreGlobal_t score
Definition chr_shared.h:406
int skills[SKILL_NUM_TYPES]
Definition chr_shared.h:122
float stats[AIR_STATS_MAX]
Definition inv_shared.h:248
float weaponDelay
Definition inv_shared.h:251
bool bullets
Definition inv_shared.h:253
float weaponSpeed
Definition inv_shared.h:250
float weaponDamage
Definition inv_shared.h:249
A installation with all it's data.
Defines all attributes of objects used in the inventory.
Definition inv_shared.h:264
craftItem craftitem
Definition inv_shared.h:331
const char * id
Definition inv_shared.h:268
This is the technology parsed from research.ufo.
char * provides
vec_t vec3_t[3]
Definition ufotypes.h:39
vec_t vec2_t[2]
Definition ufotypes.h:38
#define VectorCopy(src, dest)
Definition vector.h:51
#define VectorSet(v, x, y, z)
Definition vector.h:59
#define xmlNode_t
Definition xml.h:24