/** * @brief Checks if the production requirements are met for a defined amount. * @param[in] amount How many items are planned to be produced. * @param[in] reqs The production requirements of the item that is to be produced. * @param[in] base Pointer to base. * @return how much item/aircraft/etc can be produced */ int PR_RequirementsMet (int amount, const requirements_t* reqs, base_t* base) { int producibleAmount = amount; for (int i = 0; i < reqs->numLinks; i++) { const requirement_t* req = &reqs->links[i]; switch (req->type) { case RS_LINK_ITEM: { const int items = std::min(amount, B_ItemInBase(req->link.od, base) / ((req->amount) ? req->amount : 1)); producibleAmount = std::min(producibleAmount, items); break; } case RS_LINK_ANTIMATTER: { const int am = std::min(amount, B_AntimatterInBase(base) / ((req->amount) ? req->amount : 1)); producibleAmount = std::min(producibleAmount, am); break; } case RS_LINK_TECH: producibleAmount = (RS_IsResearched_ptr(req->link.tech)) ? producibleAmount : 0; break; case RS_LINK_TECH_NOT: producibleAmount = (RS_IsResearched_ptr(req->link.tech)) ? 0 : producibleAmount; break; default: break; } } return producibleAmount; }
/** * @brief Returns names of the UFO is UFO has been researched. * @param[in] ufocraft Pointer to the UFO. */ const char* UFO_AircraftToIDOnGeoscape (const aircraft_t *ufocraft) { const technology_t *tech = ufocraft->tech; assert(tech); if (ufocraft->detectionIdx) return va("%s #%i", (RS_IsResearched_ptr(tech)) ? _(ufocraft->name) : _("UFO"), ufocraft->detectionIdx); return (RS_IsResearched_ptr(tech)) ? _(ufocraft->name) : _("UFO"); }
/** * @brief Prints the description for robots/ugvs. * @param[in] ugvType What type of robot/ugv to print the description for. * @sa BS_MarketClick_f * @sa UP_Article */ void UP_UGVDescription (const ugv_t* ugvType) { static char itemText[512]; const technology_t* tech; assert(ugvType); tech = RS_GetTechByProvided(ugvType->id); assert(tech); cgi->INV_ItemDescription(nullptr); /* Set name of ugv/robot */ cgi->Cvar_Set("mn_itemname", "%s", _(tech->name)); cgi->Cvar_Set("mn_item", "%s", tech->provides); cgi->Cvar_Set("mn_upmetadata", "1"); if (RS_IsResearched_ptr(tech)) { /** @todo make me shiny */ Com_sprintf(itemText, sizeof(itemText), _("%s\n%s"), _(tech->name), ugvType->weapon); } else if (RS_Collected_(tech)) { /** @todo Display crippled info and pre-research text here */ Com_sprintf(itemText, sizeof(itemText), _("Unknown - need to research this")); } else { Com_sprintf(itemText, sizeof(itemText), _("Unknown - need to research this")); } cgi->UI_RegisterText(TEXT_ITEMDESCRIPTION, itemText); }
/** * @return @c true if the technology is available and matches the filter */ static bool AIM_CrafttypeFilter (const base_t *base, aircraftItemType_t filterType, const technology_t *tech) { const objDef_t *item; if (!base) return false; if (!RS_IsResearched_ptr(tech)) return false; item = INVSH_GetItemByID(tech->provides); if (!item) return false; if (item->isVirtual) return false; if (!B_BaseHasItem(base, item)) return false; /* filter by type: special case for ammo because more than 1 type is an ammo type */ if (filterType != AC_ITEM_AMMO) { if (item->craftitem.type != filterType) return false; } else { if (item->craftitem.type < AC_ITEM_AMMO) return false; } /* you can't install an item that does not have an installation time (alien item) * except for ammo which does not have installation time */ if (item->craftitem.installationTime == -1 && filterType >= AC_ITEM_AMMO) return false; return true; }
/** * @brief Update the display of building space for all researched facilities * @sa B_BaseInit_f */ static void B_BuildingSpace_f (void) { int i; base_t *base = B_GetCurrentSelectedBase(); if (!base) return; // Clear existing entries from the UI panel cgi->UI_ExecuteConfunc("clear_bld_space"); for (i = 0; i < ccs.numBuildingTemplates; i++) { const building_t* b = &ccs.buildingTemplates[i]; const baseCapacities_t capType = B_GetCapacityFromBuildingType(b->buildingType); capacities_t cap; /* skip mandatory buildings (like Entrance) which are built automatically */ if (b->mandatory) continue; /* only show already researched buildings */ if (!RS_IsResearched_ptr(b->tech)) continue; if (capType != MAX_CAP) cap = *CAP_Get(base, capType); else OBJZERO(cap); assert(b->tpl); const int count = B_GetNumberOfBuildingsInBaseByTemplate(base, b->tpl); cgi->UI_ExecuteConfunc("show_bld_space \"%s\" \"%s\" %i %i %i %i", _(b->name), b->id, cap.cur, cap.max, count, b->tpl->maxCount); } }
/** * @brief Update the building-list. * @sa B_BuildingInit_f */ static void B_BuildingInit (base_t* base) { int i; linkedList_t *buildingList = NULL; /* maybe someone call this command before the bases are parsed?? */ if (!base) return; for (i = 0; i < ccs.numBuildingTemplates; i++) { building_t *tpl = &ccs.buildingTemplates[i]; /* make an entry in list for this building */ if (tpl->mapPart) { const int numSameBuildings = B_GetNumberOfBuildingsInBaseByTemplate(base, tpl); if (tpl->maxCount >= 0 && tpl->maxCount <= numSameBuildings) continue; /* skip if limit of BASE_SIZE*BASE_SIZE exceeded */ if (numSameBuildings >= BASE_SIZE * BASE_SIZE) continue; /* if the building is researched add it to the list */ if (RS_IsResearched_ptr(tpl->tech)) B_BuildingAddToList(&buildingList, tpl); } } if (base->buildingCurrent) B_DrawBuilding(base->buildingCurrent); else cgi->UI_ExecuteConfunc("mn_buildings_reset"); buildingNumber = LIST_Count(buildingList); cgi->UI_RegisterLinkedListText(TEXT_BUILDINGS, buildingList); }
/** * @brief Alien containment menu init function. * @note Command to call this: ui_aliencont_init * @note Should be called whenever the alien containment menu gets active. */ static void AC_Init_f (void) { base_t* base; if (cgi->Cmd_Argc() < 2) base = B_GetCurrentSelectedBase(); else base = B_GetFoundedBaseByIDX(atoi(cgi->Cmd_Argv(1))); if (!base) { Com_Printf("No base selected\n"); return; } cgi->UI_ExecuteConfunc("ui_aliencont_cap %d %d", CAP_GetCurrent(base, CAP_ALIENS), CAP_GetMax(base, CAP_ALIENS)); cgi->UI_ExecuteConfunc("ui_aliencont_clear"); if (!base->alienContainment) return; linkedList_t* list = base->alienContainment->list(); LIST_Foreach(list, alienCargo_t, item) { const technology_t* tech = RS_GetTechForTeam(item->teamDef); cgi->UI_ExecuteConfunc("ui_aliencont_add \"%s\" \"%s\" \"%s\" \"%s\" \"%s\" %f %d %d", item->teamDef->id, _(item->teamDef->name), tech->id, tech->image, (RS_IsResearched_ptr(tech)) ? _("Researched") : _("Awaiting autopsy"), (1.0f - tech->time / tech->overallTime) * 100, item->alive, item->dead); } cgi->LIST_Delete(&list); }
/** * @brief sets market prices at start of the game * @sa CP_CampaignInit * @sa B_SetUpFirstBase * @sa BS_Load (Market load function) */ void BS_InitMarket (const campaign_t* campaign) { int i; market_t* market = BS_GetMarket(); for (i = 0; i < cgi->csi->numODs; i++) { const objDef_t* od = INVSH_GetItemByIDX(i); if (market->askItems[i] == 0) { market->askItems[i] = od->price; market->bidItems[i] = floor(market->askItems[i] * BID_FACTOR); } if (campaign->marketDef->numItems[i] <= 0) continue; if (RS_IsResearched_ptr(RS_GetTechForItem(od))) { /* the other relevant values were already set above */ market->numItems[i] = campaign->marketDef->numItems[i]; } else { Com_Printf("BS_InitMarket: Could not add item %s to the market - not marked as researched in campaign %s\n", od->id, campaign->id); } } for (i = 0; i < AIRCRAFTTYPE_MAX; i++) { const char* name = cgi->Com_DropShipTypeToShortName((humanAircraftType_t)i); const aircraft_t* aircraft = AIR_GetAircraft(name); if (market->askAircraft[i] == 0) { market->askAircraft[i] = aircraft->price; market->bidAircraft[i] = floor(market->askAircraft[i] * BID_FACTOR); } if (campaign->marketDef->numAircraft[i] <= 0) continue; if (RS_IsResearched_ptr(aircraft->tech)) { /* the other relevant values were already set above */ market->numAircraft[i] = campaign->marketDef->numAircraft[i]; } else { Com_Printf("BS_InitMarket: Could not add aircraft %s to the market - not marked as researched in campaign %s\n", aircraft->id, campaign->id); } } }
/** * @brief Prints the UFOpaedia description for aircraft * @note Also checks whether the aircraft tech is already researched or collected * @sa BS_MarketAircraftDescription * @sa UP_Article */ void UP_AircraftDescription (const technology_t* tech) { cgi->INV_ItemDescription(nullptr); /* ensure that the buffer is emptied in every case */ upBuffer[0] = '\0'; if (RS_IsResearched_ptr(tech)) { const aircraft_t* aircraft = AIR_GetAircraft(tech->provides); for (int i = 0; i < AIR_STATS_MAX; i++) { switch (i) { case AIR_STATS_SPEED: /* speed may be converted to km/h : multiply by pi / 180 * earth_radius */ Q_strcat(upBuffer, sizeof(upBuffer), _("%s:\t%i km/h\n"), UP_AircraftStatToName(i), AIR_AircraftMenuStatsValues(aircraft->stats[i], i)); break; case AIR_STATS_MAXSPEED: /* speed may be converted to km/h : multiply by pi / 180 * earth_radius */ Q_strcat(upBuffer, sizeof(upBuffer), _("%s:\t%i km/h\n"), UP_AircraftStatToName(i), AIR_AircraftMenuStatsValues(aircraft->stats[i], i)); break; case AIR_STATS_FUELSIZE: Q_strcat(upBuffer, sizeof(upBuffer), _("Operational range:\t%i km\n"), AIR_GetOperationRange(aircraft)); break; case AIR_STATS_ACCURACY: Q_strcat(upBuffer, sizeof(upBuffer), _("%s:\t%i\n"), UP_AircraftStatToName(i), AIR_AircraftMenuStatsValues(aircraft->stats[i], i)); break; default: break; } } const baseCapacities_t cap = AIR_GetCapacityByAircraftWeight(aircraft); const buildingType_t buildingType = B_GetBuildingTypeByCapacity(cap); const building_t* building = B_GetBuildingTemplateByType(buildingType); Q_strcat(upBuffer, sizeof(upBuffer), _("Required Hangar:\t%s\n"), _(building->name)); /* @note: while MAX_ACTIVETEAM limits the number of soldiers on a craft * there is no use to show this in case of an UFO (would be misleading): */ if (!AIR_IsUFO(aircraft)) Q_strcat(upBuffer, sizeof(upBuffer), _("Max. soldiers:\t%i\n"), aircraft->maxTeamSize); } else if (RS_Collected_(tech)) { /** @todo Display crippled info and pre-research text here */ Com_sprintf(upBuffer, sizeof(upBuffer), _("Unknown - need to research this")); } else { Com_sprintf(upBuffer, sizeof(upBuffer), _("Unknown - need to research this")); } cgi->Cvar_Set("mn_upmetadata", "1"); cgi->UI_RegisterText(TEXT_ITEMDESCRIPTION, upBuffer); UP_DisplayTechTree(tech); }
/** * @brief Open research menu. */ static void AC_ResearchAlien_f (void) { const technology_t *tech; /* Can be called from everywhere. */ if (!aliencontCurrent) return; tech = aliencontCurrent->tech; if (!tech) Com_Error(ERR_DROP, "aliencontCurrent without tech pointer"); if (!RS_IsResearched_ptr(tech)) cgi->UI_PushWindow("research"); }
/** * @brief Checks If a technology/UFOpaedia-entry will be displayed in the UFOpaedia (-list). * @note This does not check for different display modes (only pre-research text, what statistics, etc...). The * content is mostly checked in @c UP_Article * @return true if the tech gets displayed at all, otherwise false. * @sa UP_Article */ static bool UP_TechGetsDisplayed (const technology_t* tech) { const objDef_t* item; assert(tech); /* virtual items are hidden */ item = INVSH_GetItemByIDSilent(tech->provides); if (item && item->isVirtual) return false; /* Is already researched OR has collected items OR (researchable AND have description) * AND not a logical block AND not redirected */ return (RS_IsResearched_ptr(tech) || RS_Collected_(tech) || (tech->statusResearchable && tech->preDescription.numDescriptions > 0)) && tech->type != RS_LOGIC && !tech->redirect; }
/** * @brief * @sa BS_MarketClick_f * @sa BS_AddToList */ static void BS_MarketScroll_f (void) { int i; base_t *base = B_GetCurrentSelectedBase(); if (!base || buyCat >= MAX_FILTERTYPES || buyCat < 0) return; if (Cmd_Argc() < 2) { Com_Printf("Usage: %s <scrollpos>\n", Cmd_Argv(0)); return; } buyList.scroll = atoi(Cmd_Argv(1)); assert(buyList.scroll >= 0); assert(!((buyList.length > MAX_MARKET_MENU_ENTRIES && buyList.scroll >= buyList.length - MAX_MARKET_MENU_ENTRIES))); /* now update the menu pics */ for (i = 0; i < MAX_MARKET_MENU_ENTRIES; i++) { UI_ExecuteConfunc("buy_autoselli %i", i); } /* get item list */ for (i = buyList.scroll; i < buyList.length - buyList.scroll; i++) { const objDef_t *od = BS_GetObjectDefition(&buyList.l[i]); if (i >= MAX_MARKET_MENU_ENTRIES) break; /* Check whether the item matches the proper filter, storage in current base and market. */ if (od && (B_ItemInBase(od, base) > 0 || ccs.eMarket.numItems[od->idx]) && INV_ItemMatchesFilter(od, buyCat)) { const technology_t *tech = RS_GetTechForItem(od); UI_ExecuteConfunc("buy_show %i", i - buyList.scroll); BS_UpdateItem(base, i - buyList.scroll); /* autosell setting */ if (!RS_IsResearched_ptr(tech)) continue; if (ccs.eMarket.autosell[od->idx]) UI_ExecuteConfunc("buy_autoselle %i", i - buyList.scroll); else UI_ExecuteConfunc("buy_autoselld %i", i - buyList.scroll); } } }
/** * @brief Fills create installation / installation type selection popup */ static void INS_FillTypes_f (void) { cgi->UI_ExecuteConfunc("installationtype_clear"); if (INS_GetCount() < B_GetInstallationLimit()) { for (int i = 0; i < ccs.numInstallationTemplates; i++) { const installationTemplate_t* tpl = &ccs.installationTemplates[i]; if (tpl->once && INS_HasType(tpl->type, INSTALLATION_NOT_USED)) continue; if (tpl->tech == nullptr || RS_IsResearched_ptr(tpl->tech)) { cgi->UI_ExecuteConfunc("installationtype_add \"%s\" \"%s\" \"%s\" \"%d c\"", tpl->id, _(tpl->name), (tpl->buildTime > 0) ? va("%d %s", tpl->buildTime, ngettext("day", "days", tpl->buildTime)) : "-", tpl->cost); } } } /** @todo Move this out from installations code */ if (B_GetCount() < MAX_BASES) cgi->UI_ExecuteConfunc("installationtype_add base \"%s\" - \"%d c\"", _("Base"), ccs.curCampaign->basecost); }
/** * @brief Sets the amount of unread/new mails * @note This is called every campaign frame - to update ccs.numUnreadMails * just set it to -1 before calling this function * @sa CP_CampaignRun */ int UP_GetUnreadMails (void) { const uiMessageListNodeMessage_t* m = cgi->UI_MessageGetStack(); if (ccs.numUnreadMails != -1) return ccs.numUnreadMails; ccs.numUnreadMails = 0; while (m) { switch (m->type) { case MSG_RESEARCH_PROPOSAL: assert(m->pedia); if (m->pedia->mail[TECHMAIL_PRE].from && !m->pedia->mail[TECHMAIL_PRE].read) ccs.numUnreadMails++; break; case MSG_RESEARCH_FINISHED: assert(m->pedia); if (m->pedia->mail[TECHMAIL_RESEARCHED].from && RS_IsResearched_ptr(m->pedia) && !m->pedia->mail[TECHMAIL_RESEARCHED].read) ccs.numUnreadMails++; break; case MSG_NEWS: assert(m->pedia); if (m->pedia->mail[TECHMAIL_PRE].from && !m->pedia->mail[TECHMAIL_PRE].read) ccs.numUnreadMails++; if (m->pedia->mail[TECHMAIL_RESEARCHED].from && !m->pedia->mail[TECHMAIL_RESEARCHED].read) ccs.numUnreadMails++; break; case MSG_EVENT: assert(m->eventMail); if (!m->eventMail->read) ccs.numUnreadMails++; break; default: break; } m = m->next; } /* use strings here */ cgi->Cvar_Set("mn_upunreadmail", "%i", ccs.numUnreadMails); return ccs.numUnreadMails; }
/** * @brief Selects installation type to build */ static void INS_SelectType_f (void) { if (cgi->Cmd_Argc() < 2) return; const char* id = cgi->Cmd_Argv(1); if (ccs.mapAction == MA_NEWINSTALLATION) { GEO_ResetAction(); return; } const installationTemplate_t* tpl = INS_GetInstallationTemplateByID(id); if (!tpl) { Com_Printf("Invalid installation template\n"); return; } if (INS_GetCount() >= B_GetInstallationLimit()) { Com_Printf("Maximum number of installations reached\n"); return; } if (tpl->tech != nullptr && !RS_IsResearched_ptr(tpl->tech)) { Com_Printf("This type of installation is not yet researched\n"); return; } if (tpl->once && INS_HasType(tpl->type, INSTALLATION_NOT_USED)) { Com_Printf("Cannot build more of this installation\n"); return; } ccs.mapAction = MA_NEWINSTALLATION; /* show radar overlay (if not already displayed) */ if (tpl->type == INSTALLATION_RADAR && !GEO_IsRadarOverlayActivated()) GEO_SetOverlay("radar"); INS_SetInstallationTitle(tpl->type); cgi->Cvar_Set("mn_installation_type", "%s", tpl->id); }
/** * @brief Sets/unsets or flips the autosell property of an item on the market */ static void BS_SetAutosell_f (void) { const objDef_t* od; const technology_t* tech; if (cgi->Cmd_Argc() < 2) { cgi->Com_Printf("Usage: %s <item-id> [0|1]\nWhere second parameter is the state (off/on), if omitted the autosell property will be flipped.\n", cgi->Cmd_Argv(0)); return; } /* aircraft check */ if (AIR_GetAircraftSilent(cgi->Cmd_Argv(1)) != nullptr) { cgi->Com_Printf("Aircraft can't be autosold!\n"); return; } /* items */ od = INVSH_GetItemByID(cgi->Cmd_Argv(1)); if (!od) { /* no printf, INVSH_GetItemByID gave warning already */ return; } if (od->isVirtual) { cgi->Com_Printf("Item %s is virtual, can't be autosold!\n", od->id); return; } if (od->notOnMarket) { cgi->Com_Printf("Item %s is not on market, can't be autosold!\n", od->id); return; } tech = RS_GetTechForItem(od); /* Don't allow to enable autosell for items not researched. */ if (!RS_IsResearched_ptr(tech)) { cgi->Com_Printf("Item %s is not researched, can't be autosold!\n", od->id); return; } if (cgi->Cmd_Argc() >= 3) ccs.eMarket.autosell[od->idx] = atoi(cgi->Cmd_Argv(2)); else ccs.eMarket.autosell[od->idx] = ! ccs.eMarket.autosell[od->idx]; }
/** * @brief Returns if storing a specific life form is supported by the containment * @param[in] team Pointer to the alien Team Definition */ bool AlienContainment::isLifeSupported(const teamDef_t* team) { /* No team - not supported */ if (!team) return false; /* humans supported */ if (!CHRSH_IsTeamDefAlien(team)) return true; /* Robots are supported */ if (CHRSH_IsTeamDefRobot(team)) return true; /* Organic aliens need breathing apparatus known */ /** @todo find a way that doesn't need a tech ID hardcoded */ const technology_t* tech = RS_GetTechByID(BREATHINGAPPARATUS_TECH); if (!tech) return false; return RS_IsResearched_ptr(tech); }
/** * @brief Enable or disable autosell option for given itemtype. */ static void BS_Autosell_f (void) { int num; const objDef_t *item; base_t *base = B_GetCurrentSelectedBase(); /* Can be called from everywhere. */ if (!base) return; if (Cmd_Argc() < 2) { Com_Printf("Usage: %s <num>\n", Cmd_Argv(0)); return; } num = atoi(Cmd_Argv(1)); Com_DPrintf(DEBUG_CLIENT, "BS_Autosell_f: listnumber %i\n", num); if (num < 0 || num >= buyList.length) return; item = BS_GetObjectDefition(&buyList.l[num + buyList.scroll]); assert(item); if (ccs.eMarket.autosell[item->idx]) { ccs.eMarket.autosell[item->idx] = qfalse; Com_DPrintf(DEBUG_CLIENT, "item name: %s, autosell false\n", item->name); } else { const technology_t *tech = RS_GetTechForItem(item); /* Don't allow to enable autosell for items not researched. */ if (!RS_IsResearched_ptr(tech)) return; ccs.eMarket.autosell[item->idx] = qtrue; Com_DPrintf(DEBUG_CLIENT, "item name: %s, autosell true\n", item->name); } /* Reinit the menu. */ BS_BuyType(base); }
/** * @brief Updates the alienscont menu. */ static void AC_UpdateMenu (const base_t *base) { Cvar_Set("mn_al_alientype", ""); Cvar_Set("mn_al_alienimage", ""); Cvar_SetValue("mn_al_dead", 0); Cvar_SetValue("mn_al_alive", 0); Cvar_SetValue("mn_al_capacity", CAP_GetCurrent(base, CAP_ALIENS)); Cvar_SetValue("mn_al_capacity_max", CAP_GetMax(base, CAP_ALIENS)); /* Reset list. */ cgi->UI_ExecuteConfunc("aliencont_clear"); if (B_GetBuildingStatus(base, B_ALIEN_CONTAINMENT)) { const aliensCont_t *containment = base->alienscont; int i, j; for (i = 0, j = 0; i < ccs.numAliensTD; i++) { if (j < MAX_AC_MENU_ENTRIES) { if (containment[i].teamDef) { const technology_t *tech = containment[i].tech; if (!tech) { Com_Printf("AC_UpdateMenu: Tech entry for containment %i not set!\n", i); /* to let the click function still work */ continue; } if (!aliencontCurrent) { aliencontCurrent = &containment[i]; } if (containment[i].amountAlive > 0 || containment[i].amountDead > 0) { /* Generate a list entry. */ if (RS_IsResearched_ptr(tech)) { Cvar_Set(va("mn_ac_statusstr%i", j), _("Researched")); } else { Cvar_Set(va("mn_ac_statusstr%i", j), _("Awaiting autopsy")); if (!containment[i].amountDead) { cgi->UI_ExecuteConfunc("aliencontkill %i", j); } else { cgi->UI_ExecuteConfunc("aliencontneedautopsy %i", j); } } Cvar_SetValue(va("mn_ac_progress%i", j), (1 - tech->time / tech->overallTime) * 100); /* Display name in the correct list-entry. */ Cvar_Set(va("mn_ac_name%i", j), _(containment[i].teamDef->name)); /* Display amount of dead aliens in the correct list-entry. */ Cvar_SetValue(va("mn_ac_dead%i", j), containment[i].amountDead); /* Display number of live aliens in the correct list-entry. */ Cvar_SetValue(va("mn_ac_alive%i", j), containment[i].amountAlive); j++; } } } } numAliensOnList = j; for (; j < MAX_AC_MENU_ENTRIES; j++) { Cvar_Set(va("mn_ac_statusstr%i", j), _("Free slot")); Cvar_Set(va("mn_ac_name%i", j), _("None")); Cvar_Set(va("mn_ac_dead%i", j), ""); Cvar_Set(va("mn_ac_alive%i", j), ""); Cvar_SetValue(va("mn_ac_progress%i", j), 0); } } /** @todo Select the containment we (maybe) just clicked again */ AC_AlienClick(base, 0); }
/** * @brief Base Summary menu init function. * @note Should be called whenever the Base Summary menu gets active. */ static void BaseSummary_Init (const base_t *base) { static char textStatsBuffer[1024]; static char textInfoBuffer[256]; const aliensCont_t *containment = base->alienscont; int i; baseCapacities_t cap; const production_queue_t *queue; const technology_t *tech; int tmp; /* wipe away old buffers */ textStatsBuffer[0] = textInfoBuffer[0] = 0; Q_strcat(textInfoBuffer, _("^BAircraft\n"), sizeof(textInfoBuffer)); for (i = 0; i <= MAX_HUMAN_AIRCRAFT_TYPE; i++) { const aircraftType_t airType = (aircraftType_t)i; const int count = AIR_CountTypeInBase(base, airType); if (count == 0) continue; Q_strcat(textInfoBuffer, va("\t%s:\t\t\t\t%i\n", AIR_GetAircraftString(airType), count), sizeof(textInfoBuffer)); } Q_strcat(textInfoBuffer, "\n", sizeof(textInfoBuffer)); Q_strcat(textInfoBuffer, _("^BEmployees\n"), sizeof(textInfoBuffer)); for (i = 0; i < MAX_EMPL; i++) { const employeeType_t emplType = (employeeType_t)i; tmp = E_CountHired(base, emplType); if (tmp == 0) continue; Q_strcat(textInfoBuffer, va("\t%s:\t\t\t\t%i\n", E_GetEmployeeString(emplType, tmp), tmp), sizeof(textInfoBuffer)); } Q_strcat(textInfoBuffer, "\n", sizeof(textInfoBuffer)); Q_strcat(textInfoBuffer, _("^BAliens\n"), sizeof(textInfoBuffer)); for (i = 0; i < ccs.numAliensTD; i++) { if (!containment[i].amountAlive && !containment[i].amountDead) continue; Q_strcat(textInfoBuffer, va("\t%s:\t\t\t\t%i/%i\n", _(containment[i].teamDef->name), containment[i].amountAlive, containment[i].amountDead), sizeof(textInfoBuffer)); } /* link into the menu */ cgi->UI_RegisterText(TEXT_STANDARD, textInfoBuffer); Q_strcat(textStatsBuffer, _("^BBuildings\t\t\t\t\t\tCapacity\t\t\t\tAmount\n"), sizeof(textStatsBuffer)); for (i = 0; i < ccs.numBuildingTemplates; i++) { const building_t* b = &ccs.buildingTemplates[i]; /* only show already researched buildings */ if (!RS_IsResearched_ptr(b->tech)) continue; cap = B_GetCapacityFromBuildingType(b->buildingType); if (cap == MAX_CAP) continue; if (!B_GetNumberOfBuildingsInBaseByBuildingType(base, b->buildingType)) continue; /* Check if building is functional (see comments in B_UpdateBaseCapacities) */ if (B_GetBuildingStatus(base, b->buildingType)) { Q_strcat(textStatsBuffer, va("%s:\t\t\t\t\t\t%i/%i", _(b->name), CAP_GetCurrent(base, cap), CAP_GetMax(base, cap)), sizeof(textStatsBuffer)); } else { if (b->buildingStatus == B_STATUS_UNDER_CONSTRUCTION) { const float remaining = B_GetConstructionTimeRemain(b); const float timeLeft = std::max(0.0f, remaining); Q_strcat(textStatsBuffer, va("%s:\t\t\t\t\t\t%3.1f %s", _(b->name), timeLeft, ngettext("day", "days", timeLeft)), sizeof(textStatsBuffer)); } else { Q_strcat(textStatsBuffer, va("%s:\t\t\t\t\t\t%i/%i", _(b->name), CAP_GetCurrent(base, cap), 0), sizeof(textStatsBuffer)); } } Q_strcat(textStatsBuffer, va("\t\t\t\t%i\n", B_GetNumberOfBuildingsInBaseByBuildingType(base, b->buildingType)), sizeof(textStatsBuffer)); } Q_strcat(textStatsBuffer, "\n", sizeof(textStatsBuffer)); Q_strcat(textStatsBuffer, _("^BProduction\t\t\t\t\t\tQuantity\t\t\t\tPercent\n"), sizeof(textStatsBuffer)); queue = PR_GetProductionForBase(base); if (queue->numItems > 0) { for (i = 0; i < queue->numItems; i++) { const production_t *production = &queue->items[i]; const char *name = PR_GetName(&production->data); /** @todo use the same method as we do in PR_ProductionInfo */ Q_strcat(textStatsBuffer, va(_("%s\t\t\t\t\t\t%d\t\t\t\t%.2f%%\n"), name, production->amount, PR_GetProgress(production) * 100), sizeof(textStatsBuffer)); } } else { Q_strcat(textStatsBuffer, _("Nothing\n"), sizeof(textStatsBuffer)); } Q_strcat(textStatsBuffer, "\n", sizeof(textStatsBuffer)); Q_strcat(textStatsBuffer, _("^BResearch\t\t\t\t\t\tScientists\t\t\t\tPercent\n"), sizeof(textStatsBuffer)); tmp = 0; for (i = 0; i < ccs.numTechnologies; i++) { tech = RS_GetTechByIDX(i); if (tech->base == base && (tech->statusResearch == RS_RUNNING || tech->statusResearch == RS_PAUSED)) { Q_strcat(textStatsBuffer, va(_("%s\t\t\t\t\t\t%d\t\t\t\t%1.2f%%\n"), _(tech->name), tech->scientists, (1 - tech->time / tech->overallTime) * 100), sizeof(textStatsBuffer)); tmp++; } } if (!tmp) Q_strcat(textStatsBuffer, _("Nothing\n"), sizeof(textStatsBuffer)); /* link into the menu */ cgi->UI_RegisterText(TEXT_STATS_BASESUMMARY, textStatsBuffer); }
/** * @brief Initialises base. * @note This command is executed in the init node of the base menu. * It is called everytime the base menu pops up and sets the cvars. * @todo integrate building status tooltips from B_CheckBuildingStatusForMenu_f into this one */ static void B_BaseInit_f (void) { int i; base_t *base = B_GetCurrentSelectedBase(); if (!base) return; /* make sure the credits cvar is up-to-date */ CP_UpdateCredits(ccs.credits); cgi->Cvar_SetValue("mn_base_num_aircraft", AIR_BaseCountAircraft(base)); /* activate or deactivate the aircraft button */ if (AIR_AircraftAllowed(base)) { if (AIR_BaseHasAircraft(base)) cgi->UI_ExecuteConfunc("update_basebutton aircraft false \"%s\"", _("Aircraft management and crew equipment")); else cgi->UI_ExecuteConfunc("update_basebutton aircraft true \"%s\"", _("Buy or produce at least one aircraft first.")); } else { cgi->UI_ExecuteConfunc("update_basebutton aircraft true \"%s\"", _("No Hangar functional in base.")); } if (BS_BuySellAllowed(base)) cgi->UI_ExecuteConfunc("update_basebutton buysell false \"%s\"", _("Buy/Sell equipment, aircraft and UGV")); else cgi->UI_ExecuteConfunc("update_basebutton buysell true \"%s\"", va(_("No %s functional in base."), _("Storage"))); if (B_GetCount() > 1) cgi->UI_ExecuteConfunc("update_basebutton transfer false \"%s\"", _("Transfer equipment, vehicles, aliens and employees to other bases")); else cgi->UI_ExecuteConfunc("update_basebutton transfer true \"%s\"", _("Build at least a second base to transfer equipment or personnel")); if (RS_ResearchAllowed(base)) cgi->UI_ExecuteConfunc("update_basebutton research false \"%s\"", _("Research new technology")); else cgi->UI_ExecuteConfunc("update_basebutton research true \"%s\"", va(_("No %s functional in base."), _("Laboratory"))); if (PR_ProductionAllowed(base)) cgi->UI_ExecuteConfunc("update_basebutton production false \"%s\"", _("Produce equipment, aircraft and UGV")); else cgi->UI_ExecuteConfunc("update_basebutton production true \"%s\"", va(_("No %s functional in base."), _("Workshop"))); if (E_HireAllowed(base)) cgi->UI_ExecuteConfunc("update_basebutton hire false \"%s\"", _("Hire or dismiss employees")); else cgi->UI_ExecuteConfunc("update_basebutton hire true \"%s\"", va(_("No %s functional in base."), _("Living Quarters"))); if (AC_ContainmentAllowed(base)) cgi->UI_ExecuteConfunc("update_basebutton containment false \"%s\"", _("Manage captured aliens")); else cgi->UI_ExecuteConfunc("update_basebutton containment true \"%s\"", va(_("No %s functional in base."), _("Containment"))); if (HOS_HospitalAllowed(base)) cgi->UI_ExecuteConfunc("update_basebutton hospital false \"%s\"", _("Treat wounded soldiers and perform implant surgery")); else cgi->UI_ExecuteConfunc("update_basebutton hospital true \"%s\"", va(_("No %s functional in base."), _("Hospital"))); /* * Gather data on current/max space for living quarters, storage, lab and workshop * clear_bld_space ensures 0/0 data for facilities which may not exist in base */ cgi->UI_ExecuteConfunc("clear_bld_space"); for (i = 0; i < ccs.numBuildingTemplates; i++) { const building_t* b = &ccs.buildingTemplates[i]; const baseCapacities_t capType = B_GetCapacityFromBuildingType(b->buildingType); capacities_t cap; if (capType == MAX_CAP) continue; /* Check if building matches one of our four types */ if (b->buildingType != B_QUARTERS && b->buildingType != B_STORAGE && b->buildingType != B_WORKSHOP && b->buildingType != B_LAB && b->buildingType != B_ANTIMATTER) continue; /* only show already researched buildings */ if (!RS_IsResearched_ptr(b->tech)) continue; cap = *CAP_Get(base, capType); assert(b->tpl); const int count = B_GetNumberOfBuildingsInBaseByTemplate(base, b->tpl); if (count < 1) continue; cgi->UI_ExecuteConfunc("show_bld_space \"%s\" \"%s\" %i %i %i %i", _(b->name), b->id, cap.cur, cap.max, count, b->tpl->maxCount); } /* * Get the number of different employees in the base * @todo: Get the number of injured soldiers if hospital exists */ cgi->UI_ExecuteConfunc("current_employees %i %i %i %i", E_CountHired(base, EMPL_SOLDIER), E_CountHired(base, EMPL_PILOT), E_CountHired(base, EMPL_SCIENTIST), E_CountHired(base, EMPL_WORKER)); /* * List the first five aircraft in the base if they exist */ cgi->UI_ExecuteConfunc("clear_aircraft"); if (AIR_AircraftAllowed(base)) { if (AIR_BaseHasAircraft(base)) { i = 0; AIR_ForeachFromBase(aircraft, base) { if (i > 5) break; /* * UI node should use global IDX to identify aircraft but it uses order of aircraft in base (i) * See @todo in cp_aircraft_callbacks.c in AIR_AircraftSelect() */ cgi->UI_ExecuteConfunc("show_aircraft %i \"%s\" \"%s\" \"%s\" %i", i, aircraft->name, aircraft->id, AIR_AircraftStatusToName(aircraft), AIR_IsAircraftInBase(aircraft)); i++; } } } /* Get the research item closest to completion in the base if it exists */ cgi->UI_ExecuteConfunc("clear_research"); if (RS_ResearchAllowed(base)) { const technology_t *closestTech = NULL; double finished = -1; for (i = 0; i < ccs.numTechnologies; i++) { const technology_t *tech = RS_GetTechByIDX(i); if (!tech) continue; if (tech->base != base) continue; if (tech->statusResearch == RS_RUNNING) { const double percent = (1 - tech->time / tech->overallTime) * 100; if (percent > finished) { finished = percent; closestTech = tech; } } } if (closestTech != NULL) cgi->UI_ExecuteConfunc("show_research \"%s\" %i %3.0f", closestTech->name, closestTech->scientists, finished); } /* Get the production item closest to completion in the base if it exists */ cgi->UI_ExecuteConfunc("clear_production"); if (PR_ProductionAllowed(base)) { const production_queue_t *queue = PR_GetProductionForBase(base); if (queue->numItems > 0) { const production_t *production = &queue->items[0]; cgi->UI_ExecuteConfunc("show_production \"%s\" %3.0f", PR_GetName(&production->data), PR_GetProgress(production) * 100); } } }
/** * @todo It is a generic function, we can move it into cp_mapfightequip.c * @param[in] slot Pointer to an aircraft slot (can be base/installation too) * @param[in] tech Pointer to the technology to test * @return The status of the technology versus the slot */ static int AIM_CheckTechnologyIntoSlot (const aircraftSlot_t *slot, const technology_t *tech) { const objDef_t *item; if (!tech) return AIM_LOADING_NOTECHNOLOGYSELECTED; if (!slot) return AIM_LOADING_NOSLOTSELECTED; if (!RS_IsResearched_ptr(tech)) return AIM_LOADING_TECHNOLOGYNOTRESEARCHED; item = INVSH_GetItemByID(tech->provides); if (!item) return AIM_LOADING_NOTECHNOLOGYSELECTED; if (item->craftitem.type >= AC_ITEM_AMMO) { const objDef_t *weapon = slot->item; int k; if (slot->nextItem != NULL) weapon = slot->nextItem; if (weapon == NULL) return AIM_LOADING_NOWEAPON; /* Is the ammo is usable with the slot */ for (k = 0; k < weapon->numAmmos; k++) { const objDef_t *usable = weapon->ammos[k]; if (usable && item->idx == usable->idx) break; } if (k >= weapon->numAmmos) return AIM_LOADING_NOTUSABLEWITHWEAPON; #if 0 /** @todo This only works for ammo that is useable in exactly one weapon * check the weap_idx array and not only the first value */ if (!slot->nextItem && item->weapons[0] != slot->item) return AIM_LOADING_UNKNOWNPROBLEM; /* are we trying to change ammos for nextItem? */ if (slot->nextItem && item->weapons[0] != slot->nextItem) return AIM_LOADING_UNKNOWNPROBLEM; #endif } /* you can install an item only if its weight is small enough for the slot */ if (AII_GetItemWeightBySize(item) > slot->size) return AIM_LOADING_TOOHEAVY; /* you can't install an item that you don't possess * virtual ammo don't need to be possessed * installations always have weapon and ammo */ if (slot->aircraft) { if (!B_BaseHasItem(slot->aircraft->homebase, item)) return AIM_LOADING_UNKNOWNPROBLEM; } else if (slot->base) { if (!B_BaseHasItem(slot->base, item)) return AIM_LOADING_UNKNOWNPROBLEM; } /* you can't install an item that does not have an installation time (alien item) * except for ammo which does not have installation time */ if (item->craftitem.installationTime == -1 && slot->type < AC_ITEM_AMMO) return AIM_LOADING_ALIENTECH; return AIM_LOADING_OK; }
/** * @brief Fill market item list */ static void BS_FillMarket_f (void) { const base_t* base = B_GetCurrentSelectedBase(); itemFilterTypes_t type; if (cgi->Cmd_Argc() < 2) { cgi->Com_Printf("Usage: %s <category>\n", cgi->Cmd_Argv(0)); return; } if (cgi->Cmd_Argc() >= 3) base = B_GetFoundedBaseByIDX(atoi(cgi->Cmd_Argv(2))); if (!base) { cgi->Com_Printf("No/invalid base selected.\n"); return; } type = cgi->INV_GetFilterTypeID(cgi->Cmd_Argv(1)); cgi->UI_ExecuteConfunc("ui_market_clear"); switch (type) { case FILTER_UGVITEM: /* show own UGV */ E_Foreach(EMPL_ROBOT, robot) { const ugv_t* ugv = robot->getUGV(); const technology_t* tech = RS_GetTechByProvided(ugv->id); if (!robot->isHiredInBase(base)) continue; cgi->UI_ExecuteConfunc("ui_market_add \"ugv-%d\" \"%s\" 1 0 0 %d - \"%s\"", robot->chr.ucn, _(tech->name), ugv->price, robot->isAwayFromBase() ? _("UGV is away from home") : "-"); } /* show buyable UGV */ for (int i = 0; i < cgi->csi->numUGV; i++) { const ugv_t* ugv = &cgi->csi->ugvs[i]; const technology_t* tech = RS_GetTechByProvided(ugv->id); const objDef_t* ugvWeapon = INVSH_GetItemByID(ugv->weapon); const int buyable = std::min(E_CountUnhiredRobotsByType(ugv), BS_GetItemOnMarket(ugvWeapon)); assert(tech); if (!RS_IsResearched_ptr(tech)) continue; if (buyable <= 0) continue; cgi->UI_ExecuteConfunc("ui_market_add %s \"%s\" 0 %d %d %d - -", ugv->id, _(tech->name), buyable, ugv->price, ugv->price); } /* show (UGV) items, fall through */ case FILTER_S_PRIMARY: case FILTER_S_SECONDARY: case FILTER_S_HEAVY: case FILTER_S_IMPLANT: case FILTER_S_MISC: case FILTER_S_ARMOUR: case FILTER_DUMMY: case FILTER_CRAFTITEM: case MAX_FILTERTYPES: { for (int i = 0; i < cgi->csi->numODs; i++) { const objDef_t* od = &cgi->csi->ods[i]; const technology_t* tech = RS_GetTechForItem(od); if (!BS_IsOnMarket(od)) continue; if (B_ItemInBase(od, base) + BS_GetItemOnMarket(od) <= 0) continue; if (type != MAX_FILTERTYPES && !cgi->INV_ItemMatchesFilter(od, type)) continue; cgi->UI_ExecuteConfunc("ui_market_add %s \"%s\" %d %d %d %d %s -", od->id, _(od->name), B_ItemInBase(od, base), BS_GetItemOnMarket(od), BS_GetItemBuyingPrice(od), BS_GetItemSellingPrice(od), RS_IsResearched_ptr(tech) ? va("%d", ccs.eMarket.autosell[i]) : "-"); } break; } case FILTER_AIRCRAFT: { AIR_ForeachFromBase(aircraft, base) { cgi->UI_ExecuteConfunc("ui_market_add \"aircraft_%d\" \"%s\" 1 0 0 %d - \"%s\"", aircraft->idx, aircraft->name, BS_GetAircraftSellingPrice(aircraft), AIR_IsAircraftInBase(aircraft) ? "-" : _("Aircraft is away from home")); } for (int i = 0; i < ccs.numAircraftTemplates; i++) { const aircraft_t* aircraft = &ccs.aircraftTemplates[i]; if (!BS_AircraftIsOnMarket(aircraft)) continue; if (!RS_IsResearched_ptr(aircraft->tech)) continue; if (BS_GetAircraftOnMarket(aircraft) <= 0) continue; cgi->UI_ExecuteConfunc("ui_market_add \"%s\" \"%s\" 0 %d %d %d - -", aircraft->id, _(aircraft->tech->name), BS_GetAircraftOnMarket(aircraft), BS_GetAircraftBuyingPrice(aircraft), BS_GetAircraftSellingPrice(aircraft)); } break; } default: break; }
/** * @brief Initialises base. * @note This command is executed in the init node of the base menu. * It is called everytime the base menu pops up and sets the cvars. * @todo integrate building status tooltips from B_CheckBuildingStatusForMenu_f into this one */ static void B_BaseInit_f (void) { base_t* base = B_GetCurrentSelectedBase(); if (!base) return; /* make sure the credits cvar is up-to-date */ CP_UpdateCredits(ccs.credits); /* activate or deactivate the aircraft button */ if (AIR_AircraftAllowed(base)) { if (AIR_BaseHasAircraft(base)) cgi->UI_ExecuteConfunc("update_basebutton aircraft false \"%s\"", _("Aircraft management and crew equipment")); else cgi->UI_ExecuteConfunc("update_basebutton aircraft true \"%s\"", _("Buy or produce at least one aircraft first.")); } else { cgi->UI_ExecuteConfunc("update_basebutton aircraft true \"%s\"", _("No Hangar functional in base.")); } if (BS_BuySellAllowed(base)) cgi->UI_ExecuteConfunc("update_basebutton buysell false \"%s\"", _("Buy/Sell equipment, aircraft and UGV")); else cgi->UI_ExecuteConfunc("update_basebutton buysell true \"%s\"", va(_("No %s functional in base."), _("Storage"))); if (B_GetCount() > 1) cgi->UI_ExecuteConfunc("update_basebutton transfer false \"%s\"", _("Transfer equipment, vehicles, aliens and employees to other bases")); else cgi->UI_ExecuteConfunc("update_basebutton transfer true \"%s\"", _("Build at least a second base to transfer equipment or personnel")); if (RS_ResearchAllowed(base)) cgi->UI_ExecuteConfunc("update_basebutton research false \"%s\"", _("Research new technology")); else cgi->UI_ExecuteConfunc("update_basebutton research true \"%s\"", va(_("No %s functional in base."), _("Laboratory"))); if (PR_ProductionAllowed(base)) cgi->UI_ExecuteConfunc("update_basebutton production false \"%s\"", _("Produce equipment, aircraft and UGV")); else cgi->UI_ExecuteConfunc("update_basebutton production true \"%s\"", va(_("No %s functional in base."), _("Workshop"))); if (E_HireAllowed(base)) cgi->UI_ExecuteConfunc("update_basebutton hire false \"%s\"", _("Hire or dismiss employees")); else cgi->UI_ExecuteConfunc("update_basebutton hire true \"%s\"", va(_("No %s functional in base."), _("Living Quarters"))); if (AC_ContainmentAllowed(base)) cgi->UI_ExecuteConfunc("update_basebutton containment false \"%s\"", _("Manage captured aliens")); else cgi->UI_ExecuteConfunc("update_basebutton containment true \"%s\"", va(_("No %s functional in base."), _("Containment"))); if (HOS_HospitalAllowed(base)) cgi->UI_ExecuteConfunc("update_basebutton hospital false \"%s\"", _("Treat wounded soldiers and perform implant surgery")); else cgi->UI_ExecuteConfunc("update_basebutton hospital true \"%s\"", va(_("No %s functional in base."), _("Hospital"))); /* * Gather data on current/max space for living quarters, storage, lab and workshop * clear_bld_space ensures 0/0 data for facilities which may not exist in base */ cgi->UI_ExecuteConfunc("clear_bld_space"); for (int i = 0; i < ccs.numBuildingTemplates; i++) { const building_t* b = &ccs.buildingTemplates[i]; /* Check if building matches one of our four types */ if (b->buildingType != B_QUARTERS && b->buildingType != B_STORAGE && b->buildingType != B_WORKSHOP && b->buildingType != B_LAB && b->buildingType != B_ANTIMATTER) continue; /* only show already researched buildings */ if (!RS_IsResearched_ptr(b->tech)) continue; const baseCapacities_t capType = B_GetCapacityFromBuildingType(b->buildingType); if (capType == MAX_CAP) continue; const int count = B_GetNumberOfBuildingsInBaseByTemplate(base, b->tpl); if (count < 1) continue; const capacities_t& cap = *CAP_Get(base, capType); cgi->UI_ExecuteConfunc("show_bld_space \"%s\" \"%s\" %i %i %i %i", _(b->name), b->id, cap.cur, cap.max, count, b->tpl->maxCount); } /* * Get the number of different employees in the base * @todo: Get the number of injured soldiers if hospital exists */ cgi->UI_ExecuteConfunc("current_employees %i %i %i %i", E_CountHired(base, EMPL_SOLDIER), E_CountHired(base, EMPL_PILOT), E_CountHired(base, EMPL_SCIENTIST), E_CountHired(base, EMPL_WORKER)); /* * List the first five aircraft in the base if they exist */ cgi->UI_ExecuteConfunc("clear_aircraft"); if (AIR_AircraftAllowed(base)) { AIR_ForeachFromBase(aircraft, base) { cgi->UI_ExecuteConfunc("show_aircraft %i \"%s\" \"%s\" \"%s\" %i", aircraft->idx, aircraft->name, aircraft->id, AIR_AircraftStatusToName(aircraft), AIR_IsAircraftInBase(aircraft)); } }
/** * @brief Display only the TEXT_UFOPEDIA part for a given technology * @param[in] tech The technology_t pointer to print the UFOpaedia article for * @param[in] mail The mail parameters in case we produce a mail * @sa UP_Article */ static void UP_Article (technology_t* tech, eventMail_t* mail) { UP_ChangeDisplay(UFOPEDIA_ARTICLE); if (tech) { if (tech->mdl) cgi->Cvar_Set("mn_upmodel_top", "%s", tech->mdl); else cgi->Cvar_Set("mn_upmodel_top", ""); if (tech->image) cgi->Cvar_Set("mn_upimage_top", "%s", tech->image); else cgi->Cvar_Set("mn_upimage_top", ""); cgi->Cvar_Set("mn_upmodel_bottom", ""); if (tech->type == RS_WEAPON) UP_DrawAssociatedAmmo(tech); cgi->Cvar_Set("mn_uprequirement", ""); cgi->Cvar_Set("mn_upmetadata", ""); } cgi->UI_ResetData(TEXT_UFOPEDIA); cgi->UI_ResetData(TEXT_UFOPEDIA_REQUIREMENT); if (mail) { /* event mail */ cgi->Cvar_SetValue("mn_uppreavailable", 0); cgi->Cvar_SetValue("mn_updisplay", UFOPEDIA_CHAPTERS); UP_SetMailHeader(nullptr, TECHMAIL_PRE, mail); cgi->UI_RegisterText(TEXT_UFOPEDIA, _(mail->body)); /* This allows us to use the index button in the UFOpaedia, * eventMails don't have any chapter to go back to. */ upDisplay = UFOPEDIA_INDEX; } else if (tech) { currentChapter = tech->upChapter; upCurrentTech = tech; /* Reset itemdescription */ cgi->UI_ExecuteConfunc("itemdesc_view 0 0;"); if (RS_IsResearched_ptr(tech)) { cgi->Cvar_Set("mn_uptitle", _("UFOpaedia: %s (complete)"), _(tech->name)); /* If researched -> display research text */ cgi->UI_RegisterText(TEXT_UFOPEDIA, _(RS_GetDescription(&tech->description))); if (tech->preDescription.numDescriptions > 0) { /* Display pre-research text and the buttons if a pre-research text is available. */ if (mn_uppretext->integer) { cgi->UI_RegisterText(TEXT_UFOPEDIA, _(RS_GetDescription(&tech->preDescription))); UP_SetMailHeader(tech, TECHMAIL_PRE, nullptr); } else { UP_SetMailHeader(tech, TECHMAIL_RESEARCHED, nullptr); } cgi->Cvar_SetValue("mn_uppreavailable", 1); } else { /* Do not display the pre-research-text button if none is available (no need to even bother clicking there). */ cgi->Cvar_SetValue("mn_uppreavailable", 0); cgi->Cvar_SetValue("mn_updisplay", UFOPEDIA_CHAPTERS); UP_SetMailHeader(tech, TECHMAIL_RESEARCHED, nullptr); } switch (tech->type) { case RS_ARMOUR: case RS_WEAPON: for (int i = 0; i < cgi->csi->numODs; i++) { const objDef_t* od = INVSH_GetItemByIDX(i); if (Q_streq(tech->provides, od->id)) { cgi->INV_ItemDescription(od); UP_DisplayTechTree(tech); cgi->Cvar_Set("mn_upmetadata", "1"); break; } } break; case RS_TECH: UP_DisplayTechTree(tech); break; case RS_CRAFT: UP_AircraftDescription(tech); break; case RS_CRAFTITEM: UP_AircraftItemDescription(INVSH_GetItemByID(tech->provides)); break; case RS_BUILDING: UP_BuildingDescription(tech); break; case RS_UGV: UP_UGVDescription(cgi->Com_GetUGVByIDSilent(tech->provides)); break; default: break; } /* see also UP_TechGetsDisplayed */ } else if (RS_Collected_(tech) || (tech->statusResearchable && tech->preDescription.numDescriptions > 0)) { /* This tech has something collected or has a research proposal. (i.e. pre-research text) */ cgi->Cvar_Set("mn_uptitle", _("UFOpaedia: %s"), _(tech->name)); /* Not researched but some items collected -> display pre-research text if available. */ if (tech->preDescription.numDescriptions > 0) { cgi->UI_RegisterText(TEXT_UFOPEDIA, _(RS_GetDescription(&tech->preDescription))); UP_SetMailHeader(tech, TECHMAIL_PRE, nullptr); } else { cgi->UI_RegisterText(TEXT_UFOPEDIA, _("No pre-research description available.")); } } else { cgi->Cvar_Set("mn_uptitle", _("UFOpaedia: %s"), _(tech->name)); cgi->UI_ResetData(TEXT_UFOPEDIA); } } else { cgi->Com_Error(ERR_DROP, "UP_Article: No mail or tech given"); } }
/** * @brief Fills the battery list, descriptions, and weapons in slots * of the basedefence equip menu */ static void BDEF_BaseDefenceMenuUpdate_f (void) { char type[MAX_VAR]; base_t* base = B_GetCurrentSelectedBase(); installation_t* installation = INS_GetCurrentSelectedInstallation(); aircraftItemType_t bdefType; linkedList_t* slotList = nullptr; const bool missileResearched = RS_IsResearched_ptr(RS_GetTechByID("rs_building_missile")); const bool laserResearched = RS_IsResearched_ptr(RS_GetTechByID("rs_building_laser")); if (cgi->Cmd_Argc() != 2) type[0] = '\0'; else Q_strncpyz(type, cgi->Cmd_Argv(1), sizeof(type)); /* don't let old links appear on this menu */ cgi->UI_ResetData(TEXT_BASEDEFENCE_LIST); cgi->UI_ResetData(TEXT_LIST); cgi->UI_ResetData(TEXT_ITEMDESCRIPTION); /* base or installation should not be nullptr because we are in the menu of this base or installation */ if (!base && !installation) return; /* base and installation should not both be set. This function requires one or the other set. */ if (base && installation) { Sys_Error("BDEF_BaseDefenceMenuUpdate_f: Both the base and installation are set"); return; } cgi->Cvar_Set("mn_target", _("None")); cgi->UI_ExecuteConfunc("setautofire disable"); if (installation) { /* Every slot aims the same target */ if (installation->numBatteries) { cgi->UI_ExecuteConfunc("setautofire %i", installation->batteries[0].autofire); if (installation->batteries[0].target) cgi->Cvar_Set("mn_target", "%s", UFO_GetName(installation->batteries[0].target)); } } else if (base) { bool autofire = false; /* Every slot aims the same target */ if (base->numBatteries) { autofire |= base->batteries[0].autofire; if (base->batteries[0].target) cgi->Cvar_Set("mn_target", "%s", UFO_GetName(base->batteries[0].target)); } if (base->numLasers) { autofire |= base->lasers[0].autofire; if (base->lasers[0].target && !base->batteries[0].target) cgi->Cvar_Set("mn_target", "%s", UFO_GetName(base->lasers[0].target)); } if (base->numBatteries || base->numLasers) cgi->UI_ExecuteConfunc("setautofire %i", autofire); } /* Check if we can change to laser or missile */ if (base) { cgi->UI_ExecuteConfunc("set_defencetypes %s %s", (!missileResearched) ? "na" : (base && base->numBatteries > 0) ? "enable" : "disable", (!laserResearched) ? "na" : (base && base->numLasers > 0) ? "enable" : "disable"); } else if (installation) { cgi->UI_ExecuteConfunc("set_defencetypes %s %s", (!missileResearched) ? "na" : (installation && installation->installationStatus == INSTALLATION_WORKING && installation->numBatteries > 0) ? "enable" : "disable", "na"); } if (Q_streq(type, "missile")) bdefType = AC_ITEM_BASE_MISSILE; else if (Q_streq(type, "laser")) bdefType = AC_ITEM_BASE_LASER; else /* info page */ return; /* Check that the base or installation has at least 1 battery */ if (base) { if (base->numBatteries + base->numLasers < 1) { Com_Printf("BDEF_BaseDefenceMenuUpdate_f: there is no defence battery in this base: you shouldn't be in this function.\n"); return; } } else if (installation) { if (installation->installationStatus != INSTALLATION_WORKING) { Com_Printf("BDEF_BaseDefenceMenuUpdate_f: installation isn't working: you shouldn't be in this function.\n"); return; } else if (installation->installationTemplate->maxBatteries < 1) { Com_Printf("BDEF_BaseDefenceMenuUpdate_f: there is no defence battery in this installation: you shouldn't be in this function.\n"); return; } } if (installation) { /* we are in the installation defence menu */ if (installation->installationTemplate->maxBatteries == 0) { cgi->LIST_AddString(&slotList, _("No defence of this type in this installation")); } else { BDEF_FillSlotList(installation->batteries, installation->installationTemplate->maxBatteries, &slotList); } } else if (bdefType == AC_ITEM_BASE_MISSILE) { /* we are in the base defence menu for missile */ if (base->numBatteries == 0) { cgi->LIST_AddString(&slotList, _("No defence of this type in this base")); } else { BDEF_FillSlotList(base->batteries, base->numActiveBatteries, &slotList); } } else if (bdefType == AC_ITEM_BASE_LASER) { /* we are in the base defence menu for laser */ if (base->numLasers == 0) { cgi->LIST_AddString(&slotList, _("No defence of this type in this base")); } else { BDEF_FillSlotList(base->lasers, base->numActiveLasers, &slotList); } } else { Com_Printf("BDEF_BaseDefenceMenuUpdate_f: unknown bdefType.\n"); return; } cgi->UI_RegisterLinkedListText(TEXT_BASEDEFENCE_LIST, slotList); }
/** * @brief Updates the Buy/Sell menu list. * @param[in] base Pointer to the base to buy/sell at * @sa BS_BuyType_f */ static void BS_BuyType (const base_t *base) { const objDef_t *od; int i, j = 0; char tmpbuf[MAX_VAR]; if (!base || buyCat >= MAX_FILTERTYPES || buyCat < 0) return; CP_UpdateCredits(ccs.credits); bsMarketNames = NULL; bsMarketStorage = NULL; bsMarketMarket = NULL; bsMarketPrices = NULL; UI_ResetData(TEXT_ITEMDESCRIPTION); /* hide autosell checkboxes by default */ for (i = 0; i < MAX_MARKET_MENU_ENTRIES; i++) { UI_ExecuteConfunc("buy_autoselli %i", i); } switch (buyCat) { case FILTER_AIRCRAFT: /* Aircraft */ { const aircraft_t *aircraftTemplate; for (i = 0, j = 0, aircraftTemplate = ccs.aircraftTemplates; i < ccs.numAircraftTemplates; i++, aircraftTemplate++) { if (!BS_AircraftIsOnMarket(aircraftTemplate)) continue; assert(aircraftTemplate->tech); if (BS_GetStorageAmountInBase(base, aircraftTemplate->id) + BS_GetAircraftOnMarket(aircraftTemplate) > 0) { if (j >= buyList.scroll && j < MAX_MARKET_MENU_ENTRIES) { UI_ExecuteConfunc("buy_show %i", j - buyList.scroll); } BS_AddToList(aircraftTemplate->name, BS_GetStorageAmountInBase(base, aircraftTemplate->id), BS_GetAircraftOnMarket(aircraftTemplate), BS_GetAircraftBuyingPrice(aircraftTemplate)); if (j >= MAX_BUYLIST) Com_Error(ERR_DROP, "Increase the MAX_BUYLIST value to handle that much items\n"); buyList.l[j].item = NULL; buyList.l[j].aircraft = aircraftTemplate; buyList.length = j + 1; BS_UpdateItem(base, j - buyList.scroll); j++; } } } break; case FILTER_CRAFTITEM: /* Aircraft items */ /* get item list */ for (i = 0, j = 0, od = csi.ods; i < csi.numODs; i++, od++) { if (!BS_IsOnMarket(od)) continue; /* Check whether the item matches the proper filter, storage in current base and market. */ if ((B_ItemInBase(od, base) || ccs.eMarket.numItems[i]) && INV_ItemMatchesFilter(od, FILTER_CRAFTITEM)) { if (j >= buyList.scroll && j < MAX_MARKET_MENU_ENTRIES) { const technology_t *tech = RS_GetTechForItem(od); UI_ExecuteConfunc("buy_show %i", j - buyList.scroll); if (RS_IsResearched_ptr(tech)) { if (ccs.eMarket.autosell[i]) UI_ExecuteConfunc("buy_autoselle %i", j - buyList.scroll); else UI_ExecuteConfunc("buy_autoselld %i", j - buyList.scroll); } } BS_AddToList(od->name, B_ItemInBase(od, base), ccs.eMarket.numItems[i], BS_GetItemBuyingPrice(od)); if (j >= MAX_BUYLIST) Com_Error(ERR_DROP, "Increase the MAX_FILTERLIST value to handle that much items\n"); buyList.l[j].item = od; buyList.l[j].aircraft = NULL; buyList.length = j + 1; BS_UpdateItem(base, j - buyList.scroll); j++; } } break; default: /* Normal items */ if (buyCat < MAX_SOLDIER_FILTERTYPES || buyCat == FILTER_DUMMY) { /* get item list */ for (i = 0, j = 0, od = csi.ods; i < csi.numODs; i++, od++) { if (!BS_IsOnMarket(od)) continue; /* Check whether the item matches the proper filter, storage in current base and market. */ if ((B_ItemInBase(od, base) || ccs.eMarket.numItems[i]) && INV_ItemMatchesFilter(od, buyCat)) { BS_AddToList(od->name, B_ItemInBase(od, base), ccs.eMarket.numItems[i], BS_GetItemBuyingPrice(od)); /* Set state of Autosell button. */ if (j >= buyList.scroll && j < MAX_MARKET_MENU_ENTRIES) { const technology_t *tech = RS_GetTechForItem(od); UI_ExecuteConfunc("buy_show %i", j - buyList.scroll); if (RS_IsResearched_ptr(tech)) { if (ccs.eMarket.autosell[i]) UI_ExecuteConfunc("buy_autoselle %i", j - buyList.scroll); else UI_ExecuteConfunc("buy_autoselld %i", j - buyList.scroll); } } if (j >= MAX_BUYLIST) Com_Error(ERR_DROP, "Increase the MAX_BUYLIST value to handle that much items\n"); buyList.l[j].item = od; buyList.l[j].aircraft = NULL; buyList.length = j + 1; BS_UpdateItem(base, j - buyList.scroll); j++; } } } break; } for (; j < MAX_MARKET_MENU_ENTRIES; j++) { /* Hide the rest of the entries. */ UI_ExecuteConfunc("buy_hide %i", j); } /* Update some menu cvars. */ /* Set up base capacities. */ Com_sprintf(tmpbuf, sizeof(tmpbuf), "%i/%i", CAP_GetCurrent(base, CAP_ITEMS), CAP_GetMax(base, CAP_ITEMS)); Cvar_Set("mn_bs_storage", tmpbuf); /* select first item */ if (buyList.length) { switch (buyCat) { /** @sa BS_MarketClick_f */ case FILTER_AIRCRAFT: BS_MarketAircraftDescription(buyList.l[0].aircraft); break; case FILTER_CRAFTITEM: Cvar_Set("mn_aircraftname", ""); /** @todo Use craftitem name here? See also BS_MarketClick_f */ /* Select current item or first one. */ if (currentSelectedMenuEntry) UP_AircraftItemDescription(currentSelectedMenuEntry); else UP_AircraftItemDescription(buyList.l[0].item); break; default: assert(buyCat != MAX_FILTERTYPES); /* Select current item or first one. */ if (currentSelectedMenuEntry) INV_ItemDescription(currentSelectedMenuEntry); else INV_ItemDescription(buyList.l[0].item); break; } } else { /* reset description */ INV_ItemDescription(NULL); } UI_RegisterLinkedListText(TEXT_MARKET_NAMES, bsMarketNames); UI_RegisterLinkedListText(TEXT_MARKET_STORAGE, bsMarketStorage); UI_RegisterLinkedListText(TEXT_MARKET_MARKET, bsMarketMarket); UI_RegisterLinkedListText(TEXT_MARKET_PRICES, bsMarketPrices); }
/** * @brief Prints the (UFOpaedia and other) description for aircraft items * @param item The object definition of the item * @sa UP_Article * Not only called from UFOpaedia but also from other places to display * @todo Don't display things like speed for base defence items - a missile * facility isn't getting slower or faster due a special weapon or ammunition */ void UP_AircraftItemDescription (const objDef_t* item) { static char itemText[1024]; const technology_t* tech; /* Set menu text node content to null. */ cgi->INV_ItemDescription(nullptr); *itemText = '\0'; /* no valid item id given */ if (!item) { cgi->Cvar_Set("mn_item", ""); cgi->Cvar_Set("mn_itemname", ""); cgi->Cvar_Set("mn_upmodel_top", ""); cgi->UI_ResetData(TEXT_ITEMDESCRIPTION); return; } tech = RS_GetTechForItem(item); /* select item */ cgi->Cvar_Set("mn_item", "%s", item->id); cgi->Cvar_Set("mn_itemname", "%s", _(item->name)); if (tech->mdl) cgi->Cvar_Set("mn_upmodel_top", "%s", tech->mdl); else cgi->Cvar_Set("mn_upmodel_top", ""); /* set description text */ if (RS_IsResearched_ptr(tech)) { const objDef_t* ammo = nullptr; switch (item->craftitem.type) { case AC_ITEM_WEAPON: Q_strcat(itemText, sizeof(itemText), _("Weight:\t%s\n"), AII_WeightToName(AII_GetItemWeightBySize(item))); break; case AC_ITEM_BASE_MISSILE: case AC_ITEM_BASE_LASER: Q_strcat(itemText, sizeof(itemText), _("Weapon for base defence system\n")); break; case AC_ITEM_AMMO: ammo = item; break; default: break; } /* check ammo of weapons */ if (item->craftitem.type <= AC_ITEM_WEAPON) { for(int i = 0; i < item->numAmmos; i++) if (item->ammos[i]->isVirtual) { ammo = item->ammos[i]; break; } } if (ammo) { /* We display the characteristics of this ammo */ Q_strcat(itemText, sizeof(itemText), _("Ammo:\t%i\n"), ammo->ammo); if (!EQUAL(ammo->craftitem.weaponDamage, 0)) Q_strcat(itemText, sizeof(itemText), _("Damage:\t%i\n"), (int) ammo->craftitem.weaponDamage); Q_strcat(itemText, sizeof(itemText), _("Reloading time:\t%i\n"), (int) ammo->craftitem.weaponDelay); } /* We write the range of the weapon */ if (!EQUAL(item->craftitem.stats[AIR_STATS_WRANGE], 0)) Q_strcat(itemText, sizeof(itemText), "%s:\t%i\n", UP_AircraftStatToName(AIR_STATS_WRANGE), AIR_AircraftMenuStatsValues(item->craftitem.stats[AIR_STATS_WRANGE], AIR_STATS_WRANGE)); /* we scan all stats except weapon range */ for (int i = 0; i < AIR_STATS_MAX; i++) { const char* statsName = UP_AircraftStatToName(i); if (i == AIR_STATS_WRANGE) continue; if (item->craftitem.stats[i] > 2.0f) Q_strcat(itemText, sizeof(itemText), "%s:\t+%i\n", statsName, AIR_AircraftMenuStatsValues(item->craftitem.stats[i], i)); else if (item->craftitem.stats[i] < -2.0f) Q_strcat(itemText, sizeof(itemText), "%s:\t%i\n", statsName, AIR_AircraftMenuStatsValues(item->craftitem.stats[i], i)); else if (item->craftitem.stats[i] > 1.0f) Q_strcat(itemText, sizeof(itemText), _("%s:\t+%i %%\n"), statsName, (int)(item->craftitem.stats[i] * 100) - 100); else if (!EQUAL(item->craftitem.stats[i], 0)) Q_strcat(itemText, sizeof(itemText), _("%s:\t%i %%\n"), statsName, (int)(item->craftitem.stats[i] * 100) - 100); } } else { Q_strcat(itemText, sizeof(itemText), _("Unknown - need to research this")); } cgi->Cvar_Set("mn_upmetadata", "1"); cgi->UI_RegisterText(TEXT_ITEMDESCRIPTION, itemText); }
/** * @brief make number of items change every day. * @sa CP_CampaignRun * @sa daily called * @note This function makes items number on market slowly reach the asymptotic number of items defined in equipment.ufo * If an item has just been researched, it's not available on market until RESEARCH_LIMIT_DELAY days is reached. */ void CP_CampaignRunMarket (campaign_t* campaign) { int i; const float TYPICAL_TIME = 10.f; /**< Number of days to reach the asymptotic number of items */ const int RESEARCH_LIMIT_DELAY = 30; /**< Numbers of days after end of research to wait in order to have * items added on market */ market_t* market = BS_GetMarket(); assert(campaign->marketDef); assert(campaign->asymptoticMarketDef); for (i = 0; i < cgi->csi->numODs; i++) { const objDef_t* od = INVSH_GetItemByIDX(i); const technology_t* tech = RS_GetTechForItem(od); int asymptoticNumber; if (RS_IsResearched_ptr(tech) && (campaign->marketDef->numItems[i] != 0 || ccs.date.day > tech->researchedDate.day + RESEARCH_LIMIT_DELAY)) { /* if items are researched for more than RESEARCH_LIMIT_DELAY or was on the initial market, * there number tend to the value defined in equipment.ufo. * This value is the asymptotic value if it is not 0, or initial value else */ asymptoticNumber = campaign->asymptoticMarketDef->numItems[i] ? campaign->asymptoticMarketDef->numItems[i] : campaign->marketDef->numItems[i]; } else { /* items that have just been researched don't appear on market, but they can disappear */ asymptoticNumber = 0; } /* Store the evolution of the market in currentEvolution */ market->currentEvolutionItems[i] += (asymptoticNumber - market->numItems[i]) / TYPICAL_TIME; /* Check if new items appeared or disappeared on market */ if (fabs(market->currentEvolutionItems[i]) >= 1.0f) { const int num = (int)(market->currentEvolutionItems[i]); if (num >= 0) BS_AddItemToMarket(od, num); else BS_RemoveItemFromMarket(od, -num); market->currentEvolutionItems[i] -= num; } } for (i = 0; i < AIRCRAFTTYPE_MAX; i++) { const humanAircraftType_t type = (humanAircraftType_t)i; const char* aircraftID = cgi->Com_DropShipTypeToShortName(type); const aircraft_t* aircraft = AIR_GetAircraft(aircraftID); const technology_t* tech = aircraft->tech; int asymptoticNumber; if (RS_IsResearched_ptr(tech) && (campaign->marketDef->numAircraft[i] != 0 || ccs.date.day > tech->researchedDate.day + RESEARCH_LIMIT_DELAY)) { /* if aircraft is researched for more than RESEARCH_LIMIT_DELAY or was on the initial market, * there number tend to the value defined in equipment.ufo. * This value is the asymptotic value if it is not 0, or initial value else */ asymptoticNumber = campaign->asymptoticMarketDef->numAircraft[i] ? campaign->asymptoticMarketDef->numAircraft[i] : campaign->marketDef->numAircraft[i]; } else { /* items that have just been researched don't appear on market, but they can disappear */ asymptoticNumber = 0; } /* Store the evolution of the market in currentEvolution */ market->currentEvolutionAircraft[i] += (asymptoticNumber - market->numAircraft[i]) / TYPICAL_TIME; /* Check if new items appeared or disappeared on market */ if (fabs(market->currentEvolutionAircraft[i]) >= 1.0f) { const int num = (int)(market->currentEvolutionAircraft[i]); if (num >= 0) BS_AddAircraftToMarket(aircraft, num); else BS_RemoveAircraftFromMarket(aircraft, -num); market->currentEvolutionAircraft[i] -= num; } } }
/** * @brief Puts alien cargo into Alien Containment. * @param[in] aircraft Aircraft transporting cargo to homebase. * @sa B_AircraftReturnedToHomeBase * @sa AL_FillInContainment * @note an event mail about missing breathing tech will be triggered if necessary. */ void AL_AddAliens (aircraft_t *aircraft) { base_t *toBase; const aliensTmp_t *cargo; int alienCargoTypes; int i; int j; qboolean limit = qfalse; qboolean messageAlreadySet = qfalse; technology_t *breathingTech; qboolean alienBreathing = qfalse; const objDef_t *alienBreathingObjDef; assert(aircraft); toBase = aircraft->homebase; assert(toBase); cargo = AL_GetAircraftAlienCargo(aircraft); alienCargoTypes = AL_GetAircraftAlienCargoTypes(aircraft); if (alienCargoTypes == 0) return; if (!B_GetBuildingStatus(toBase, B_ALIEN_CONTAINMENT)) { MS_AddNewMessage(_("Notice"), _("You cannot process aliens yet. Alien Containment not ready in this base."), qfalse, MSG_STANDARD, NULL); return; } breathingTech = RS_GetTechByID(BREATHINGAPPARATUS_TECH); if (!breathingTech) Com_Error(ERR_DROP, "AL_AddAliens: Could not get breathing apparatus tech definition"); alienBreathing = RS_IsResearched_ptr(breathingTech); alienBreathingObjDef = INVSH_GetItemByID(breathingTech->provides); if (!alienBreathingObjDef) Com_Error(ERR_DROP, "AL_AddAliens: Could not get breathing apparatus item definition"); for (i = 0; i < alienCargoTypes; i++) { for (j = 0; j < ccs.numAliensTD; j++) { assert(toBase->alienscont[j].teamDef); assert(cargo[i].teamDef); if (toBase->alienscont[j].teamDef == cargo[i].teamDef) { toBase->alienscont[j].amountDead += cargo[i].amountDead; /* Add breathing apparatuses to aircraft cargo so that they are processed with other collected items */ AII_CollectItem(aircraft, alienBreathingObjDef, cargo[i].amountDead); if (cargo[i].amountAlive <= 0) continue; if (!alienBreathing && !CHRSH_IsTeamDefRobot(cargo[i].teamDef)) { /* We can not store living (i.e. no robots or dead bodies) aliens without rs_alien_breathing tech */ toBase->alienscont[j].amountDead += cargo[i].amountAlive; /* Add breathing apparatuses as well */ AII_CollectItem(aircraft, alienBreathingObjDef, cargo[i].amountAlive); /* only once */ if (!messageAlreadySet) { MS_AddNewMessage(_("Notice"), _("You can't hold live aliens yet. Aliens died."), qfalse, MSG_DEATH, NULL); messageAlreadySet = qtrue; } if (!ccs.breathingMailSent) { Cmd_ExecuteString("addeventmail alienbreathing"); ccs.breathingMailSent = qtrue; } } else { int k; for (k = 0; k < cargo[i].amountAlive; k++) { /* Check base capacity. */ if (AL_CheckAliveFreeSpace(toBase, NULL, 1)) { AL_ChangeAliveAlienNumber(toBase, &(toBase->alienscont[j]), 1); } else { /* Every exceeding alien is killed * Display a message only when first one is killed */ if (!limit) { toBase->capacities[CAP_ALIENS].cur = toBase->capacities[CAP_ALIENS].max; MS_AddNewMessage(_("Notice"), _("You don't have enough space in Alien Containment. Some aliens got killed."), qfalse, MSG_STANDARD, NULL); limit = qtrue; } /* Just kill aliens which don't fit the limit. */ toBase->alienscont[j].amountDead++; AII_CollectItem(aircraft, alienBreathingObjDef, 1); } } /* only once */ if (!messageAlreadySet) { MS_AddNewMessage(_("Notice"), _("You've captured new aliens."), qfalse, MSG_STANDARD, NULL); messageAlreadySet = qtrue; } } break; } } } for (i = 0; i < ccs.numAliensTD; i++) { aliensCont_t *ac = &toBase->alienscont[i]; technology_t *tech = ac->tech; #ifdef DEBUG if (!tech) Sys_Error("AL_AddAliens: Failed to initialize the tech for '%s'\n", ac->teamDef->name); #endif /* we need this to let RS_Collected_ return true */ if (ac->amountAlive + ac->amountDead > 0) RS_MarkCollected(tech); #ifdef DEBUG /* print all of them */ if (ac->amountAlive > 0) Com_DPrintf(DEBUG_CLIENT, "AL_AddAliens alive: %s amount: %i\n", ac->teamDef->name, ac->amountAlive); if (ac->amountDead > 0) Com_DPrintf(DEBUG_CLIENT, "AL_AddAliens bodies: %s amount: %i\n", ac->teamDef->name, ac->amountDead); #endif } /* we shouldn't have any more aliens on the aircraft after this */ AL_SetAircraftAlienCargoTypes(aircraft, 0); }