/** * @brief Remove aliens that exceed containment capacity * @note called on destroying an Alien Containment (from building_ondestroy) * @param[in, out] base Pointer to the base to check */ void AL_RemoveAliensExceedingCapacity (base_t *base) { const int max = CAP_GetMax(base, CAP_ALIENS); int current = CAP_GetCurrent(base, CAP_ALIENS); int i; assert(base); assert(max >= 0); for (i = 0; i < ccs.numAliensTD; i++) { const int remove = min(base->alienscont[i].amountAlive, current - max); if (!base->alienscont[i].teamDef) continue; /* remove dead aliens if there is no alien containment */ if (max == 0) base->alienscont[i].amountDead = 0; if (remove > 0) { base->alienscont[i].amountAlive -= remove; CAP_SetCurrent(base, CAP_ALIENS, current - remove); current = CAP_GetCurrent(base, CAP_ALIENS); } } assert(max >= current); }
/** * @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 onDestroy Callback for Antimatter Storage */ static void B_Destroy_AntimaterStorage_f (void) { base_t *base; const float prob = frand(); if (cgi->Cmd_Argc() < 4) { /** note: third parameter not used but we must be sure we have probability parameter */ Com_Printf("Usage: %s <probability> <baseID> <buildingType>\n", cgi->Cmd_Argv(0)); return; } base = B_GetFoundedBaseByIDX(atoi(cgi->Cmd_Argv(2))); if (!base) return; if (CAP_GetCurrent(base, CAP_ANTIMATTER) <= 0) return; CAP_RemoveAntimatterExceedingCapacity(base); if (base->baseStatus != BASE_WORKING) return; if (prob < atof(cgi->Cmd_Argv(1))) { MS_AddNewMessage(_("Notice"), va(_("%s has been destroyed by an antimatter storage breach."), base->name)); cgi->UI_PopWindow(false); B_Destroy(base); } }
/** * @brief Remove exceeding antimatter if an antimatter tank has been destroyed. * @param[in] base Pointer to the base. */ void CAP_RemoveAntimatterExceedingCapacity (base_t* base) { const int amount = CAP_GetCurrent(base, CAP_ANTIMATTER) - CAP_GetMax(base, CAP_ANTIMATTER); if (amount <= 0) return; B_ManageAntimatter(base, amount, false); }
/** * @brief Remove items until everything fits in storage. * @note items will be randomly selected for removal. * @param[in] base Pointer to the base */ void CAP_RemoveItemsExceedingCapacity (base_t *base) { int i; int objIdx[MAX_OBJDEFS]; /**< Will contain idx of items that can be removed */ int num, cnt; if (CAP_GetFreeCapacity(base, CAP_ITEMS) >= 0) return; for (i = 0, num = 0; i < cgi->csi->numODs; i++) { const objDef_t *obj = INVSH_GetItemByIDX(i); if (!B_ItemIsStoredInBaseStorage(obj)) continue; /* Don't count item that we don't have in base */ if (B_ItemInBase(obj, base) <= 0) continue; objIdx[num++] = i; } cnt = E_CountHired(base, EMPL_ROBOT); /* UGV takes room in storage capacity: we store them with a value MAX_OBJDEFS that can't be used by objIdx */ for (i = 0; i < cnt; i++) { objIdx[num++] = MAX_OBJDEFS; } while (num && CAP_GetFreeCapacity(base, CAP_ITEMS) < 0) { /* Select the item to remove */ const int randNumber = rand() % num; if (objIdx[randNumber] >= MAX_OBJDEFS) { /* A UGV is destroyed: get first one */ Employee* employee = E_GetHiredRobot(base, 0); /* There should be at least a UGV */ assert(employee); E_DeleteEmployee(employee); } else { /* items are destroyed. We guess that all items of a given type are stored in the same location * => destroy all items of this type */ const int idx = objIdx[randNumber]; const objDef_t *od = INVSH_GetItemByIDX(idx); B_UpdateStorageAndCapacity(base, od, -B_ItemInBase(od, base), false); } REMOVE_ELEM(objIdx, randNumber, num); /* Make sure that we don't have an infinite loop */ if (num <= 0) break; } Com_DPrintf(DEBUG_CLIENT, "B_RemoveItemsExceedingCapacity: Remains %i in storage for a maximum of %i\n", CAP_GetCurrent(base, CAP_ITEMS), CAP_GetMax(base, CAP_ITEMS)); }
/** * @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 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 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); }