/** * @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 Sells the given UGV with all the equipment. * @param robot The employee record of the UGV to sell * @return @c true if the ugv could get sold, @c false otherwise * @todo Implement this correctly once we have UGV */ bool BS_SellUGV (Employee* robot) { const objDef_t* ugvWeapon; const ugv_t* ugv; base_t* base; if (!robot) cgi->Com_Error(ERR_DROP, "Selling nullptr UGV!"); if (!robot->getUGV()) cgi->Com_Error(ERR_DROP, "Selling invalid UGV with UCN: %i", robot->chr.ucn); ugv = robot->getUGV(); base = robot->baseHired; /* Check if we have a weapon for this ugv in the market to sell it. */ ugvWeapon = INVSH_GetItemByID(ugv->weapon); if (!ugvWeapon) cgi->Com_Error(ERR_DROP, "BS_BuyItem_f: Could not get wepaon '%s' for ugv/tank '%s'.", ugv->weapon, ugv->id); if (!robot->unhire()) { /** @todo message - Couldn't fire employee. */ Com_DPrintf(DEBUG_CLIENT, "Couldn't sell/fire robot/ugv.\n"); return false; } BS_AddItemToMarket(ugvWeapon, 1); CP_UpdateCredits(ccs.credits + ugv->price); B_AddToStorage(base, ugvWeapon, -1); return true; }
/** * @brief Buys the given UGV * @param[in] ugv The ugv template of the UGV to buy * @param[out] base Base to buy at * @return @c true if the ugv could get bought, @c false otherwise * @todo Implement this correctly once we have UGV */ bool BS_BuyUGV (const ugv_t* ugv, base_t* base) { const objDef_t* ugvWeapon; if (!ugv) cgi->Com_Error(ERR_DROP, "BS_BuyUGV: Called on nullptr UGV!"); if (!base) cgi->Com_Error(ERR_DROP, "BS_BuyUGV: Called on nullptr base!"); ugvWeapon = INVSH_GetItemByID(ugv->weapon); if (!ugvWeapon) cgi->Com_Error(ERR_DROP, "BS_BuyItem_f: Could not get weapon '%s' for ugv/tank '%s'.", ugv->weapon, ugv->id); if (ccs.credits < ugv->price) return false; if (E_CountUnhiredRobotsByType(ugv) <= 0) return false; if (BS_GetItemOnMarket(ugvWeapon) <= 0) return false; if (CAP_GetFreeCapacity(base, CAP_ITEMS) < UGV_SIZE + ugvWeapon->size) return false; if (!E_HireRobot(base, ugv)) return false; BS_RemoveItemFromMarket(ugvWeapon, 1); CP_UpdateCredits(ccs.credits - ugv->price); B_AddToStorage(base, ugvWeapon, 1); return true; }
/** * @brief Set the ammo model to display to selected ammo (only for a reloadable weapon) * @param tech technology_t pointer for the weapon's tech * @sa UP_Article */ static void UP_DrawAssociatedAmmo (const technology_t* tech) { const objDef_t* od = INVSH_GetItemByID(tech->provides); /* If this is a weapon, we display the model of the associated ammunition in the lower right */ if (od->numAmmos > 0) { const technology_t* associated = RS_GetTechForItem(od->ammos[0]); cgi->Cvar_Set("mn_upmodel_bottom", "%s", associated->mdl); } }
/** * @brief Load callback for savegames in XML Format * @param[in] parent XML Node structure, where we get the information from */ bool AIRFIGHT_LoadXML (xmlNode_t* parent) { int i; xmlNode_t* node; for (i = 0, node = cgi->XML_GetNode(parent, SAVE_AIRFIGHT_PROJECTILE); i < MAX_PROJECTILESONGEOSCAPE && node; node = cgi->XML_GetNextNode(node, parent, SAVE_AIRFIGHT_PROJECTILE), i++) { technology_t* tech = RS_GetTechByProvided(cgi->XML_GetString(node, SAVE_AIRFIGHT_ITEMID)); int j; xmlNode_t* positions; xmlNode_t* attackingAircraft; xmlNode_t* aimedAircraft; aircraftProjectile_t* projectile = &ccs.projectiles[i]; if (!tech) { Com_Printf("AIR_Load: Could not get technology of projectile %i\n", i); return false; } projectile->aircraftItem = INVSH_GetItemByID(tech->provides); for (j = 0, positions = cgi->XML_GetPos2(node, SAVE_AIRFIGHT_POS, projectile->pos[0]); j < MAX_MULTIPLE_PROJECTILES && positions; j++, positions = cgi->XML_GetNextPos2(positions, node, SAVE_AIRFIGHT_POS, projectile->pos[j])) ; projectile->numProjectiles = j; cgi->XML_GetPos3(node, SAVE_AIRFIGHT_IDLETARGET, projectile->idleTarget); projectile->time = cgi->XML_GetInt(node, SAVE_AIRFIGHT_TIME, 0); projectile->angle = cgi->XML_GetFloat(node, SAVE_AIRFIGHT_ANGLE, 0.0); projectile->bullets = cgi->XML_GetBool(node, SAVE_AIRFIGHT_BULLET, false); projectile->beam = cgi->XML_GetBool(node, SAVE_AIRFIGHT_BEAM, false); if ((attackingAircraft = cgi->XML_GetNode(node, SAVE_AIRFIGHT_ATTACKINGAIRCRAFT))) { if (cgi->XML_GetBool(attackingAircraft, SAVE_AIRFIGHT_ISUFO, false)) /** @todo 0 as default might be incorrect */ projectile->attackingAircraft = UFO_GetByIDX(cgi->XML_GetInt(attackingAircraft, SAVE_AIRFIGHT_AIRCRAFTIDX, 0)); else projectile->attackingAircraft = AIR_AircraftGetFromIDX(cgi->XML_GetInt(attackingAircraft, SAVE_AIRFIGHT_AIRCRAFTIDX, AIRCRAFT_INVALID)); } else { projectile->attackingAircraft = nullptr; } cgi->XML_GetPos3(node, SAVE_AIRFIGHT_ATTACKERPOS, projectile->attackerPos); if ((aimedAircraft = cgi->XML_GetNode(node, SAVE_AIRFIGHT_AIMEDAIRCRAFT))) { if (cgi->XML_GetBool(aimedAircraft, SAVE_AIRFIGHT_ISUFO, false)) /** @todo 0 as default might be incorrect */ projectile->aimedAircraft = UFO_GetByIDX(cgi->XML_GetInt(aimedAircraft, SAVE_AIRFIGHT_AIRCRAFTIDX, 0)); else projectile->aimedAircraft = AIR_AircraftGetFromIDX(cgi->XML_GetInt(aimedAircraft, SAVE_AIRFIGHT_AIRCRAFTIDX, AIRCRAFT_INVALID)); } else { projectile->aimedAircraft = nullptr; } } ccs.numProjectiles = i; return true; }
/** * @brief Unloads transfer cargo when finishing the transfer or destroys it when no buildings/base. * @param[in,out] destination The destination base - might be NULL in case the base * is already destroyed * @param[in] transfer Pointer to transfer in ccs.transfers. * @param[in] success True if the transfer reaches dest base, false if the base got destroyed. * @sa TR_TransferEnd */ static void TR_EmptyTransferCargo (base_t *destination, transfer_t *transfer, qboolean success) { assert(transfer); if (transfer->hasItems && success) { /* Items. */ const objDef_t *od = INVSH_GetItemByID(ANTIMATTER_TECH_ID); int i; /* antimatter */ if (transfer->itemAmount[od->idx] > 0) { if (B_GetBuildingStatus(destination, B_ANTIMATTER)) { B_ManageAntimatter(destination, transfer->itemAmount[od->idx], qtrue); } else { Com_sprintf(cp_messageBuffer, sizeof(cp_messageBuffer), _("%s does not have Antimatter Storage, antimatter are removed!"), destination->name); MSO_CheckAddNewMessage(NT_TRANSFER_LOST, _("Transport mission"), cp_messageBuffer, qfalse, MSG_TRANSFERFINISHED, NULL); } } /* items */ for (i = 0; i < csi.numODs; i++) { od = INVSH_GetItemByIDX(i); if (transfer->itemAmount[od->idx] <= 0) continue; if (!B_ItemIsStoredInBaseStorage(od)) continue; if (!B_GetBuildingStatus(destination, B_STORAGE)) { Com_sprintf(cp_messageBuffer, sizeof(cp_messageBuffer), _("%s does not have Storage, items are removed!"), destination->name); MSO_CheckAddNewMessage(NT_TRANSFER_LOST, _("Transport mission"), cp_messageBuffer, qfalse, MSG_TRANSFERFINISHED, NULL); break; } B_UpdateStorageAndCapacity(destination, od, transfer->itemAmount[od->idx], qfalse, qtrue); } } if (transfer->hasEmployees && transfer->srcBase) { /* Employees. (cannot come from a mission) */ if (!success || !B_GetBuildingStatus(destination, B_QUARTERS)) { /* Employees will be unhired. */ employeeType_t i; if (success) { Com_sprintf(cp_messageBuffer, sizeof(cp_messageBuffer), _("%s does not have Living Quarters, employees got unhired!"), destination->name); MSO_CheckAddNewMessage(NT_TRANSFER_LOST, _("Transport mission"), cp_messageBuffer, qfalse, MSG_TRANSFERFINISHED, NULL); } for (i = EMPL_SOLDIER; i < MAX_EMPL; i++) { employee_t *employee; TR_ForeachEmployee(employee, transfer, i) { employee->baseHired = transfer->srcBase; /* Restore back the original baseid. */ employee->transfer = qfalse; E_UnhireEmployee(employee); } } } else {
/** * @brief Change UFOpaedia article when clicking on the name of associated ammo or weapon */ static void UP_ResearchedLinkClick_f (void) { const objDef_t* od; if (!upCurrentTech) /* if called from console */ return; od = INVSH_GetItemByID(upCurrentTech->provides); assert(od); if (od->isAmmo()) { const technology_t* t = RS_GetTechForItem(od->weapons[0]); if (UP_TechGetsDisplayed(t)) UP_OpenWith(t->id); } else if (od->weapon && od->isReloadable()) { const technology_t* t = RS_GetTechForItem(od->ammos[0]); if (UP_TechGetsDisplayed(t)) UP_OpenWith(t->id); } }
/** * @brief Unloads transfer cargo when finishing the transfer or destroys it when no buildings/base. * @param[in,out] destination The destination base - might be nullptr in case the base * is already destroyed * @param[in] transfer Pointer to transfer in ccs.transfers. * @param[in] success True if the transfer reaches dest base, false if the base got destroyed. * @sa TR_TransferEnd */ static void TR_EmptyTransferCargo (base_t* destination, transfer_t* transfer, bool success) { assert(transfer); if (transfer->hasItems && success) { /* Items. */ const objDef_t* od = INVSH_GetItemByID(ANTIMATTER_ITEM_ID); int i; /* antimatter */ if (transfer->itemAmount[od->idx] > 0) { if (B_GetBuildingStatus(destination, B_ANTIMATTER)) { B_ManageAntimatter(destination, transfer->itemAmount[od->idx], true); } else { Com_sprintf(cp_messageBuffer, sizeof(cp_messageBuffer), _("%s does not have Antimatter Storage, antimatter are removed!"), destination->name); MSO_CheckAddNewMessage(NT_TRANSFER_LOST, _("Transport mission"), cp_messageBuffer, MSG_TRANSFERFINISHED); } } /* items */ for (i = 0; i < cgi->csi->numODs; i++) { od = INVSH_GetItemByIDX(i); if (transfer->itemAmount[od->idx] <= 0) continue; if (!B_ItemIsStoredInBaseStorage(od)) continue; B_AddToStorage(destination, od, transfer->itemAmount[od->idx]); } } if (transfer->hasEmployees && transfer->srcBase) { /* Employees. (cannot come from a mission) */ if (!success) { /* Employees will be unhired. */ for (int i = EMPL_SOLDIER; i < MAX_EMPL; i++) { const employeeType_t type = (employeeType_t)i; TR_ForeachEmployee(employee, transfer, type) { employee->baseHired = transfer->srcBase; /* Restore back the original baseid. */ employee->transfer = false; employee->unhire(); } } } else { for (int i = EMPL_SOLDIER; i < MAX_EMPL; i++) {
/** * @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 Update the list of item you can choose * @param[in] slot Pointer to aircraftSlot where items can be equiped */ static void AIM_UpdateAircraftItemList (const aircraftSlot_t *slot) { linkedList_t *amountList = NULL; technology_t **techList; technology_t **currentTech; const base_t *base = slot->aircraft->homebase; int count = 0; uiNode_t *AIM_items = NULL; /* Add all items corresponding to airequipID to list */ techList = AII_GetCraftitemTechsByType(airequipID); /* Count only those which are researched to buffer */ currentTech = techList; while (*currentTech) { if (AIM_CrafttypeFilter(base, airequipID, *currentTech)) count++; currentTech++; } /* List only those which are researched to buffer */ currentTech = techList; while (*currentTech) { if (AIM_CrafttypeFilter(base, airequipID, *currentTech)) { uiNode_t *option; const objDef_t *item = INVSH_GetItemByID((*currentTech)->provides); const int amount = B_ItemInBase(item, base); LIST_AddString(&amountList, va("%d", amount)); option = cgi->UI_AddOption(&AIM_items, (*currentTech)->name, _((*currentTech)->name), va("%d", (*currentTech)->idx)); if (!AIM_SelectableCraftItem(slot, *currentTech)) option->disabled = true; } currentTech++; } cgi->UI_RegisterOption(TEXT_LIST, AIM_items); cgi->UI_RegisterLinkedListText(TEXT_LIST2, amountList); }
/** * @brief Load callback for savegames * @param[in] parent XML Node structure, where we get the information from * @sa BS_Save * @sa SAV_GameLoad */ bool BS_LoadXML (xmlNode_t* parent) { xmlNode_t* node, *snode; market_t* market = BS_GetMarket(); node = cgi->XML_GetNode(parent, SAVE_MARKET_MARKET); if (!node) return false; for (snode = cgi->XML_GetNode(node, SAVE_MARKET_ITEM); snode; snode = cgi->XML_GetNextNode(snode, node, SAVE_MARKET_ITEM)) { const char* s = cgi->XML_GetString(snode, SAVE_MARKET_ID); const objDef_t* od = INVSH_GetItemByID(s); if (!od) { Com_Printf("BS_LoadXML: Could not find item '%s'\n", s); continue; } market->numItems[od->idx] = cgi->XML_GetInt(snode, SAVE_MARKET_NUM, 0); market->bidItems[od->idx] = cgi->XML_GetInt(snode, SAVE_MARKET_BID, 0); market->askItems[od->idx] = cgi->XML_GetInt(snode, SAVE_MARKET_ASK, 0); market->currentEvolutionItems[od->idx] = cgi->XML_GetDouble(snode, SAVE_MARKET_EVO, 0.0); market->autosell[od->idx] = cgi->XML_GetBool(snode, SAVE_MARKET_AUTOSELL, false); } for (snode = cgi->XML_GetNode(node, SAVE_MARKET_AIRCRAFT); snode; snode = cgi->XML_GetNextNode(snode, node, SAVE_MARKET_AIRCRAFT)) { const char* s = cgi->XML_GetString(snode, SAVE_MARKET_ID); const humanAircraftType_t type = cgi->Com_DropShipShortNameToID(s); market->numAircraft[type] = cgi->XML_GetInt(snode, SAVE_MARKET_NUM, 0); market->bidAircraft[type] = cgi->XML_GetInt(snode, SAVE_MARKET_BID, 0); market->askAircraft[type] = cgi->XML_GetInt(snode, SAVE_MARKET_ASK, 0); market->currentEvolutionAircraft[type] = cgi->XML_GetDouble(snode, SAVE_MARKET_EVO, 0.0); } return true; }
/** * @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 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); }
/** * @brief add item to a base defence slot (installation too) */ static void BDEF_AddItem_f (void) { aircraftSlot_t* slot; installation_t* installation = INS_GetCurrentSelectedInstallation(); base_t* base = B_GetCurrentSelectedBase(); technology_t** list; technology_t* itemTech = nullptr; aircraftItemType_t bdefType; int slotIDX; if ((!base && !installation) || (base && installation)) { Com_Printf("Exiting early base and installation both true or both false\n"); return; } if (cgi->Cmd_Argc() < 3) { Com_Printf("Usage: %s <type> <slotIDX>\n", cgi->Cmd_Argv(0)); return; } bdefType = BDEF_GetItemTypeFromID(cgi->Cmd_Argv(1)); slotIDX = atoi(cgi->Cmd_Argv(2)); if (bdefType == MAX_ACITEMS) { Com_Printf("BDEF_AddItem_f: Invalid defence type.\n"); return; } if (slotIDX < 0) { return; } else { int maxWeapon; if (base) maxWeapon = (bdefType == AC_ITEM_BASE_MISSILE) ? base->numActiveBatteries : base->numActiveLasers; else maxWeapon = installation->numBatteries; if (slotIDX >= maxWeapon) return; } slot = (installation) ? BDEF_GetInstallationSlotByIDX(installation, bdefType, slotIDX) : BDEF_GetBaseSlotByIDX(base, bdefType, slotIDX); if (!slot) { Com_Printf("BDEF_AddItem_f: Invalid slot.\n"); return; } list = AII_GetCraftitemTechsByType(bdefType); while (*list) { if (AIM_SelectableCraftItem(slot, *list)) { itemTech = *list; break; } list++; } if (!itemTech) return; if (!slot->nextItem) { /* we add the weapon, shield, item, or base defence if slot is free or the installation of * current item just began */ if (!slot->item || (slot->item && slot->installationTime == slot->item->craftitem.installationTime)) { AII_RemoveItemFromSlot(base, slot, false); AII_AddItemToSlot(base, itemTech, slot, false); /* Aircraft stats are updated below */ AII_AutoAddAmmo(slot); } else if (slot->item == INVSH_GetItemByID(itemTech->provides)) { /* the added item is the same than the one in current slot */ if (slot->installationTime == -slot->item->craftitem.installationTime) { /* player changed his mind: he just want to re-add the item he just removed */ slot->installationTime = 0; } else if (!slot->installationTime) { /* player try to add a weapon he already have: just skip */ } } else { /* We start removing current item in slot, and the selected item will be installed afterwards */ slot->installationTime = -slot->item->craftitem.installationTime; AII_AddItemToSlot(base, itemTech, slot, true); AII_AutoAddAmmo(slot); } } else { /* remove weapon and ammo of next item */ AII_RemoveItemFromSlot(base, slot, false); AII_AddItemToSlot(base, itemTech, slot, true); AII_AutoAddAmmo(slot); } /* Reinit menu */ cgi->Cmd_ExecuteString("basedef_updatemenu %s", BDEF_GetIDFromItemType(slot->type)); }
/** * @brief Load callback for xml savegames * @param[in] p XML Node structure, where we get the information from * @sa PR_SaveXML * @sa SAV_GameLoadXML */ bool PR_LoadXML (xmlNode_t* p) { xmlNode_t* node = cgi->XML_GetNode(p, SAVE_PRODUCE_PRODUCTION); for (xmlNode_t* snode = cgi->XML_GetNode(node, SAVE_PRODUCE_QUEUE); snode; snode = cgi->XML_GetNextNode(snode, node, SAVE_PRODUCE_QUEUE)) { xmlNode_t* ssnode; const int baseIDX = cgi->XML_GetInt(snode, SAVE_PRODUCE_QUEUEIDX, MAX_BASES); base_t* base = B_GetBaseByIDX(baseIDX); production_queue_t* pq; if (base == nullptr) { Com_Printf("Invalid production queue index %i\n", baseIDX); continue; } pq = PR_GetProductionForBase(base); for (ssnode = cgi->XML_GetNode(snode, SAVE_PRODUCE_ITEM); pq->numItems < MAX_PRODUCTIONS && ssnode; ssnode = cgi->XML_GetNextNode(ssnode, snode, SAVE_PRODUCE_ITEM)) { const char* s1 = cgi->XML_GetString(ssnode, SAVE_PRODUCE_ITEMID); production_t* prod = &pq->items[pq->numItems]; prod->idx = pq->numItems; prod->amount = cgi->XML_GetInt(ssnode, SAVE_PRODUCE_AMOUNT, 0); prod->frame = cgi->XML_GetInt(ssnode, SAVE_PRODUCE_PROGRESS, 0); /* amount */ if (prod->amount <= 0) { Com_Printf("PR_LoadXML: Production with amount <= 0 dropped (baseidx=%i, production idx=%i).\n", baseIDX, pq->numItems); continue; } /* item */ if (s1[0] != '\0') PR_SetData(&prod->data, PRODUCTION_TYPE_ITEM, INVSH_GetItemByID(s1)); /* UFO */ const int ufoIDX = cgi->XML_GetInt(ssnode, SAVE_PRODUCE_UFOIDX, -1); if (ufoIDX != -1) { storedUFO_t* ufo = US_GetStoredUFOByIDX(ufoIDX); if (!ufo) { Com_Printf("PR_LoadXML: Could not find ufo idx: %i\n", ufoIDX); continue; } PR_SetData(&prod->data, PRODUCTION_TYPE_DISASSEMBLY, ufo); PR_SetUFODisassembly(prod); } /* aircraft */ const char* s2 = cgi->XML_GetString(ssnode, SAVE_PRODUCE_AIRCRAFTID); if (s2[0] != '\0') PR_SetData(&prod->data, PRODUCTION_TYPE_AIRCRAFT, AIR_GetAircraft(s2)); if (!PR_IsDataValid(&prod->data)) { Com_Printf("PR_LoadXML: Production is not an item an aircraft nor a disassembly\n"); continue; } pq->numItems++; } } return true; }
/** * @brief Parses one "components" entry in a .ufo file and writes it into the next free entry in xxxxxxxx (components_t). * @param[in] name The unique id of a components_t array entry. * @param[in] text the whole following text after the "components" definition. * @sa CP_ParseScriptFirst */ static void CP_ParseComponents (const char *name, const char **text) { components_t *comp; const char *errhead = "CP_ParseComponents: unexpected end of file."; const char *token; /* get body */ token = Com_Parse(text); if (!*text || *token != '{') { Com_Printf("CP_ParseComponents: \"%s\" components def without body ignored.\n", name); return; } if (ccs.numComponents >= MAX_ASSEMBLIES) { Com_Printf("CP_ParseComponents: too many technology entries. limit is %i.\n", MAX_ASSEMBLIES); return; } /* New components-entry (next free entry in global comp-list) */ comp = &ccs.components[ccs.numComponents]; ccs.numComponents++; OBJZERO(*comp); /* set standard values */ Q_strncpyz(comp->assemblyId, name, sizeof(comp->assemblyId)); comp->assemblyItem = INVSH_GetItemByIDSilent(comp->assemblyId); if (comp->assemblyItem) Com_DPrintf(DEBUG_CLIENT, "CP_ParseComponents: linked item: %s with components: %s\n", name, comp->assemblyId); do { /* get the name type */ token = Com_EParse(text, errhead, name); if (!*text) break; if (*token == '}') break; /* get values */ if (Q_streq(token, "item")) { /* Defines what items need to be collected for this item to be researchable. */ if (comp->numItemtypes < MAX_COMP) { /* Parse item name */ token = Com_Parse(text); comp->items[comp->numItemtypes] = INVSH_GetItemByID(token); /* item id -> item pointer */ /* Parse number of items. */ token = Com_Parse(text); comp->itemAmount[comp->numItemtypes] = atoi(token); token = Com_Parse(text); /* If itemcount needs to be scaled */ if (token[0] == '%') comp->itemAmount2[comp->numItemtypes] = COMP_ITEMCOUNT_SCALED; else comp->itemAmount2[comp->numItemtypes] = atoi(token); /** @todo Set item links to NONE if needed */ /* comp->item_idx[comp->numItemtypes] = xxx */ comp->numItemtypes++; } else { Com_Printf("CP_ParseComponents: \"%s\" Too many 'items' defined. Limit is %i - ignored.\n", name, MAX_COMP); } } else if (Q_streq(token, "time")) { /* Defines how long disassembly lasts. */ token = Com_Parse(text); comp->time = atoi(token); } else { Com_Printf("CP_ParseComponents: Error in \"%s\" - unknown token: \"%s\".\n", name, token); } } while (*text); }
/** * @brief Add selected item to current zone. * @note Called from airequip menu * @sa aircraftItemType_t */ static void AIM_AircraftEquipAddItem_f (void) { int zone; aircraftSlot_t *slot; aircraft_t *aircraft = NULL; base_t *base = B_GetCurrentSelectedBase(); zone = (airequipID == AC_ITEM_AMMO) ? 2 : 1; /* proceed only if an item has been selected */ if (!aimSelectedTechnology) return; assert(base); aircraft = base->aircraftCurrent; assert(aircraft); base = aircraft->homebase; /* we need to know where items will be removed */ slot = AII_SelectAircraftSlot(aircraft, airequipID); /* the clicked button doesn't correspond to the selected zone */ if (zone != airequipSelectedZone) return; /* check if the zone exists */ if (zone >= ZONE_MAX) return; /* update the new item to slot */ switch (zone) { case ZONE_MAIN: if (!slot->nextItem) { /* we add the weapon, shield, item if slot is free or the installation of current item just began */ if (!slot->item || (slot->item && slot->installationTime == slot->item->craftitem.installationTime)) { AII_RemoveItemFromSlot(base, slot, false); AII_AddItemToSlot(base, aimSelectedTechnology, slot, false); /* Aircraft stats are updated below */ AII_AutoAddAmmo(slot); break; } else if (slot->item == INVSH_GetItemByID(aimSelectedTechnology->provides)) { /* the added item is the same than the one in current slot */ if (slot->installationTime == -slot->item->craftitem.installationTime) { /* player changed his mind: he just want to re-add the item he just removed */ slot->installationTime = 0; break; } else if (!slot->installationTime) { /* player try to add a weapon he already have: just skip */ return; } } else { /* We start removing current item in slot, and the selected item will be installed afterwards */ slot->installationTime = -slot->item->craftitem.installationTime; /* more below */ } } else { /* remove weapon and ammo of next item */ AII_RemoveNextItemFromSlot(base, slot, false); /* more below */ } /* we change the weapon, shield, item, or base defence that will be installed AFTER the removal * of the one in the slot atm */ AII_AddItemToSlot(base, aimSelectedTechnology, slot, true); AII_AutoAddAmmo(slot); break; case ZONE_AMMO: /* we can change ammo only if the selected item is an ammo (for weapon or base defence system) */ if (airequipID >= AC_ITEM_AMMO) { AII_AddAmmoToSlot(base, aimSelectedTechnology, slot); } break; default: /* Zone higher than ZONE_AMMO shouldn't exist */ return; } /* Update the values of aircraft stats (just in case an item has an installationTime of 0) */ AII_UpdateAircraftStats(aircraft); AIM_AircraftEquipMenuUpdate(); }
/** * @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 Buy/Sell item/aircraft/ugv on the market */ static void BS_Buy_f (void) { const char* itemid; int count; base_t* base = B_GetCurrentSelectedBase(); const aircraft_t* aircraft; const ugv_t* ugv; const objDef_t* od; if (cgi->Cmd_Argc() < 2) { cgi->Com_Printf("Usage: %s <item-id> <count> [base-idx] \nNegative count means selling. If base index is omitted buys on the currently selected base.\n", cgi->Cmd_Argv(0)); return; } itemid = cgi->Cmd_Argv(1); count = atoi(cgi->Cmd_Argv(2)); if (cgi->Cmd_Argc() >= 4) base = B_GetFoundedBaseByIDX(atoi(cgi->Cmd_Argv(3))); if (char const* const rest = Q_strstart(itemid, "aircraft_")) { /* aircraft sell - with aircraft golbal idx */ int idx = atoi(rest); aircraft_t* aircraft = AIR_AircraftGetFromIDX(idx); if (!aircraft) { cgi->Com_Printf("Invalid aircraft index!\n"); return; } AIR_RemoveEmployees(*aircraft); BS_SellAircraft(aircraft); return; } if (char const* const rest = Q_strstart(itemid, "ugv-")) { /* ugv sell - with unique character number index */ int ucn = atoi(rest); Employee* robot = E_GetEmployeeByTypeFromChrUCN(EMPL_ROBOT, ucn); if (!robot) { cgi->Com_Printf("Invalid UCN for UGV!\n"); return; } BS_SellUGV(robot); return; } if (!base) { cgi->Com_Printf("No/invalid base selected.\n"); return; } aircraft = AIR_GetAircraftSilent(itemid); if (aircraft) { if (!B_GetBuildingStatus(base, B_COMMAND)) { CP_Popup(_("Note"), _("No Command Centre in this base.\nHangars are not functional.\n")); return; } /* We cannot buy aircraft if there is no power in our base. */ if (!B_GetBuildingStatus(base, B_POWER)) { CP_Popup(_("Note"), _("No power supplies in this base.\nHangars are not functional.")); return; } /* We cannot buy aircraft without any hangar. */ if (!AIR_AircraftAllowed(base)) { CP_Popup(_("Note"), _("Build a hangar first.")); return; } /* Check free space in hangars. */ if (CAP_GetFreeCapacity(base, AIR_GetHangarCapacityType(aircraft)) <= 0) { CP_Popup(_("Notice"), _("You cannot buy this aircraft.\nNot enough space in hangars.\n")); return; } if (ccs.credits < BS_GetAircraftBuyingPrice(aircraft)) { CP_Popup(_("Notice"), _("You cannot buy this aircraft.\nNot enough credits.\n")); return; } BS_BuyAircraft(aircraft, base); return; } ugv = cgi->Com_GetUGVByIDSilent(itemid); if (ugv) { const objDef_t* ugvWeapon = INVSH_GetItemByID(ugv->weapon); if (!ugvWeapon) cgi->Com_Error(ERR_DROP, "BS_BuyItem_f: Could not get weapon '%s' for ugv/tank '%s'.", ugv->weapon, ugv->id); if (E_CountUnhiredRobotsByType(ugv) < 1) return; if (ccs.eMarket.numItems[ugvWeapon->idx] < 1) return; if (ccs.credits < ugv->price) { CP_Popup(_("Not enough money"), _("You cannot buy this item as you don't have enough credits.")); return; } if (CAP_GetFreeCapacity(base, CAP_ITEMS) < UGV_SIZE + ugvWeapon->size) { CP_Popup(_("Not enough storage space"), _("You cannot buy this item.\nNot enough space in storage.\nBuild more storage facilities.")); return; } BS_BuyUGV(ugv, base); return; } if (count == 0) { cgi->Com_Printf("Invalid number of items to buy/sell: %s\n", cgi->Cmd_Argv(2)); return; } /* item */ od = INVSH_GetItemByID(cgi->Cmd_Argv(1)); if (od) { if (!BS_IsOnMarket(od)) return; if (count > 0) { /* buy */ const int price = BS_GetItemBuyingPrice(od); count = std::min(count, BS_GetItemOnMarket(od)); /* no items available on market */ if (count <= 0) return; if (price <= 0) { cgi->Com_Printf("Item on market with invalid buying price: %s (%d)\n", od->id, BS_GetItemBuyingPrice(od)); return; } /** @todo warn if player can buy less item due to available credits? */ count = std::min(count, ccs.credits / price); /* not enough money for a single item */ if (count <= 0) { CP_Popup(_("Not enough money"), _("You cannot buy this item as you don't have enough credits.")); return; } if (od->size <= 0) { cgi->Com_Printf("Item on market with invalid size: %s (%d)\n", od->id, od->size); return; } count = std::min(count, CAP_GetFreeCapacity(base, CAP_ITEMS) / od->size); if (count <= 0) { CP_Popup(_("Not enough storage space"), _("You cannot buy this item.\nNot enough space in storage.\nBuild more storage facilities.")); return; } BS_BuyItem(od, base, count); } else { /* sell */ count = std::min(-1 * count, B_ItemInBase(od, base)); /* no items in storage */ if (count <= 0) return; BS_SellItem(od, base, count); } return; } cgi->Com_Printf("Invalid item ID\n"); }
/** * @brief Show information about item/aircaft/ugv in the market */ static void BS_ShowInfo_f (void) { const char* itemid; const aircraft_t* aircraft; const ugv_t* ugv; const objDef_t* od; if (cgi->Cmd_Argc() < 2) { cgi->Com_Printf("Usage: %s <item-id>\n", cgi->Cmd_Argv(0)); return; } itemid = cgi->Cmd_Argv(1); if (char const* const rest = Q_strstart(itemid, "aircraft_")) { /* PHALANX aircraft - with aircraft golbal idx */ int idx = atoi(rest); aircraft = AIR_AircraftGetFromIDX(idx); if (!aircraft) { cgi->Com_Printf("Invalid aircraft index!\n"); return; } /** @todo show specialized info about PHALANX aircraft */ BS_MarketAircraftDescription(aircraft->tpl); return; } if (char const* const rest = Q_strstart(itemid, "ugv-")) { /* PHALANX ugv - with unique character number index */ int ucn = atoi(rest); Employee* robot = E_GetEmployeeByTypeFromChrUCN(EMPL_ROBOT, ucn); if (!robot) { cgi->Com_Printf("Invalid UCN for UGV!\n"); return; } /** @todo show specialized info about PHLANX UGVs */ UP_UGVDescription(robot->getUGV()); return; } aircraft = AIR_GetAircraftSilent(itemid); if (aircraft) { BS_MarketAircraftDescription(aircraft->tpl); return; } ugv = cgi->Com_GetUGVByIDSilent(itemid); if (ugv) { UP_UGVDescription(ugv); return; } /* item */ od = INVSH_GetItemByID(cgi->Cmd_Argv(1)); if (od) { if (!BS_IsOnMarket(od)) return; if (od->craftitem.type != MAX_ACITEMS) UP_AircraftItemDescription(od); else cgi->INV_ItemDescription(od); return; } cgi->Com_Printf("Invalid item ID\n"); }
/** * @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; }