00001
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027
00028 #include "../cl_shared.h"
00029 #include "../ui/ui_main.h"
00030 #include "cp_campaign.h"
00031 #include "cp_mapfightequip.h"
00032 #include "cp_ufo.h"
00033 #include "cp_map.h"
00034 #include "save/save_fightequip.h"
00035
00036 #define UFO_RELOAD_DELAY_MULTIPLIER 2
00037 #define AIRCRAFT_RELOAD_DELAY_MULTIPLIER 2
00038 #define BASE_RELOAD_DELAY_MULTIPLIER 2
00039 #define INSTALLATION_RELOAD_DELAY_MULTIPLIER 2
00040
00046 technology_t **AII_GetCraftitemTechsByType (int type)
00047 {
00048 static technology_t *techList[MAX_TECHNOLOGIES];
00049 int i, j = 0;
00050
00051 for (i = 0; i < csi.numODs; i++) {
00052 const objDef_t *aircraftitem = INVSH_GetItemByIDX(i);
00053 if (aircraftitem->craftitem.type == type) {
00054 technology_t *tech = RS_GetTechForItem(aircraftitem);
00055 assert(j < MAX_TECHNOLOGIES);
00056 techList[j] = tech;
00057 j++;
00058 }
00059
00060 if (j + 1 >= MAX_TECHNOLOGIES) {
00061 Com_Printf("AII_GetCraftitemTechsByType: MAX_TECHNOLOGIES limit hit.\n");
00062 break;
00063 }
00064 }
00065
00066 techList[j] = NULL;
00067 return techList;
00068 }
00069
00076 itemWeight_t AII_GetItemWeightBySize (const objDef_t *od)
00077 {
00078 assert(od);
00079 assert(od->craftitem.type >= 0);
00080
00081 if (od->size < 50)
00082 return ITEM_LIGHT;
00083 else if (od->size < 100)
00084 return ITEM_MEDIUM;
00085 else
00086 return ITEM_HEAVY;
00087 }
00088
00095 qboolean AIM_SelectableCraftItem (const aircraftSlot_t *slot, const technology_t *tech)
00096 {
00097 objDef_t *item;
00098
00099 if (!slot)
00100 return qfalse;
00101
00102 if (!RS_IsResearched_ptr(tech))
00103 return qfalse;
00104
00105 item = INVSH_GetItemByID(tech->provides);
00106 if (!item)
00107 return qfalse;
00108
00109 if (item->craftitem.type >= AC_ITEM_AMMO) {
00110 const objDef_t *weapon = slot->item;
00111 int k;
00112 if (slot->nextItem != NULL)
00113 weapon = slot->nextItem;
00114
00115 if (weapon == NULL)
00116 return qfalse;
00117
00118
00119 for (k = 0; k < weapon->numAmmos; k++) {
00120 const objDef_t *usable = weapon->ammos[k];
00121 if (usable && item->idx == usable->idx)
00122 break;
00123 }
00124 if (k >= weapon->numAmmos)
00125 return qfalse;
00126 }
00127
00129 if (slot->type >= AC_ITEM_AMMO) {
00132 if (!slot->nextItem && item->weapons[0] != slot->item)
00133 return qfalse;
00134
00135
00136 if (slot->nextItem && item->weapons[0] != slot->nextItem)
00137 return qfalse;
00138 }
00139
00140
00141 if (AII_GetItemWeightBySize(item) > slot->size)
00142 return qfalse;
00143
00144
00145
00146
00147 if (slot->aircraft) {
00148 if (!B_BaseHasItem(slot->aircraft->homebase, item))
00149 return qfalse;
00150 } else if (slot->base) {
00151 if (!B_BaseHasItem(slot->base, item))
00152 return qfalse;
00153 }
00154
00155
00156
00157 if (item->craftitem.installationTime == -1 && slot->type < AC_ITEM_AMMO)
00158 return qfalse;
00159
00160 return qtrue;
00161 }
00162
00169 qboolean AIM_PilotAssignedAircraft (const base_t* base, const employee_t* pilot)
00170 {
00171 qboolean found = qfalse;
00172 aircraft_t *aircraft;
00173
00174 aircraft = NULL;
00175 while ((aircraft = AIR_GetNextFromBase(base, aircraft)) != NULL) {
00176 if (aircraft->pilot == pilot) {
00177 found = qtrue;
00178 break;
00179 }
00180 }
00181
00182 return found;
00183 }
00184
00191 void BDEF_AddBattery (basedefenceType_t basedefType, base_t* base)
00192 {
00193 switch (basedefType) {
00194 case BASEDEF_MISSILE:
00195 if (base->numBatteries >= MAX_BASE_SLOT) {
00196 Com_Printf("BDEF_AddBattery: too many missile batteries in base\n");
00197 return;
00198 }
00199 if (base->numBatteries)
00200 base->batteries[base->numBatteries].autofire = base->batteries[0].autofire;
00201 else if (base->numLasers)
00202 base->batteries[base->numBatteries].autofire = base->lasers[0].autofire;
00203 else
00204 base->batteries[base->numBatteries].autofire = qtrue;
00205 base->numBatteries++;
00206 break;
00207 case BASEDEF_LASER:
00208 if (base->numLasers >= MAX_BASE_SLOT) {
00209 Com_Printf("BDEF_AddBattery: too many laser batteries in base\n");
00210 return;
00211 }
00212 base->lasers[base->numLasers].slot.ammoLeft = AMMO_STATUS_NOT_SET;
00213 if (base->numBatteries)
00214 base->lasers[base->numLasers].autofire = base->batteries[0].autofire;
00215 else if (base->numLasers)
00216 base->lasers[base->numLasers].autofire = base->lasers[0].autofire;
00217 else
00218 base->lasers[base->numLasers].autofire = qtrue;
00219 base->numLasers++;
00220 break;
00221 default:
00222 Com_Printf("BDEF_AddBattery: unknown type of base defence system.\n");
00223 }
00224 }
00225
00235 void BDEF_RemoveBattery (base_t *base, basedefenceType_t basedefType, int idx)
00236 {
00237 int i;
00238 assert(base);
00239
00240
00241 switch (basedefType) {
00242 case BASEDEF_MISSILE:
00243
00244 assert(base->numBatteries > 0);
00245
00246 if (idx < 0) {
00247 for (i = 0; i < base->numBatteries; i++) {
00248 if (!base->batteries[i].slot.item) {
00249 idx = i;
00250 break;
00251 }
00252 }
00253 }
00254
00255 if (idx < 0)
00256 idx = base->numBatteries - 1;
00257 REMOVE_ELEM(base->batteries, idx, base->numBatteries);
00258
00259 AII_InitialiseSlot(&base->batteries[base->numBatteries].slot, NULL, base, NULL, AC_ITEM_BASE_MISSILE);
00260 break;
00261 case BASEDEF_LASER:
00262
00263 assert(base->numLasers > 0);
00264
00265 if (idx < 0) {
00266 for (i = 0; i < base->numLasers; i++) {
00267 if (!base->lasers[i].slot.item) {
00268 idx = i;
00269 break;
00270 }
00271 }
00272 }
00273
00274 if (idx < 0)
00275 idx = base->numLasers - 1;
00276 REMOVE_ELEM(base->lasers, idx, base->numLasers);
00277
00278 AII_InitialiseSlot(&base->lasers[base->numLasers].slot, NULL, base, NULL, AC_ITEM_BASE_LASER);
00279 break;
00280 default:
00281 Com_Printf("BDEF_RemoveBattery_f: unknown type of base defence system.\n");
00282 }
00283 }
00284
00289 void BDEF_InitialiseBaseSlots (base_t *base)
00290 {
00291 int i;
00292
00293 for (i = 0; i < MAX_BASE_SLOT; i++) {
00294 baseWeapon_t* battery = &base->batteries[i];
00295 baseWeapon_t* laser = &base->lasers[i];
00296 AII_InitialiseSlot(&battery->slot, NULL, base, NULL, AC_ITEM_BASE_MISSILE);
00297 AII_InitialiseSlot(&laser->slot, NULL, base, NULL, AC_ITEM_BASE_LASER);
00298 battery->autofire = qtrue;
00299 battery->target = NULL;
00300 laser->autofire = qtrue;
00301 laser->target = NULL;
00302 }
00303 }
00304
00309 void BDEF_InitialiseInstallationSlots (installation_t *installation)
00310 {
00311 int i;
00312
00313 for (i = 0; i < installation->installationTemplate->maxBatteries; i++) {
00314 baseWeapon_t* battery = &installation->batteries[i];
00315 AII_InitialiseSlot(&battery->slot, NULL, NULL, installation, AC_ITEM_BASE_MISSILE);
00316 battery->target = NULL;
00317 battery->autofire = qtrue;
00318 }
00319 }
00320
00321
00330 static void AII_UpdateOneInstallationDelay (base_t* base, installation_t* installation, aircraft_t *aircraft, aircraftSlot_t *slot)
00331 {
00332 assert(base || installation);
00333
00334
00335 if (slot->installationTime == 0)
00336 return;
00337 else if (slot->installationTime > 0) {
00338
00339 slot->installationTime--;
00340
00341 if (slot->installationTime <= 0) {
00342
00343 if (aircraft) {
00344 AII_UpdateAircraftStats(aircraft);
00345 Com_sprintf(cp_messageBuffer, lengthof(cp_messageBuffer),
00346 _("%s was successfully installed into aircraft %s at %s."),
00347 _(slot->item->name), aircraft->name, aircraft->homebase->name);
00348 } else if (installation) {
00349 Com_sprintf(cp_messageBuffer, lengthof(cp_messageBuffer), _("%s was successfully installed at installation %s."),
00350 _(slot->item->name), installation->name);
00351 } else {
00352 Com_sprintf(cp_messageBuffer, lengthof(cp_messageBuffer), _("%s was successfully installed at %s."),
00353 _(slot->item->name), base->name);
00354 }
00355 MSO_CheckAddNewMessage(NT_INSTALLATION_INSTALLED, _("Notice"), cp_messageBuffer, qfalse, MSG_STANDARD, NULL);
00356 }
00357 } else if (slot->installationTime < 0) {
00358 const objDef_t *olditem;
00359
00360
00361 slot->installationTime++;
00362 if (slot->installationTime >= 0) {
00363 #ifdef DEBUG
00364 if (aircraft && aircraft->homebase != base)
00365 Sys_Error("AII_UpdateOneInstallationDelay: aircraft->homebase and base pointers are out of sync\n");
00366 #endif
00367 olditem = slot->item;
00368 AII_RemoveItemFromSlot(base, slot, qfalse);
00369 if (aircraft) {
00370 AII_UpdateAircraftStats(aircraft);
00371
00372 if (!slot->item) {
00373 Com_sprintf(cp_messageBuffer, lengthof(cp_messageBuffer),
00374 _("%s was successfully removed from aircraft %s at %s."),
00375 _(olditem->name), aircraft->name, base->name);
00376 MSO_CheckAddNewMessage(NT_INSTALLATION_REMOVED, _("Notice"), cp_messageBuffer, qfalse,
00377 MSG_STANDARD, NULL);
00378 } else {
00379 Com_sprintf(cp_messageBuffer, lengthof(cp_messageBuffer),
00380 _ ("%s was successfully removed, starting installation of %s into aircraft %s at %s"),
00381 _(olditem->name), _(slot->item->name), aircraft->name, base->name);
00382 MSO_CheckAddNewMessage(NT_INSTALLATION_REPLACE, _("Notice"), cp_messageBuffer, qfalse,
00383 MSG_STANDARD, NULL);
00384 }
00385 } else if (!slot->item) {
00386 if (installation) {
00387 Com_sprintf(cp_messageBuffer, lengthof(cp_messageBuffer),
00388 _("%s was successfully removed from installation %s."),
00389 _(olditem->name), installation->name);
00390 } else {
00391 Com_sprintf(cp_messageBuffer, lengthof(cp_messageBuffer), _("%s was successfully removed from %s."),
00392 _(olditem->name), base->name);
00393 }
00394 MSO_CheckAddNewMessage(NT_INSTALLATION_REMOVED, _("Notice"), cp_messageBuffer, qfalse, MSG_STANDARD, NULL);
00395 }
00396 }
00397 }
00398 }
00399
00406 void AII_UpdateInstallationDelay (void)
00407 {
00408 int j, k;
00409
00410 for (j = 0; j < MAX_INSTALLATIONS; j++) {
00411 installation_t *installation = INS_GetFoundedInstallationByIDX(j);
00412 if (!installation)
00413 continue;
00414
00415
00416 for (k = 0; k < installation->installationTemplate->maxBatteries; k++)
00417 AII_UpdateOneInstallationDelay(NULL, installation, NULL, &installation->batteries[k].slot);
00418 }
00419
00420 for (j = 0; j < MAX_BASES; j++) {
00421 aircraft_t *aircraft;
00422 base_t *base = B_GetFoundedBaseByIDX(j);
00423 if (!base)
00424 continue;
00425
00426
00427 for (k = 0; k < base->numBatteries; k++)
00428 AII_UpdateOneInstallationDelay(base, NULL, NULL, &base->batteries[k].slot);
00429 for (k = 0; k < base->numLasers; k++)
00430 AII_UpdateOneInstallationDelay(base, NULL, NULL, &base->lasers[k].slot);
00431
00432
00433 aircraft = NULL;
00434 while ((aircraft = AIR_GetNextFromBase(base, aircraft)) != NULL) {
00435 if (aircraft->homebase) {
00436 assert(aircraft->homebase == base);
00437 if (AIR_IsAircraftInBase(aircraft)) {
00438
00439 for (k = 0; k < aircraft->maxElectronics; k++)
00440 AII_UpdateOneInstallationDelay(base, NULL, aircraft, aircraft->electronics + k);
00441
00442
00443 for (k = 0; k < aircraft->maxWeapons; k++)
00444 AII_UpdateOneInstallationDelay(base, NULL, aircraft, aircraft->weapons + k);
00445
00446
00447 AII_UpdateOneInstallationDelay(base, NULL, aircraft, &aircraft->shield);
00448 }
00449 }
00450 }
00451 }
00452 }
00453
00460 void AII_AutoAddAmmo (aircraftSlot_t *slot)
00461 {
00462 int k;
00463 const objDef_t *item;
00464
00465 assert(slot);
00466
00467
00468 item = slot->nextItem ? slot->nextItem : slot->item;
00469
00470 if (!item)
00471 return;
00472
00473 if (item->craftitem.type > AC_ITEM_WEAPON)
00474 return;
00475
00476 if (slot->nextItem ? slot->nextAmmo : slot->ammo)
00477 return;
00478
00479 for (k = 0; k < item->numAmmos; k++) {
00480 const objDef_t *ammo = item->ammos[k];
00481 if (ammo) {
00482 const technology_t *ammoTech = RS_GetTechForItem(ammo);
00483 if (AIM_SelectableCraftItem(slot, ammoTech)) {
00484 base_t* base;
00485 if (ammo->isVirtual)
00486 base = NULL;
00487 else if (slot->aircraft)
00488 base = slot->aircraft->homebase;
00489 else
00490 base = slot->base;
00491 AII_AddAmmoToSlot(base, ammoTech, slot);
00492 break;
00493 }
00494 }
00495 }
00496 }
00497
00509 void AII_RemoveItemFromSlot (base_t* base, aircraftSlot_t *slot, qboolean ammo)
00510 {
00511 assert(slot);
00512
00513 if (ammo) {
00514
00515 if (slot->ammo) {
00516
00517 if (!slot->ammo->isVirtual) {
00518 if (base)
00519 B_UpdateStorageAndCapacity(base, slot->ammo, 1, qfalse, qfalse);
00520 slot->ammo = NULL;
00521 slot->ammoLeft = 0;
00522 }
00523 }
00524 } else if (slot->item) {
00525
00526 AII_RemoveItemFromSlot(base, slot, qtrue);
00527 if (!slot->item->isVirtual) {
00528 if (base)
00529 B_UpdateStorageAndCapacity(base, slot->item, 1, qfalse, qfalse);
00530
00531 if (slot->nextItem) {
00532
00533 slot->item = slot->nextItem;
00534
00535 slot->ammo = slot->nextAmmo;
00536 if (slot->ammo)
00537 slot->ammoLeft = slot->ammo->ammo;
00538 slot->installationTime = slot->item->craftitem.installationTime;
00539 slot->nextItem = NULL;
00540 slot->nextAmmo = NULL;
00541 } else {
00542 slot->item = NULL;
00543
00544
00545 slot->ammo = NULL;
00546 slot->ammoLeft = 0;
00547 slot->installationTime = 0;
00548 }
00549 }
00550 }
00551 }
00552
00562 void AII_RemoveNextItemFromSlot (base_t* base, aircraftSlot_t *slot, qboolean ammo)
00563 {
00564 assert(slot);
00565
00566 if (ammo) {
00567
00568 if (slot->nextAmmo) {
00569 if (!slot->nextAmmo->isVirtual) {
00570 if (base)
00571 B_UpdateStorageAndCapacity(base, slot->nextAmmo, 1, qfalse, qfalse);
00572 slot->nextAmmo = NULL;
00573 }
00574 }
00575 } else if (slot->nextItem) {
00576
00577 if (base)
00578 B_UpdateStorageAndCapacity(base, slot->nextItem, 1, qfalse, qfalse);
00579 slot->nextItem = NULL;
00580
00581 if (slot->nextAmmo)
00582 AII_RemoveNextItemFromSlot(base, slot, qtrue);
00583
00584
00585 slot->nextAmmo = NULL;
00586 }
00587 }
00588
00594 qboolean AII_ReloadWeapon (aircraftSlot_t *slot)
00595 {
00596 assert(slot);
00597
00598
00599 if (!slot->item)
00600 return qfalse;
00601
00602 if (!slot->ammo)
00603 AII_AutoAddAmmo(slot);
00604
00605 if (!slot->ammo)
00606 return qfalse;
00607
00608 if (slot->ammoLeft >= slot->ammo->ammo)
00609 return qtrue;
00610
00611 if (slot->aircraft) {
00612
00613 if (AIR_IsUFO(slot->aircraft)) {
00614
00615 slot->ammoLeft = slot->ammo->ammo;
00616 slot->delayNextShot = slot->ammo->craftitem.weaponDelay * UFO_RELOAD_DELAY_MULTIPLIER;
00617 } else {
00618
00619
00620 if (!AIR_IsAircraftInBase(slot->aircraft))
00621 return qfalse;
00622
00623 if (!B_BaseHasItem(slot->aircraft->homebase, slot->ammo))
00624 return qfalse;
00625
00626 B_UpdateStorageAndCapacity(slot->aircraft->homebase, slot->ammo, -1, qfalse, qfalse);
00627 slot->ammoLeft = slot->ammo->ammo;
00628 slot->delayNextShot = slot->ammo->craftitem.weaponDelay * AIRCRAFT_RELOAD_DELAY_MULTIPLIER;
00629 }
00630 } else if (slot->base) {
00631
00632
00633 if (!B_BaseHasItem(slot->base, slot->ammo))
00634 return qfalse;
00635
00636 B_UpdateStorageAndCapacity(slot->base, slot->ammo, -1, qfalse, qfalse);
00637 slot->ammoLeft = slot->ammo->ammo;
00638 slot->delayNextShot = slot->ammo->craftitem.weaponDelay * BASE_RELOAD_DELAY_MULTIPLIER;
00639 } else if (slot->installation) {
00640
00641 slot->ammoLeft = slot->ammo->ammo;
00642 slot->delayNextShot = slot->ammo->craftitem.weaponDelay * INSTALLATION_RELOAD_DELAY_MULTIPLIER;
00643 } else {
00644 Com_Error(ERR_DROP, "AII_ReloadWeapon: AircraftSlot not linked anywhere (aircraft/base/installation)!\n");
00645 }
00646 return qtrue;
00647 }
00648
00653 void AII_ReloadAircraftWeapons (aircraft_t *aircraft)
00654 {
00655 int i;
00656
00657 assert(aircraft);
00658
00659 for (i = 0; i < aircraft->maxWeapons; i++) {
00660 AII_ReloadWeapon(&aircraft->weapons[i]);
00661 }
00662 }
00663
00670 void BDEF_ReloadBaseWeapons (base_t *base, installation_t *installation)
00671 {
00672 int i;
00673
00674 if (base && installation)
00675 Com_Error(ERR_DROP, "BDEF_ReloadBattery: Both base and Installation pointer set!\n");
00676 if (!(base || installation))
00677 Com_Error(ERR_DROP, "BDEF_ReloadBattery: Neither base nor Installation pointer set!\n");
00678
00679 if (base) {
00680 for (i = 0; i < base->numBatteries; i++) {
00681 AII_ReloadWeapon(&(base->batteries[i].slot));
00682 }
00683 for (i = 0; i < base->numLasers; i++) {
00684 AII_ReloadWeapon(&(base->lasers[i].slot));
00685 }
00686 } else if (installation) {
00687 for (i = 0; i < installation->numBatteries; i++) {
00688 AII_ReloadWeapon(&(installation->batteries[i].slot));
00689 }
00690 }
00691 }
00692
00701 qboolean AII_AddAmmoToSlot (base_t* base, const technology_t *tech, aircraftSlot_t *slot)
00702 {
00703 const objDef_t *ammo;
00704 const objDef_t *item;
00705 int k;
00706
00707 if (slot == NULL || slot->item == NULL)
00708 return qfalse;
00709
00710 assert(tech);
00711
00712 ammo = INVSH_GetItemByID(tech->provides);
00713 if (!ammo) {
00714 Com_Printf("AII_AddAmmoToSlot: Could not add item (%s) to slot\n", tech->provides);
00715 return qfalse;
00716 }
00717
00718 item = (slot->nextItem) ? slot->nextItem : slot->item;
00719
00720
00721 for (k = 0; k < item->numAmmos; k++) {
00722 const objDef_t *usable = item->ammos[k];
00723 if (usable && ammo->idx == usable->idx)
00724 break;
00725 }
00726 if (k >= item->numAmmos)
00727 return qfalse;
00728
00729
00730
00731 if (base && ammo->craftitem.type <= AC_ITEM_AMMO) {
00732 if (!B_BaseHasItem(base, ammo)) {
00733 Com_Printf("AII_AddAmmoToSlot: No more ammo of this type to equip (%s)\n", ammo->id);
00734 return qfalse;
00735 }
00736 }
00737
00738
00739 if (slot->nextItem) {
00740 if (slot->nextAmmo)
00741 AII_RemoveNextItemFromSlot(base, slot, qtrue);
00742
00743 if (slot->nextAmmo)
00744 return qfalse;
00745 slot->nextAmmo = ammo;
00746 } else {
00747
00748 assert(!slot->nextAmmo);
00749 AII_RemoveItemFromSlot(base, slot, qtrue);
00750
00751 if (slot->ammo)
00752 return qfalse;
00753 slot->ammo = ammo;
00754 }
00755
00756
00757 if (slot->nextItem) {
00758
00759 if (base)
00760 B_UpdateStorageAndCapacity(base, ammo, -1, qfalse, qfalse);
00761 return qtrue;
00762 }
00763 AII_ReloadWeapon(slot);
00764
00765 return qtrue;
00766 }
00767
00779 qboolean AII_AddItemToSlot (base_t *base, const technology_t *tech, aircraftSlot_t *slot, qboolean nextItem)
00780 {
00781 const objDef_t *item;
00782
00783 assert(slot);
00784 assert(tech);
00785
00786 item = INVSH_GetItemByID(tech->provides);
00787 if (!item) {
00788 Com_Printf("AII_AddItemToSlot: Could not add item (%s) to slot\n", tech->provides);
00789 return qfalse;
00790 }
00791
00792 #ifdef DEBUG
00793
00794
00795
00796 if (item->craftitem.type >= AC_ITEM_AMMO) {
00797 Com_Printf("AII_AddItemToSlot: Type of the item to install (%s) should be a weapon, a shield, or electronics (no ammo)\n", item->id);
00798 return qfalse;
00799 }
00800 #endif
00801
00802
00803 if (slot->type != item->craftitem.type) {
00804 Com_Printf("AII_AddItemToSlot: Type of the item to install (%s -- %i) doesn't match type of the slot (%i)\n", item->id, item->craftitem.type, slot->type);
00805 return qfalse;
00806 }
00807
00808
00809 if (base && !B_BaseHasItem(base, item)) {
00810 Com_Printf("AII_AddItemToSlot: No more item of this type to equip (%s)\n", item->id);
00811 return qfalse;
00812 }
00813
00814 if (slot->size >= AII_GetItemWeightBySize(item)) {
00815 if (nextItem)
00816 slot->nextItem = item;
00817 else {
00818 slot->item = item;
00819 slot->installationTime = item->craftitem.installationTime;
00820 }
00821
00822
00823
00824 if (base)
00825 B_UpdateStorageAndCapacity(base, item, -1, qfalse, qfalse);
00826 } else {
00827 Com_Printf("AII_AddItemToSlot: Could not add item '%s' to slot %i (slot-size: %i - item-weight: %i)\n",
00828 item->id, slot->idx, slot->size, item->size);
00829 return qfalse;
00830 }
00831
00832 return qtrue;
00833 }
00834
00841 void AIM_AutoEquipAircraft (aircraft_t *aircraft)
00842 {
00843 int i;
00844 objDef_t *item;
00846 const technology_t *tech = RS_GetTechByID("rs_craft_weapon_sparrowhawk");
00847
00848 if (!tech)
00849 Com_Error(ERR_DROP, "Could not get tech rs_craft_weapon_sparrowhawk");
00850
00851 assert(aircraft);
00852 assert(aircraft->homebase);
00853
00854 item = INVSH_GetItemByID(tech->provides);
00855 if (!item)
00856 return;
00857
00858 for (i = 0; i < aircraft->maxWeapons; i++) {
00859 aircraftSlot_t *slot = &aircraft->weapons[i];
00860 if (slot->size < AII_GetItemWeightBySize(item))
00861 continue;
00862 if (!B_BaseHasItem(aircraft->homebase, item))
00863 continue;
00864 AII_AddItemToSlot(aircraft->homebase, tech, slot, qfalse);
00865 AII_AutoAddAmmo(slot);
00866 slot->installationTime = 0;
00867 }
00868
00869
00870 tech = RS_GetTechByID("rs_craft_weapon_shiva");
00871
00872 if (!tech)
00873 Com_Error(ERR_DROP, "Could not get tech rs_craft_weapon_shiva");
00874
00875 item = INVSH_GetItemByID(tech->provides);
00876
00877 if (!item)
00878 return;
00879
00880 for (i = 0; i < aircraft->maxWeapons; i++) {
00881 aircraftSlot_t *slot = &aircraft->weapons[i];
00882 if (slot->size < AII_GetItemWeightBySize(item))
00883 continue;
00884 if (!B_BaseHasItem(aircraft->homebase, item))
00885 continue;
00886 if (slot->item)
00887 continue;
00888 AII_AddItemToSlot(aircraft->homebase, tech, slot, qfalse);
00889 AII_AutoAddAmmo(slot);
00890 slot->installationTime = 0;
00891 }
00892
00893 AII_UpdateAircraftStats(aircraft);
00894 }
00895
00904 void AII_InitialiseSlot (aircraftSlot_t *slot, aircraft_t *aircraftTemplate, base_t *base, installation_t *installation, aircraftItemType_t type)
00905 {
00906
00907 assert((!base && aircraftTemplate) || (base && !aircraftTemplate) || (installation && !aircraftTemplate));
00908
00909 assert((!base && installation) || (base && !installation) || (!base && !installation));
00910
00911 memset(slot, 0, sizeof(slot));
00912 slot->aircraft = aircraftTemplate;
00913 slot->base = base;
00914 slot->installation = installation;
00915 slot->item = NULL;
00916 slot->ammo = NULL;
00917 slot->nextAmmo = NULL;
00918 slot->size = ITEM_HEAVY;
00919 slot->nextItem = NULL;
00920 slot->type = type;
00922 slot->ammoLeft = AMMO_STATUS_NOT_SET;
00923 slot->installationTime = 0;
00924 }
00925
00932 static qboolean AII_CheckUpdateAircraftStats (const aircraftSlot_t *slot, int stat)
00933 {
00934 assert(slot);
00935
00936
00937 if (!slot->item)
00938 return qfalse;
00939
00940
00941 if (slot->installationTime != 0) {
00942 const objDef_t *item = slot->item;
00943 if (item->craftitem.stats[stat] > 1.0f)
00944 return qfalse;
00945 }
00946
00947 return qtrue;
00948 }
00949
00957 aircraftSlot_t *BDEF_GetBaseSlotByIDX (base_t *base, aircraftItemType_t type, int idx)
00958 {
00959 switch (type) {
00960 case AC_ITEM_BASE_MISSILE:
00961 if (idx < 0) {
00962 int i;
00963 for (i = 0; i < base->numBatteries; i++)
00964 if (!base->batteries[i].slot.item && !base->batteries[i].slot.nextItem)
00965 return &base->batteries[i].slot;
00966 } else if (idx < base->numBatteries)
00967 return &base->batteries[idx].slot;
00968 break;
00969 case AC_ITEM_BASE_LASER:
00970 if (idx < 0) {
00971 int i;
00972 for (i = 0; i < base->numLasers; i++)
00973 if (!base->lasers[i].slot.item && !base->lasers[i].slot.nextItem)
00974 return &base->lasers[i].slot;
00975 } else if (idx < base->numLasers)
00976 return &base->lasers[idx].slot;
00977 break;
00978 default:
00979 break;
00980 }
00981 return NULL;
00982 }
00983
00991 aircraftSlot_t *BDEF_GetInstallationSlotByIDX (installation_t *installation, aircraftItemType_t type, int idx)
00992 {
00993 switch (type) {
00994 case AC_ITEM_BASE_MISSILE:
00995 if (idx < 0) {
00996 int i;
00997 for (i = 0; i < installation->numBatteries; i++)
00998 if (!installation->batteries[i].slot.item && !installation->batteries[i].slot.nextItem)
00999 return &installation->batteries[i].slot;
01000 } else if (idx < installation->numBatteries)
01001 return &installation->batteries[idx].slot;
01002 break;
01003 default:
01004 break;
01005 }
01006 return NULL;
01007 }
01008
01016 aircraftSlot_t *AII_GetAircraftSlotByIDX (aircraft_t *aircraft, aircraftItemType_t type, int idx)
01017 {
01018 switch (type) {
01019 case AC_ITEM_WEAPON:
01020 if (idx < 0) {
01021 int i;
01022 for (i = 0; i < aircraft->maxWeapons; i++)
01023 if (!aircraft->weapons[i].item && !aircraft->weapons[i].nextItem)
01024 return &aircraft->weapons[i];
01025 } else if (idx < aircraft->maxWeapons)
01026 return &aircraft->weapons[idx];
01027 break;
01028 case AC_ITEM_SHIELD:
01029 if (idx == 0 || ((idx < 0) && !aircraft->shield.item && !aircraft->shield.nextItem))
01030 return &aircraft->shield;
01031 break;
01032 case AC_ITEM_ELECTRONICS:
01033 if (idx < 0) {
01034 int i;
01035 for (i = 0; i < aircraft->maxElectronics; i++)
01036 if (!aircraft->electronics[i].item && !aircraft->electronics[i].nextItem)
01037 return &aircraft->electronics[i];
01038 } else if (idx < aircraft->maxElectronics)
01039 return &aircraft->electronics[idx];
01040 break;
01041 default:
01042 break;
01043 }
01044 return NULL;
01045 }
01046
01047
01054 float AIR_GetMaxAircraftWeaponRange (const aircraftSlot_t *slot, int maxSlot)
01055 {
01056 int i;
01057 float range = 0.0f;
01058
01059 assert(slot);
01060
01061
01062 for (i = 0; i < maxSlot; i++) {
01063 const aircraftSlot_t *weapon = slot + i;
01064 const objDef_t *ammo = weapon->ammo;
01065
01066 if (!ammo)
01067 continue;
01068
01069
01070 if (!AII_CheckUpdateAircraftStats(slot, AIR_STATS_WRANGE))
01071 continue;
01072
01073
01074 if (ammo->craftitem.stats[AIR_STATS_WRANGE] > range) {
01075 range = ammo->craftitem.stats[AIR_STATS_WRANGE];
01076 }
01077 }
01078 return range;
01079 }
01080
01085 void AII_RepairAircraft (void)
01086 {
01087 int baseIDX;
01088 const int REPAIR_PER_HOUR = 1;
01090 for (baseIDX = 0; baseIDX < MAX_BASES; baseIDX++) {
01091 base_t *base = B_GetFoundedBaseByIDX(baseIDX);
01092 aircraft_t *aircraft;
01093
01094 aircraft = NULL;
01095 while ((aircraft = AIR_GetNextFromBase(base, aircraft)) != NULL) {
01096 if (!AIR_IsAircraftInBase(aircraft))
01097 continue;
01098 aircraft->damage = min(aircraft->damage + REPAIR_PER_HOUR, aircraft->stats[AIR_STATS_DAMAGE]);
01099 }
01100 }
01101 }
01102
01108 void AII_UpdateAircraftStats (aircraft_t *aircraft)
01109 {
01110 int i, currentStat;
01111 const aircraft_t *source;
01112 const objDef_t *item;
01113
01114 assert(aircraft);
01115
01116 source = aircraft->tpl;
01117
01118 for (currentStat = 0; currentStat < AIR_STATS_MAX; currentStat++) {
01119
01120 if (currentStat == AIR_STATS_WRANGE)
01121 continue;
01122
01123
01124 aircraft->stats[currentStat] = source->stats[currentStat];
01125
01126
01127 for (i = 0; i < aircraft->maxElectronics; i++) {
01128 if (!AII_CheckUpdateAircraftStats(&aircraft->electronics[i], currentStat))
01129 continue;
01130 item = aircraft->electronics[i].item;
01131 if (fabs(item->craftitem.stats[currentStat]) > 2.0f)
01132 aircraft->stats[currentStat] += (int) item->craftitem.stats[currentStat];
01133 else if (!equal(item->craftitem.stats[currentStat], 0))
01134 aircraft->stats[currentStat] *= item->craftitem.stats[currentStat];
01135 }
01136
01137
01138
01139 for (i = 0; i < aircraft->maxWeapons; i++) {
01140 if (!AII_CheckUpdateAircraftStats(&aircraft->weapons[i], currentStat))
01141 continue;
01142 item = aircraft->weapons[i].item;
01143 if (fabs(item->craftitem.stats[currentStat]) > 2.0f)
01144 aircraft->stats[currentStat] += item->craftitem.stats[currentStat];
01145 else if (!equal(item->craftitem.stats[currentStat], 0))
01146 aircraft->stats[currentStat] *= item->craftitem.stats[currentStat];
01147 }
01148
01149
01150 if (AII_CheckUpdateAircraftStats(&aircraft->shield, currentStat)) {
01151 item = aircraft->shield.item;
01152 if (fabs(item->craftitem.stats[currentStat]) > 2.0f)
01153 aircraft->stats[currentStat] += item->craftitem.stats[currentStat];
01154 else if (!equal(item->craftitem.stats[currentStat], 0))
01155 aircraft->stats[currentStat] *= item->craftitem.stats[currentStat];
01156 }
01157 }
01158
01159
01160 aircraft->stats[AIR_STATS_WRANGE] = 1000.0f * AIR_GetMaxAircraftWeaponRange(aircraft->weapons, aircraft->maxWeapons);
01161
01162
01163 if (aircraft->fuel > aircraft->stats[AIR_STATS_FUELSIZE])
01164 aircraft->fuel = aircraft->stats[AIR_STATS_FUELSIZE];
01165
01166
01167 if (aircraft->damage > aircraft->stats[AIR_STATS_DAMAGE])
01168 aircraft->damage = aircraft->stats[AIR_STATS_DAMAGE];
01169
01170
01171 if (aircraft->stats[AIR_STATS_SPEED] < 1)
01172 aircraft->stats[AIR_STATS_SPEED] = 1;
01173
01174
01175 if (aircraft->status == AIR_HOME && aircraft->fuel < aircraft->stats[AIR_STATS_FUELSIZE])
01176 aircraft->status = AIR_REFUEL;
01177 }
01178
01186 static qboolean AII_WeaponsCanShoot (const baseWeapon_t *weapons, int numWeapons)
01187 {
01188 int i;
01189
01190 for (i = 0; i < numWeapons; i++) {
01191 if (AIRFIGHT_CheckWeapon(&weapons[i].slot, 0) != AIRFIGHT_WEAPON_CAN_NEVER_SHOOT)
01192 return qtrue;
01193 }
01194
01195 return qfalse;
01196 }
01197
01204 int AII_BaseCanShoot (const base_t *base)
01205 {
01206 assert(base);
01207
01208
01209 if (B_GetBuildingStatus(base, B_DEFENCE_MISSILE)
01210 && AII_WeaponsCanShoot(base->batteries, base->numBatteries))
01211 return qtrue;
01212
01213
01214 if (B_GetBuildingStatus(base, B_DEFENCE_LASER)
01215 && AII_WeaponsCanShoot(base->lasers, base->numLasers))
01216 return qtrue;
01217
01218 return qfalse;
01219 }
01220
01227 qboolean AII_InstallationCanShoot (const installation_t *installation)
01228 {
01229 assert(installation);
01230
01231 if (installation->founded && installation->installationStatus == INSTALLATION_WORKING
01232 && installation->installationTemplate->maxBatteries > 0) {
01233
01234 return AII_WeaponsCanShoot(installation->batteries, installation->installationTemplate->maxBatteries);
01235 }
01236
01237 return qfalse;
01238 }
01239
01245 static void BDEF_AutoTarget (baseWeapon_t *weapons, int maxWeapons)
01246 {
01247 const installation_t *inst;
01248 const base_t *base;
01249 aircraft_t *closestCraft = NULL;
01250 float minCraftDistance = -1;
01251 aircraft_t *closestAttacker = NULL;
01252 float minAttackerDistance = -1;
01253 const aircraftSlot_t *slot;
01254 int i;
01255
01256 if (maxWeapons <= 0)
01257 return;
01258
01259 slot = &weapons[0].slot;
01261 if (slot->installation) {
01262 inst = slot->installation;
01263 base = NULL;
01264 } else if (slot->base) {
01265 base = slot->base;
01266 inst = NULL;
01267 } else
01268 Com_Error(ERR_DROP, "BDEF_AutoSelectTarget: slot doesn't belong to any base or installation");
01269
01271 for (i = 0; i < ccs.numUFOs; i++) {
01272 aircraft_t *ufo = &ccs.ufos[i];
01273 float distance;
01274
01275 if (!UFO_IsUFOSeenOnGeoscape(ufo))
01276 continue;
01277 distance = GetDistanceOnGlobe(inst ? inst->pos : base->pos, ufo->pos);
01278 if (minCraftDistance < 0 || minCraftDistance > distance) {
01279 minCraftDistance = distance;
01280 closestCraft = ufo;
01281 }
01282 if ((minAttackerDistance < 0 || minAttackerDistance > distance) && ufo->mission
01283 && ((base && ufo->mission->category == INTERESTCATEGORY_BASE_ATTACK && ufo->mission->data == base)
01284 || (inst && ufo->mission->category == INTERESTCATEGORY_INTERCEPT && ufo->mission->data == inst))) {
01285 minAttackerDistance = distance;
01286 closestAttacker = ufo;
01287 }
01288 }
01289
01291 for (i = 0; i < maxWeapons; i++) {
01292 slot = &weapons[i].slot;
01294 if (!weapons[i].autofire)
01295 continue;
01297 if (!slot->item || !slot->ammo)
01298 continue;
01300 if (slot->installationTime > 0)
01301 continue;
01304 if (slot->ammoLeft <= 0)
01305 continue;
01306
01307 if (closestAttacker) {
01308 const int test = AIRFIGHT_CheckWeapon(slot, minAttackerDistance);
01309 if (test != AIRFIGHT_WEAPON_CAN_NEVER_SHOOT
01310 && test != AIRFIGHT_WEAPON_CAN_NOT_SHOOT_AT_THE_MOMENT
01311 && (minAttackerDistance <= slot->ammo->craftitem.stats[AIR_STATS_WRANGE]))
01312 weapons[i].target = closestAttacker;
01313 } else if (closestCraft) {
01314 const int test = AIRFIGHT_CheckWeapon(slot, minCraftDistance);
01315 if (test != AIRFIGHT_WEAPON_CAN_NEVER_SHOOT
01316 && test != AIRFIGHT_WEAPON_CAN_NOT_SHOOT_AT_THE_MOMENT
01317 && (minCraftDistance <= slot->ammo->craftitem.stats[AIR_STATS_WRANGE]))
01318 weapons[i].target = closestCraft;
01319 }
01320 }
01321 }
01322
01326 void BDEF_AutoSelectTarget (void)
01327 {
01328 int i;
01329
01330 for (i = 0; i < ccs.numBases; i++) {
01331 base_t *base = B_GetFoundedBaseByIDX(i);
01332 if (!base)
01333 continue;
01334
01335 BDEF_AutoTarget(base->batteries, base->numBatteries);
01336 BDEF_AutoTarget(base->lasers, base->numLasers);
01337 }
01338
01339 for (i = 0; i < ccs.numInstallations; i++) {
01340 installation_t *inst = INS_GetFoundedInstallationByIDX(i);
01341 if (!inst)
01342 continue;
01343
01344 BDEF_AutoTarget(inst->batteries, inst->numBatteries);
01345 }
01346 }
01347
01353 const char* AII_WeightToName (itemWeight_t weight)
01354 {
01355 switch (weight) {
01356 case ITEM_LIGHT:
01357 return _("Light weight");
01358 break;
01359 case ITEM_MEDIUM:
01360 return _("Medium weight");
01361 break;
01362 case ITEM_HEAVY:
01363 return _("Heavy weight");
01364 break;
01365 default:
01366 return _("Unknown weight");
01367 break;
01368 }
01369 }
01370
01377 void AII_SaveOneSlotXML (mxml_node_t *p, const aircraftSlot_t* slot, qboolean weapon)
01378 {
01379 mxml_AddStringValue(p, SAVE_SLOT_ITEMID, slot->item ? slot->item->id : "");
01380 mxml_AddStringValue(p, SAVE_SLOT_NEXTITEMID, slot->nextItem ? slot->nextItem->id : "");
01381 mxml_AddIntValue(p, SAVE_SLOT_INSTALLATIONTIME, slot->installationTime);
01382
01383
01384 if (!weapon)
01385 return;
01386
01387 mxml_AddIntValue(p, SAVE_SLOT_AMMOLEFT, slot->ammoLeft);
01388 mxml_AddStringValue(p, SAVE_SLOT_AMMOID, slot->ammo ? slot->ammo->id : "");
01389 mxml_AddStringValue(p, SAVE_SLOT_NEXTAMMOID, slot->nextAmmo ? slot->nextAmmo->id : "");
01390 mxml_AddIntValue(p, SAVE_SLOT_DELAYNEXTSHOT, slot->delayNextShot);
01391 }
01392
01401 void AII_LoadOneSlotXML (mxml_node_t *node, aircraftSlot_t* slot, qboolean weapon)
01402 {
01403 const char *name;
01404 name = mxml_GetString(node, SAVE_SLOT_ITEMID);
01405 if (name[0] != '\0') {
01406 technology_t *tech = RS_GetTechByProvided(name);
01407
01408
01409
01410
01411
01412 if (tech)
01413 AII_AddItemToSlot(NULL, tech, slot, qfalse);
01414 }
01415
01416
01417 name = mxml_GetString(node, SAVE_SLOT_NEXTITEMID);
01418 if (name && name[0] != '\0') {
01419 technology_t *tech = RS_GetTechByProvided(name);
01420 if (tech)
01421 AII_AddItemToSlot(NULL, tech, slot, qtrue);
01422 }
01423
01424 slot->installationTime = mxml_GetInt(node, SAVE_SLOT_INSTALLATIONTIME, 0);
01425
01426
01427 if (!weapon)
01428 return;
01429
01430
01431
01432 slot->ammoLeft = mxml_GetInt(node, SAVE_SLOT_AMMOLEFT, 0);
01433 name = mxml_GetString(node, SAVE_SLOT_AMMOID);
01434 if (name && name[0] != '\0') {
01435 technology_t *tech = RS_GetTechByProvided(name);
01436
01437 if (tech)
01438 AII_AddAmmoToSlot(NULL, tech, slot);
01439 }
01440
01441 name = mxml_GetString(node, SAVE_SLOT_NEXTAMMOID);
01442 if (name && name[0] != '\0') {
01443 technology_t *tech = RS_GetTechByProvided(name);
01444 if (tech)
01445 AII_AddAmmoToSlot(NULL, tech, slot);
01446 }
01447 slot->delayNextShot = mxml_GetInt(node, SAVE_SLOT_DELAYNEXTSHOT, 0);
01448 }
01449