Index: src/client/campaign/cp_base.h =================================================================== --- src/client/campaign/cp_base.h (revision 25739) +++ src/client/campaign/cp_base.h (working copy) @@ -305,7 +305,6 @@ void B_RemoveAircraftExceedingCapacity(base_t* base, buildingType_t buildingType); void B_DrawBuilding(base_t* base, building_t* building); void B_RemoveItemsExceedingCapacity(base_t *base); -void B_RemoveUFOsExceedingCapacity(base_t *base, const buildingType_t buildingType); void B_RemoveAntimatterExceedingCapacity(base_t *base); void B_ManageAntimatter(base_t *base, int amount, qboolean add); void B_UpdateStorageCap(base_t *base); Index: src/client/campaign/cp_campaign.h =================================================================== --- src/client/campaign/cp_campaign.h (revision 25739) +++ src/client/campaign/cp_campaign.h (working copy) @@ -4,7 +4,7 @@ */ /* -Copyright (C) 2002-2007 UFO: Alien Invasion team. +Copyright (C) 2002-2009 UFO: Alien Invasion team. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License @@ -32,6 +32,7 @@ struct installation_s; struct employee_s; struct menuNode_s; /**< @todo remove this once the menuNode_t usage is cleaned up */ +struct storedUFO_s; #define MAX_ASSEMBLIES 16 #define ANTIMATTER_SIZE 10 @@ -467,6 +468,10 @@ int numComponents; components_t components[MAX_ASSEMBLIES]; + /** stored UFOs */ + storedUFO_t storedUFOs[MAX_STOREDUFOS]; + int numStoredUFOs; + /* Alien Team Definitions. */ int numAliensTD; Index: src/client/campaign/cp_installation.c =================================================================== --- src/client/campaign/cp_installation.c (revision 25739) +++ src/client/campaign/cp_installation.c (working copy) @@ -156,7 +156,14 @@ if (!installation->founded) return; + /* Disable radar */ RADAR_UpdateInstallationRadarCoverage(installation, 0, 0); + /* Destroy stored UFOs */ + if (installation->ufoCapacity.max) { + installation->ufoCapacity.max = 0; + US_RemoveUFOsExceedingCapacity(installation); + } + CP_MissionNotifyInstallationDestroyed(installation); Com_sprintf(cp_messageBuffer, sizeof(cp_messageBuffer), _("Installation %s was destroyed."), installation->name); @@ -306,7 +313,33 @@ } #endif +/** + * @brief returns the first installation with (free) ufostoring capacity + * @param[in] free On qtrue it gives the first UFO Yard with free space + * @return installation_t Pointer to the UFO Yard + **/ +installation_t *INS_GetFirstUFOYard (qboolean free) +{ + int i; + for (i = 0; i < ccs.numInstallations; i++) { + installation_t *installation = INS_GetFoundedInstallationByIDX(i); + + if (!installation) + continue; + + if (INS_GetType(installation) != INSTALLATION_UFOYARD) + continue; + + if (free && installation->ufoCapacity.cur >= installation->ufoCapacity.max) + continue; + + return installation; + } + + return NULL; +} + /** * @brief Resets console commands. * @sa MN_ResetMenus Index: src/client/campaign/cp_installation.h =================================================================== --- src/client/campaign/cp_installation.h (revision 25739) +++ src/client/campaign/cp_installation.h (working copy) @@ -103,13 +103,15 @@ /* Functions */ installation_t *INS_GetInstallationByIDX(int instIdx); -installation_t* INS_GetFoundedInstallationByIDX(int installationIdx); +installation_t *INS_GetFoundedInstallationByIDX(int installationIdx); installation_t *INS_GetFirstUnfoundedInstallation(void); -installationTemplate_t* INS_GetInstallationTemplateFromInstallationID(const char *id); +installationTemplate_t *INS_GetInstallationTemplateFromInstallationID(const char *id); installationType_t INS_GetType(const installation_t *installation); +installation_t *INS_GetFirstUFOYard(qboolean free); + void INS_SetUpInstallation(installation_t* installation, installationTemplate_t *installationTemplate, vec2_t pos); installation_t *INS_GetCurrentSelectedInstallation(void); Index: src/client/campaign/cp_uforecovery.c =================================================================== --- src/client/campaign/cp_uforecovery.c (revision 25739) +++ src/client/campaign/cp_uforecovery.c (working copy) @@ -1,11 +1,12 @@ /** * @file cp_uforecovery.c - * @brief UFO recovery - * @note Functions with UR_* + * @brief UFO recovery and storing + * @note UFO recovery functions with UR_* + * @note UFO storing functions with US_* */ /* -Copyright (C) 2002-2007 UFO: Alien Invasion team. +Copyright (C) 2002-2009 UFO: Alien Invasion team. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License @@ -28,6 +29,7 @@ #include "../menu/m_main.h" #include "cp_campaign.h" #include "cp_ufo.h" +#include "cp_map.h" #include "cp_uforecovery.h" #include "cp_uforecovery_callbacks.h" #include "cp_aircraft.h" @@ -41,7 +43,7 @@ * @sa CL_EventAddMail_f * @sa CL_NewEventMail */ -static void UR_SendMail (const aircraft_t *ufocraft, const base_t *base) +static void UR_SendMail (const aircraft_t *ufocraft, const char *baseName) { int i; components_t *comp; @@ -49,7 +51,7 @@ char body[512] = ""; assert(ufocraft); - assert(base); + assert(baseName); if (ccs.missionResults.crashsite) { /* take the source mail and create a copy of it */ @@ -95,96 +97,20 @@ assert(mail); /* don't free the old mail body here - it's the string of the source mail */ - mail->body = Mem_PoolStrDup(va(_(mail->body), UFO_TypeToName(ccs.missionResults.ufotype), base->name, body), cp_campaignPool, 0); + mail->body = Mem_PoolStrDup(va(_(mail->body), UFO_TypeToName(ccs.missionResults.ufotype), baseName, body), cp_campaignPool, 0); /* update subject */ /* Insert name of the mission in the template */ - mail->subject = Mem_PoolStrDup(va(_(mail->subject), base->name), cp_campaignPool, 0); + mail->subject = Mem_PoolStrDup(va(_(mail->subject), baseName), cp_campaignPool, 0); /* Add the mail to unread mail */ Cmd_ExecuteString(va("addeventmail %s", mail->id)); } /** - * @brief Prepares UFO recovery in global recoveries array. - * @param[in] base Pointer to the base, where the UFO recovery will be made. - * @sa UR_ProcessActive - * @sa UR_ConditionsForStoring - */ -void UR_Prepare (base_t *base, ufoType_t ufoType) -{ - int i; - ufoRecoveries_t *recovery = NULL; - aircraft_t *ufocraft = NULL; - date_t event; - - assert(base); - - /* Find ufo sample of given ufotype. */ - for (i = 0; i < ccs.numAircraftTemplates; i++) { - ufocraft = &ccs.aircraftTemplates[i]; - if (ufocraft->type != AIRCRAFT_UFO) - continue; - if (ufocraft->ufotype == ufoType) - break; - } - if (ufocraft == NULL) { - Com_DPrintf(DEBUG_CLIENT, "UR_Prepare: no ufo with the type %i found.\n", ufoType); - return; - } - - /* Find free uforecovery slot. */ - for (i = 0; i < MAX_RECOVERIES; i++) { - if (!ccs.recoveries[i].active) { - /* Make sure it is empty here. */ - memset(&ccs.recoveries[i], 0, sizeof(ccs.recoveries[i])); - recovery = &ccs.recoveries[i]; - break; - } - } - if (!recovery) { - Com_DPrintf(DEBUG_CLIENT, "UR_Prepare: free recovery slot not found.\n"); - return; - } - - /* Prepare date of the recovery event - always two days after mission. */ - event = ccs.date; - /* if you change these 2 days to something other you - * have to review all translations, too */ - event.day += 2; - /* Prepare recovery. */ - recovery->active = qtrue; - recovery->base = base; - recovery->ufoTemplate = ufocraft->tpl; - recovery->event = event; - - /* Update base capacity. */ - if (ufocraft->size == AIRCRAFT_LARGE) { - /* Large UFOs can only fit in large UFO hangars */ - if (base->capacities[CAP_UFOHANGARS_LARGE].max > base->capacities[CAP_UFOHANGARS_LARGE].cur) - base->capacities[CAP_UFOHANGARS_LARGE].cur++; - else - /* no more room -- this shouldn't happen as we've used UR_ConditionsForStoring */ - Com_Error(ERR_DROP, "UR_Prepare: No room in large UFO hangars to store %s", ufocraft->id); - } else { - /* Small UFOs can only fit in small UFO hangars */ - if (base->capacities[CAP_UFOHANGARS_SMALL].max > base->capacities[CAP_UFOHANGARS_SMALL].cur) - base->capacities[CAP_UFOHANGARS_SMALL].cur++; - else - /* no more room -- this shouldn't happen as we've used UR_ConditionsForStoring */ - Com_Error(ERR_DROP, "UR_Prepare: No room in small UFO hangars to store %s", ufocraft->id); - } - - Com_DPrintf(DEBUG_CLIENT, "UR_Prepare: the recovery entry in global array is done; base: %s, ufotype: %s, date: %i\n", - base->name, recovery->ufoTemplate->id, recovery->event.day); - - /* Send an email */ - UR_SendMail(ufocraft, base); -} - -/** * @brief Function to process crashed UFO. * @note Command to call this: cp_ufocrashed. + * @todo is this still used? Remove or fix. */ static void CP_UFOCrashed_f (void) { @@ -237,7 +163,7 @@ cargo = aircraft->itemcargo; /* Find components definition. */ - comp = CL_GetComponentsByItem(INVSH_GetItemByID(ufocraft->id)); + comp = CL_GetComponentsByID(ufocraft->id); assert(comp); /* Add components of crashed UFO to dropship. */ @@ -256,7 +182,8 @@ ccs.missionResults.ufotype = ufocraft->ufotype; /* send mail */ - UR_SendMail(ufocraft, aircraft->homebase); + assert(aircraft->homebase); + UR_SendMail(ufocraft, aircraft->homebase->name); } /*================================== @@ -264,125 +191,315 @@ ==================================*/ /** - * @brief Updates current capacities for UFO hangars in given base. - * @param[in] base The base where you want the update to be done + * @brief Function to process active recoveries. + * @sa CL_CampaignRun + * @sa UR_Prepare */ -void UR_UpdateUFOHangarCapForAll (base_t *base) +void UR_ProcessActive (void) { int i; - aircraft_t *ufocraft; - if (!base) { -#ifdef DEBUG - Com_Printf("UR_UpdateUFOHangarCapForAll: no base given!\n"); -#endif - return; + for (i = 0; i < ccs.numStoredUFOs; i++) { + storedUFO_t *ufo = US_GetStoredUFOByIDX(i); + + if (!ufo) + continue; + assert(ufo->ufoTemplate); + assert(ufo->ufoTemplate->tech); + if (ufo->ufoTemplate->tech->statusCollected) + continue; + if (ufo->arrive.day > ccs.date.day || (ufo->arrive.day == ccs.date.day && ufo->arrive.sec > ccs.date.sec)) + continue; + + RS_MarkCollected(ufo->ufoTemplate->tech); } +} - /* Reset current capacities for UFO hangar. */ - base->capacities[CAP_UFOHANGARS_LARGE].cur = 0; - base->capacities[CAP_UFOHANGARS_SMALL].cur = 0; +/* ==== UFO Storing stuff ==== */ - for (i = 0; i < csi.numODs; i++) { - /* we are looking for UFO */ - if (csi.ods[i].tech->type != RS_CRAFT) +/** + * @brief Returns an ufostore place + * @param[in] idx index of ufostore place + * @return storedUFO_t Pointer + */ +storedUFO_t* US_GetStoredUFOPlaceByIDX (const int idx) +{ + if (idx < 0 || idx >= MAX_STOREDUFOS) + return NULL; + return &(ccs.storedUFOs[idx]); +} + +/** + * @brief Returns a stored ufo + * @param[in] idx index of ufostore place + * @return storedUFO_t Pointer + */ +storedUFO_t* US_GetStoredUFOByIDX (const int idx) +{ + if (idx < 0 || idx >= ccs.numStoredUFOs) + return NULL; + return &(ccs.storedUFOs[idx]); +} + +/** + * @brief Adds an UFO to the storage + * @param[in] ufoTemplate Pointer to the aircraft(ufo)Template to add + * @param[in] installation Pointer to the installation it should be added to + * @param[in] date Date when UFO is arrives to the storage (recovery || transfer) + * @return storedUFO_t pointer to the newly stored UFO (or NULL if failed) + */ +storedUFO_t *US_StoreUFO (aircraft_t *ufoTemplate, installation_t *installation, date_t date) +{ + storedUFO_t *ufo; + + if (!ufoTemplate) { + Com_DPrintf(DEBUG_CLIENT, "US_StoreUFO: Invalid aircraft (UFO) Template.\n"); + return NULL; + } + + if (!installation) { + Com_DPrintf(DEBUG_CLIENT, "US_StoreUFO: Invalid Installation\n"); + return NULL; + } + + if (ccs.numStoredUFOs >= MAX_STOREDUFOS) { + Com_DPrintf(DEBUG_CLIENT, "US_StoreUFO: stored UFOs array is full.\n"); + return NULL; + } + + if (installation->ufoCapacity.cur >= installation->ufoCapacity.max) { + Com_DPrintf(DEBUG_CLIENT, "US_StoreUFO: Installation is full with UFOs.\n"); + return NULL; + } + + /* we can store it there */ + ufo = US_GetStoredUFOPlaceByIDX(ccs.numStoredUFOs); /* Select first empty place */ + ufo->idx = ccs.numStoredUFOs; + Q_strncpyz(ufo->id, ufoTemplate->id, sizeof(ufo->id)); + ufo->comp = CL_GetComponentsByID(ufo->id); + assert(ufo->comp); + + ufo->installation = installation; + installation->ufoCapacity.cur++; + + assert(ufoTemplate->tech); + + ufo->ufoTemplate = ufoTemplate; + ufo->disassembly = NULL; + + ufo->arrive = date; + if (date.day < ccs.date.day || (date.day == ccs.date.day && date.sec <= ccs.date.sec)) + RS_MarkCollected(ufo->ufoTemplate->tech); + + ccs.numStoredUFOs++; + + return ufo; +} + +/** + * @brief Removes an UFO from the storage + * @param[in] ufo stored UFO to remove + */ +void US_RemoveStoredUFO (storedUFO_t *ufo) +{ + int ufoCount; + int i; + + assert(ufo); + + /* Stop disassembling */ + if (ufo->disassembly) { + base_t *prodBase = PR_ProductionBase(ufo->disassembly); + + assert(prodBase); + + PR_QueueDelete(prodBase, &ccs.productions[prodBase->idx], ufo->disassembly->idx); + } + + /* Stop running research if this is the only UFO from this type + * also clear collected status */ + assert(ufo->ufoTemplate); + ufoCount = US_UFOsInStorage(ufo->ufoTemplate, NULL); + if (ufoCount <= 1 && ufo->ufoTemplate->tech->statusResearch == RS_RUNNING) { + RS_StopResearch(ufo->ufoTemplate->tech); + ufo->ufoTemplate->tech->statusCollected = qfalse; + } + + /* remove ufo */ + ufo->installation->ufoCapacity.cur--; + REMOVE_ELEM_ADJUST_IDX(ccs.storedUFOs, ufo->idx, ccs.numStoredUFOs); + + /* Adjust UFO pointer of other disassemblies */ + for (i = 0; i < ccs.numBases; i++) { + int j; + for (j = 0; j < ccs.productions[i].numItems; j++) { + production_t *prod = &(ccs.productions[i].items[j]); + + if (!prod->ufo) + continue; + if (prod->ufo > ufo) + prod->ufo--; + } + } +} + + +/** + * @brief Returns the number of UFOs stored (on an installation or anywhere) + * @param[in] ufoTemplate aircraftTemplate of the ufo + * @param[in] installation Pointer to the installation to count at + * @returns the number of ufos stored of the given ufotype at given installation or overall + * @note installation == NULL means count on every ufoyards + */ +int US_UFOsInStorage (const aircraft_t *ufoTemplate, const installation_t *installation) +{ + int i; + int count = 0; + + for (i = 0; i < ccs.numStoredUFOs; i++) { + const storedUFO_t *ufo = US_GetStoredUFOByIDX(i); + + if (!ufo) continue; + if (ufo->ufoTemplate != ufoTemplate) + continue; + if (installation && ufo->installation != installation) + continue; + if (ufo->arrive.day > ccs.date.day + || (ufo->arrive.day == ccs.date.day && ufo->arrive.sec > ccs.date.sec)) + continue; - /* do we have UFO of this type ? */ - if (!base->storage.num[i]) + count++; + } + + return count; +} + +/** + * @brief Removes ufos which are over the storing capacity + * @param[in] installation pointer to the ufoyard the ufos are stored in + */ +void US_RemoveUFOsExceedingCapacity (installation_t *installation) +{ + int i; + capacities_t *ufoCap; + + if (!installation) + Com_Error(ERR_DROP, "US_RemoveUFOsExceedingCapacity: No installation given!\n"); + + ufoCap = &(installation->ufoCapacity); + + /* loop ufos backwards */ + for (i = ccs.numStoredUFOs - 1; i >= 0; i--) { + storedUFO_t *ufo = US_GetStoredUFOByIDX(i); + + assert(ufo); + + if (ufo->installation != installation) continue; - /* look for corresponding aircraft in global array */ - ufocraft = AIR_GetAircraft(csi.ods[i].id); - if (!ufocraft) { - Com_Printf("UR_UpdateUFOHangarCapForAll: Did not find UFO %s\n", csi.ods[i].id); + if (ufoCap->cur > ufoCap->max) + US_RemoveStoredUFO(ufo); + } +} + +/** + * @brief get the closest stored ufo (of a type) to a base + * @param[in] ufoTemplate Pointer to the aircraft (ufo) template to look for (NULL for any type) + * @param[in] base Pointer to the base. If it's NULL the function simply return the first stored UFO of type + * @return storedUFO_t Pointer to the first stored UFO matches the conditions + */ +storedUFO_t *US_GetClosestStoredUFO (const aircraft_t *ufoTemplate, const base_t *base) +{ + int i; + float minDistance = -1; + storedUFO_t *closestUFO = NULL; + + for (i = 0; i < ccs.numStoredUFOs; i++) { + storedUFO_t *ufo = US_GetStoredUFOByIDX(i); + float distance = 0; + + if (!ufo) continue; - } - /* Update base capacity. */ - if (ufocraft->size == AIRCRAFT_LARGE) - base->capacities[CAP_UFOHANGARS_LARGE].cur += base->storage.num[i]; - else - base->capacities[CAP_UFOHANGARS_SMALL].cur += base->storage.num[i]; + if (ufoTemplate && ufo->ufoTemplate != ufoTemplate) + continue; + + if (ufo->arrive.day > ccs.date.day + || (ufo->arrive.day == ccs.date.day && ufo->arrive.sec > ccs.date.sec)) + continue; + + assert(ufo->installation); + if (base) + distance = MAP_GetDistance(ufo->installation->pos, base->pos); + + if (minDistance < 0 || minDistance > distance) { + minDistance = distance; + closestUFO = ufo; + } } - - Com_DPrintf(DEBUG_CLIENT, "UR_UpdateUFOHangarCapForAll: base capacities.cur: small: %i big: %i\n", base->capacities[CAP_UFOHANGARS_SMALL].cur, base->capacities[CAP_UFOHANGARS_LARGE].cur); + return closestUFO; } + /** - * @brief Function to process active recoveries. - * @sa CL_CampaignRun - * @sa UR_Prepare + * @brief Save callback for savegames in XML Format + * @sa US_LoadXML + * @sa SAV_GameSaveXML */ -void UR_ProcessActive (void) +qboolean US_SaveXML (mxml_node_t *p) { int i; + mxml_node_t *node = mxml_AddNode(p, "storedufos"); + for (i = 0; i < ccs.numStoredUFOs; i++) { + const storedUFO_t *ufo = US_GetStoredUFOByIDX(i); + mxml_node_t * snode = mxml_AddNode(node, "ufo"); - for (i = 0; i < MAX_RECOVERIES; i++) { - ufoRecoveries_t *recovery = &ccs.recoveries[i]; - if (recovery->active && recovery->event.day == ccs.date.day) { - objDef_t *od; - const aircraft_t *ufocraft; - base_t *base = recovery->base; - if (!base->founded) { - /* Destination base was destroyed meanwhile. */ - /* UFO is lost, send proper message to the user.*/ - recovery->active = qfalse; - /** @todo prepare MS_AddNewMessage() here */ - return; - } - /* Get ufocraft. */ - ufocraft = recovery->ufoTemplate; - assert(ufocraft); - /* Get item. */ - /* We can do this because aircraft id is the same as dummy item id. */ - od = INVSH_GetItemByID(ufocraft->id); - if (!od) - Com_Error(ERR_DROP, "Could not find ufo with id '%s'\n", ufocraft->id); + mxml_AddString(snode, "ufoid", ufo->id); + mxml_AddInt(snode, "day", ufo->arrive.day); + mxml_AddInt(snode, "sec", ufo->arrive.sec); - /* Process UFO recovery. */ - /* don't call B_UpdateStorageAndCapacity here - it's a dummy item */ - base->storage.num[od->idx]++; /* Add dummy UFO item to enable research topic. */ - RS_MarkCollected(od->tech); /* Enable research topic. */ - /* Reset this recovery. */ - memset(&ccs.recoveries[i], 0, sizeof(ccs.recoveries[i])); - } + if (ufo->installation) + mxml_AddInt(snode, "installationidx", ufo->installation->idx); } + return qtrue; } /** - * @brief Checks conditions for storing given ufo in given base. - * @param[in] base Pointer to the base, where we are going to store UFO. - * @param[in] ufocraft Pointer to ufocraft which we are going to store. - * @return qtrue if given base can store given ufo. + * @brief Load callback for xml savegames + * @sa US_SaveXML + * @sa SAV_GameLoadXML */ -qboolean UR_ConditionsForStoring (const base_t *base, const aircraft_t *ufocraft) +qboolean US_LoadXML (mxml_node_t *p) { - assert(base && ufocraft); + int i; + mxml_node_t *node, *snode; - if (ufocraft->size == AIRCRAFT_LARGE) { - /* Large UFOs can only fit large UFO hangars */ - if (!B_GetBuildingStatus(base, B_UFO_HANGAR)) - return qfalse; + node = mxml_GetNode(p, "storedufos"); - /* Check there is still enough room for this UFO */ - if (base->capacities[CAP_UFOHANGARS_LARGE].max <= base->capacities[CAP_UFOHANGARS_LARGE].cur) + for (i = 0, snode = mxml_GetNode(node, "ufo"); i < MAX_STOREDUFOS && snode; + i++, snode = mxml_GetNextNode(snode, node, "ufo")) { + aircraft_t *ufoTemplate = AIR_GetAircraft(mxml_GetString(snode, "ufoid")); + int instIDX = mxml_GetInt(snode, "installationidx", MAX_INSTALLATIONS); + installation_t *inst = (instIDX == MAX_INSTALLATIONS) ? NULL : INS_GetFoundedInstallationByIDX(instIDX); + date_t arrive; + + arrive.day = mxml_GetInt(snode, "day", 0); + arrive.sec = mxml_GetInt(snode, "sec", 0); + + if (!ufoTemplate) return qfalse; - } else { - /* This is a small UFO, can only fit in small hangar */ - /* There's no small hangar functional */ - if (!B_GetBuildingStatus(base, B_UFO_SMALL_HANGAR)) + if (!inst) return qfalse; - /* Check there is still enough room for this UFO */ - if (base->capacities[CAP_UFOHANGARS_SMALL].max <= base->capacities[CAP_UFOHANGARS_SMALL].cur) - return qfalse; + if (!US_StoreUFO(ufoTemplate, inst, arrive)) + Com_Printf("Cannot store ufo %s at installation idx=%i.\n", ufoTemplate->id, instIDX); } - return qtrue; } + void UR_InitStartup (void) { Cmd_AddCommand("cp_ufocrashed", CP_UFOCrashed_f, "Function to process crashed UFO after a mission."); Index: src/client/campaign/cp_parse.c =================================================================== --- src/client/campaign/cp_parse.c (revision 25739) +++ src/client/campaign/cp_parse.c (working copy) @@ -605,6 +605,24 @@ } /** + * @brief Returns components definition by ID. + * @param[in] id assemblyId of the component definition. + * @return comp Pointer to components_t definition. + */ +components_t *CL_GetComponentsByID(const char *id) +{ + int i; + + for (i = 0; i < ccs.numComponents; i++) { + components_t *comp = &ccs.components[i]; + if (!strcmp(comp->assemblyId, id)) { + return comp; + } + } + Com_Error(ERR_DROP, "CL_GetComponentsByItem: could not find components id for: %s", id); +} + +/** * @brief Parsing only for singleplayer * * parsed if we are no dedicated server Index: src/client/campaign/cp_uforecovery.h =================================================================== --- src/client/campaign/cp_uforecovery.h (revision 25739) +++ src/client/campaign/cp_uforecovery.h (working copy) @@ -1,10 +1,10 @@ /** * @file cp_uforecovery.h - * @brief UFO recovery + * @brief UFO recovery and storing */ /* -Copyright (C) 2002-2007 UFO: Alien Invasion team. +Copyright (C) 2002-2009 UFO: Alien Invasion team. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License @@ -33,15 +33,42 @@ * @sa ufoRecovery_t */ typedef struct ufoRecoveries_s { qboolean active; /**< True if the recovery is under processing. */ - base_t *base; /**< Base where the recovery will be processed. */ + installation_t *installation; /**< UFO Yard where the recovery will be processed. */ aircraft_t *ufoTemplate; /**< Entry of UFO in aircraftTemplates array. */ date_t event; /**< When the process will start (UFO got transported to base). */ } ufoRecoveries_t; -void UR_Prepare(base_t *base, ufoType_t ufoType); -void UR_UpdateUFOHangarCapForAll(base_t *base); +#define MAX_STOREDUFOS 128 + +/* time the recovery takes in days */ +#define RECOVERY_DELAY 2.0f + +/** @brief Structure for stored UFOs */ +typedef struct storedUFO_s { + int idx; + char id[MAX_VAR]; + components_t *comp; + aircraft_t *ufoTemplate; + + /* arrival date (recovery/transfer) */ + date_t arrive; + + /* installation UFO is stored */ + installation_t *installation; + + /* link to disassembly item */ + production_t *disassembly; +} storedUFO_t; + void UR_ProcessActive(void); -qboolean UR_ConditionsForStoring(const base_t *base, const aircraft_t *ufocraft); void UR_InitStartup(void); +storedUFO_t *US_StoreUFO(aircraft_t *ufoTemplate, installation_t *installation, date_t date); +storedUFO_t *US_GetStoredUFOPlaceByIDX(const int idx); +storedUFO_t *US_GetStoredUFOByIDX(const int idx); +storedUFO_t *US_GetClosestStoredUFO(const aircraft_t *ufoTemplate, const base_t *base); +void US_RemoveStoredUFO(storedUFO_t *ufo); +int US_UFOsInStorage(const aircraft_t *ufoTemplate, const installation_t *installation); +void US_RemoveUFOsExceedingCapacity(installation_t *installation); + #endif Index: src/client/campaign/cp_parse.h =================================================================== --- src/client/campaign/cp_parse.h (revision 25739) +++ src/client/campaign/cp_parse.h (working copy) @@ -47,5 +47,6 @@ void CL_ParseCampaign(const char *name, const char **text); void CL_ScriptSanityCheck(void); components_t *CL_GetComponentsByItem(const objDef_t *item); +components_t *CL_GetComponentsByID(const char *id); #endif Index: src/client/campaign/cp_aircraft.c =================================================================== --- src/client/campaign/cp_aircraft.c (revision 25739) +++ src/client/campaign/cp_aircraft.c (working copy) @@ -802,20 +802,11 @@ int AIR_GetCapacityByAircraftWeight (const aircraft_t *aircraft) { - if (aircraft->ufotype == UFO_MAX) { - switch (aircraft->size) { - case AIRCRAFT_SMALL: - return CAP_AIRCRAFT_SMALL; - case AIRCRAFT_LARGE: - return CAP_AIRCRAFT_BIG; - } - } else { - switch (aircraft->size) { - case AIRCRAFT_SMALL: - return CAP_UFOHANGARS_SMALL; - case AIRCRAFT_LARGE: - return CAP_UFOHANGARS_LARGE; - } + switch (aircraft->size) { + case AIRCRAFT_SMALL: + return CAP_AIRCRAFT_SMALL; + case AIRCRAFT_LARGE: + return CAP_AIRCRAFT_BIG; } Com_Error(ERR_DROP, "AIR_GetCapacityByAircraftWeight: Unkown weight of aircraft '%i'\n", aircraft->size); } @@ -2604,22 +2595,6 @@ mxml_AddBool(snode, "laser", ccs.projectiles[i].laser); } - /* Save recoveries. */ - for (i = 0; i < MAX_RECOVERIES; i++) { - /*check, if the transfer is active */ - /** @todo Check if we can really ignore them if active is false */ - if (!ccs.recoveries[i].active) - continue; - snode = mxml_AddNode(node, "recovery"); - /* mxml_AddBool(snode, "active", ccs.recoveries[i].active); */ - if (ccs.recoveries[i].base) - mxml_AddInt(snode, "targetbase", ccs.recoveries[i].base->idx); - /** @todo At some point we really need to save a unique string here. */ - if (ccs.recoveries[i].ufoTemplate) - mxml_AddInt(snode, "ufotemplateidx", ccs.recoveries[i].ufoTemplate->idx); - mxml_AddInt(snode, "day", ccs.recoveries[i].event.day); - mxml_AddInt(snode, "sec", ccs.recoveries[i].event.sec); - } return qtrue; } @@ -2931,17 +2906,28 @@ ccs.numProjectiles = i; - for (i = 0, snode = mxml_GetNode(node, "recovery"); snode && i < MAX_RECOVERIES; - snode = mxml_GetNextNode(snode, node, "recovery"), i++) { - byte base, ufotype; - ccs.recoveries[i].active = qtrue; - base = mxml_GetInt(snode, "targetbase", BYTES_NONE); - ccs.recoveries[i].base = (base != BYTES_NONE) ? B_GetBaseByIDX((byte)base) : NULL; - assert(ccs.numAircraftTemplates); - ufotype = mxml_GetInt(snode, "ufotemplateidx", BYTES_NONE); - ccs.recoveries[i].ufoTemplate = (ufotype != BYTES_NONE) ? &ccs.aircraftTemplates[ufotype] : NULL; - ccs.recoveries[i].event.day = mxml_GetInt(snode, "day", 0); - ccs.recoveries[i].event.sec = mxml_GetInt(snode, "sec", 0); + /* fallback code to keep compatibility with UFO Hangars */ + /* @todo remove this on release or after a time */ + for (snode = mxml_GetNode(node, "recovery"); snode; + snode = mxml_GetNextNode(snode, node, "recovery")) { + installation_t *inst = INS_GetFirstUFOYard(qtrue); + byte ufotype = mxml_GetInt(snode, "ufotemplateidx", BYTES_NONE); + date_t event; + + if (!inst) { + Com_Printf("AIR_LoadXML: No more free UFOYards for recovery\n"); + break; + } + + event.day = mxml_GetInt(snode, "day", 0); + event.sec = mxml_GetInt(snode, "sec", 0); + + if (ufotype >= ccs.numAircraftTemplates) { + Com_Printf("AIR_LoadXML: Invalid template idx %i\n", ufotype); + continue; + } + + US_StoreUFO(&(ccs.aircraftTemplates[ufotype]), inst, event); } for (i = ccs.numUFOs - 1; i >= 0; i--) { Index: src/client/campaign/cp_research_callbacks.c =================================================================== --- src/client/campaign/cp_research_callbacks.c (revision 25739) +++ src/client/campaign/cp_research_callbacks.c (working copy) @@ -608,10 +608,7 @@ switch (tech->statusResearch) { case RS_RUNNING: - /* Remove all scis from it and set the status to paused (i.e. it's differnet from a RS_NONE since it may have a little bit of progress already). */ - while (tech->scientists > 0) - RS_RemoveScientist(tech, NULL); - assert(tech->statusResearch == RS_PAUSED); + RS_StopResearch(tech); break; case RS_PAUSED: /** @todo remove? Popup info how much is already researched? */ Index: src/client/campaign/cp_base_callbacks.c =================================================================== --- src/client/campaign/cp_base_callbacks.c (revision 25739) +++ src/client/campaign/cp_base_callbacks.c (working copy) @@ -33,6 +33,7 @@ #include "cp_base.h" #include "cp_map.h" #include "cp_popup.h" +#include "cp_ufo.h" /** @brief Used from menu scripts as parameter for mn_select_base */ #define CREATE_NEW_BASE_ID -1 @@ -377,10 +378,6 @@ case B_SMALL_HANGAR: B_RemoveAircraftExceedingCapacity(base, buildingType); break; - case B_UFO_HANGAR: - case B_UFO_SMALL_HANGAR: - B_RemoveUFOsExceedingCapacity(base, buildingType); - break; case B_QUARTERS: E_DeleteEmployeesExceedingCapacity(base); break; @@ -853,8 +850,17 @@ const production_t *production = &queue->items[i]; const objDef_t *objDef = production->item; const aircraft_t *aircraft = production->aircraft; - const char *name = objDef ? _(objDef->name) : _(aircraft->name); - assert(name); + const storedUFO_t *ufo = production->ufo; + const char *name; + + if (objDef) + name = _(objDef->name); + else if (aircraft) + name = _(aircraft->name); + else if (ufo) + name = _(UFO_TypeToName(ufo->ufoTemplate->ufotype)); + else + Com_Error(ERR_DROP, "BaseSummary_Init: Invalid production type (not item, not aircraft, not disassembly)\n"); /** @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, Index: src/client/campaign/cp_transfer.c =================================================================== --- src/client/campaign/cp_transfer.c (revision 25739) +++ src/client/campaign/cp_transfer.c (working copy) @@ -111,8 +111,9 @@ */ static int TR_CheckItem (const objDef_t *od, const base_t *destbase, int amount) { - int i, intransfer = 0, amtransfer = 0; - int smallufotransfer = 0, bigufotransfer = 0; + int i; + int intransfer = 0; + int amtransfer = 0; assert(od); assert(destbase); @@ -122,14 +123,7 @@ if (td.trItemsTmp[i] > 0) { if (!strcmp(csi.ods[i].id, ANTIMATTER_TECH_ID)) amtransfer = ANTIMATTER_SIZE * td.trItemsTmp[i]; - if (od->tech->type == RS_CRAFT) { - aircraft_t *ufocraft = AIR_GetAircraft(od->tech->provides); - assert(ufocraft); - if (ufocraft->size == AIRCRAFT_LARGE) - bigufotransfer++; - else - smallufotransfer++; - } else + else intransfer += csi.ods[i].size * td.trItemsTmp[i]; } } @@ -151,33 +145,7 @@ /* amount to transfer can't be bigger than what we have */ amount = min(amount, (destbase->capacities[CAP_ANTIMATTER].max - destbase->capacities[CAP_ANTIMATTER].cur - amtransfer) / ANTIMATTER_SIZE); } - } else if (od->tech->type == RS_CRAFT) { /* This is UFO craft */ - aircraft_t *ufocraft = AIR_GetAircraft(od->tech->provides); - assert(ufocraft); - if (ufocraft->size == AIRCRAFT_LARGE) { - if (!B_GetBuildingStatus(destbase, B_UFO_HANGAR)) { - MN_Popup(_("Missing Large UFO Hangar"), _("Destination base does not have functional Large UFO Hangar.\n")); - return 0; - } else { - amount = min(amount, destbase->capacities[CAP_UFOHANGARS_LARGE].max - destbase->capacities[CAP_UFOHANGARS_LARGE].cur - bigufotransfer); - if (amount <= 0) { - MN_Popup(_("Missing Large UFO Hangar"), _("Destination base does not have enough Large UFO Hangar.\n")); - return 0; - } - } - } else if (ufocraft->size == AIRCRAFT_SMALL) { - if (!B_GetBuildingStatus(destbase, B_UFO_SMALL_HANGAR)) { - MN_Popup(_("Missing Small UFO Hangar"), _("Destination base does not have functional Small UFO Hangar.\n")); - return qfalse; - } else { - amount = min(amount, destbase->capacities[CAP_UFOHANGARS_SMALL].max - destbase->capacities[CAP_UFOHANGARS_SMALL].cur - smallufotransfer); - if (amount <= 0) { - MN_Popup(_("Missing Small UFO Hangar"), _("Destination base does not have enough Small UFO Hangar.\n")); - return 0; - } - } - } - } else { /*This is not antimatter*/ + } else { /*This is not antimatter */ if (!B_GetBuildingStatus(destbase, B_STORAGE)) /* Return if the target base doesn't have storage or power. */ return 0; @@ -689,16 +657,7 @@ if (td.trItemsTmp[i] > 0) { if (!strcmp(csi.ods[i].id, ANTIMATTER_TECH_ID)) B_ManageAntimatter(base, td.trItemsTmp[i], qtrue); - else if (csi.ods[i].tech->type == RS_CRAFT) { /* This is UFO craft */ - const aircraft_t *ufocraft = AIR_GetAircraft(csi.ods[i].tech->provides); - assert(ufocraft); - /* don't use B_UpdateStorageAndCapacity: UFO are not stored in storage */ - base->storage.num[i] += td.trItemsTmp[i]; - if (ufocraft->size == AIRCRAFT_LARGE) - base->capacities[CAP_UFOHANGARS_LARGE].cur++; - else - base->capacities[CAP_UFOHANGARS_SMALL].cur++; - } else + else B_UpdateStorageAndCapacity(base, &csi.ods[i], td.trItemsTmp[i], qfalse, qfalse); } } @@ -744,20 +703,7 @@ if (transfer->itemAmount[i] > 0) { if (!strcmp(csi.ods[i].id, ANTIMATTER_TECH_ID)) B_ManageAntimatter(destination, transfer->itemAmount[i], qtrue); - else if (csi.ods[i].tech->type == RS_CRAFT) { /* This is UFO craft */ - const aircraft_t *ufocraft = AIR_GetAircraft(csi.ods[i].tech->provides); - assert(ufocraft); - for (j = 0; j < transfer->itemAmount[i]; j++) { - if (UR_ConditionsForStoring(destination, ufocraft)) { - /* don't use B_UpdateStorageAndCapacity: UFO are not stored in storage */ - destination->storage.num[i]++; - if (ufocraft->size == AIRCRAFT_LARGE) - destination->capacities[CAP_UFOHANGARS_LARGE].cur++; - else - destination->capacities[CAP_UFOHANGARS_SMALL].cur++; - } - } - } else + else B_UpdateStorageAndCapacity(destination, &csi.ods[i], transfer->itemAmount[i], qfalse, qtrue); } } @@ -1202,16 +1148,7 @@ td.trItemsTmp[i] += amount; if (!strcmp(od->id, ANTIMATTER_TECH_ID)) B_ManageAntimatter(base, amount, qfalse); - else if (od->tech->type == RS_CRAFT) { /* This is UFO craft */ - const aircraft_t *ufocraft = AIR_GetAircraft(csi.ods[i].tech->provides); - assert(ufocraft); - /* don't use B_UpdateStorageAndCapacity: UFO are not stored in storage */ - base->storage.num[i] -= amount; - if (ufocraft->size == AIRCRAFT_LARGE) - base->capacities[CAP_UFOHANGARS_LARGE].cur -= amount; - else - base->capacities[CAP_UFOHANGARS_SMALL].cur -= amount; - } else + else B_UpdateStorageAndCapacity(base, od, -amount, qfalse, qfalse); break; } else @@ -1569,16 +1506,7 @@ td.trItemsTmp[i] -= amount; if (!strcmp(csi.ods[i].id, ANTIMATTER_TECH_ID)) B_ManageAntimatter(base, amount, qfalse); - else if (csi.ods[i].tech->type == RS_CRAFT) { /* This is UFO craft */ - const aircraft_t *ufocraft = AIR_GetAircraft(csi.ods[i].tech->provides); - assert(ufocraft); - /* don't use B_UpdateStorageAndCapacity: UFO are not stored in storage */ - base->storage.num[i] += amount; - if (ufocraft->size == AIRCRAFT_LARGE) - base->capacities[CAP_UFOHANGARS_LARGE].cur += amount; - else - base->capacities[CAP_UFOHANGARS_SMALL].cur += amount; - } else + else B_UpdateStorageAndCapacity(base, &csi.ods[i], amount, qfalse, qfalse); break; } Index: src/client/campaign/cp_save.c =================================================================== --- src/client/campaign/cp_save.c (revision 25739) +++ src/client/campaign/cp_save.c (working copy) @@ -650,6 +650,7 @@ static saveSubsystems_t xvi_subsystemXML = {"xvirate", XVI_SaveXML, XVI_LoadXML}; static saveSubsystems_t ins_subsystemXML = {"installation", INS_SaveXML, INS_LoadXML}; static saveSubsystems_t mso_subsystemXML = {"messageoptions", MSO_SaveXML, MSO_LoadXML}; + static saveSubsystems_t us_subsystemXML = {"ufostores", US_SaveXML, US_LoadXML}; saveSubsystemsAmount = 0; memset(&saveSubsystems, 0, sizeof(saveSubsystems)); @@ -664,15 +665,16 @@ SAV_AddSubsystem(&rs_subsystemXML); SAV_AddSubsystem(&e_subsystemXML); SAV_AddSubsystem(&ac_subsystemXML); + SAV_AddSubsystem(&air_subsystemXML); + SAV_AddSubsystem(&ins_subsystemXML); + SAV_AddSubsystem(&us_subsystemXML); SAV_AddSubsystem(&pr_subsystemXML); - SAV_AddSubsystem(&air_subsystemXML); SAV_AddSubsystem(&ms_subsystemXML); SAV_AddSubsystem(&stats_subsystemXML); SAV_AddSubsystem(&na_subsystemXML); SAV_AddSubsystem(&trans_subsystemXML); SAV_AddSubsystem(&ab_subsystemXML); SAV_AddSubsystem(&xvi_subsystemXML); - SAV_AddSubsystem(&ins_subsystemXML); SAV_AddSubsystem(&mso_subsystemXML); Cmd_AddCommand("game_quickloadinit", SAV_GameQuickLoadInit_f, "Check whether there is a quicksave at all"); Index: src/client/campaign/cp_produce_callbacks.c =================================================================== --- src/client/campaign/cp_produce_callbacks.c (revision 25739) +++ src/client/campaign/cp_produce_callbacks.c (working copy) @@ -5,7 +5,7 @@ */ /* -Copyright (C) 1997-2008 UFO:AI Team +Copyright (C) 1997-2009 UFO:AI Team This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License @@ -29,6 +29,7 @@ #include "../menu/m_popup.h" #include "cp_campaign.h" #include "cp_market.h" +#include "cp_ufo.h" #include "cp_produce.h" #include "cp_produce_callbacks.h" @@ -50,7 +51,7 @@ /** Currently selected entry in the productionItemList (depends on content) */ static objDef_t *selectedItem = NULL; -static components_t *selectedDisassembly = NULL; +static storedUFO_t *selectedDisassembly = NULL; static aircraft_t *selectedAircraft = NULL; /** @brief Number of blank lines between queued items and tech list. */ @@ -68,28 +69,6 @@ } /** - * @brief Conditions for disassembling. - * @param[in] base Pointer to base where the component is stored. - * @param[in] comp Pointer to components definition. - * @return qtrue if disassembling is ready, qfalse otherwise. - */ -static qboolean PR_ConditionsDisassembly (const base_t *base, const components_t *comp) -{ - const objDef_t *od; - - assert(base); - assert(comp); - - od = comp->assemblyItem; - assert(od); - - if (RS_IsResearched_ptr(od->tech) && base->storage.num[od->idx] > 0) - return qtrue; - else - return qfalse; -} - -/** * @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. @@ -112,6 +91,12 @@ producible = qfalse; } } + if (req->type == RS_LINK_UFO) { + /* The same code is used in "RS_RequirementsMet" */ + Com_DPrintf(DEBUG_CLIENT, "PR_RequirementsMet: %s\n", req->id); + if (US_UFOsInStorage(req->link, NULL) < req->amount) + producible = qfalse; + } } if (producible) producibleAmount++; @@ -130,13 +115,13 @@ * @param[in] amount Desired amount to produce. * @param[in] disassembling True if this is disassembling, false if production. */ -static production_t *PR_QueueNew (base_t *base, production_queue_t *queue, objDef_t *item, aircraft_t *aircraftTemplate, signed int amount, qboolean disassembling) +static production_t *PR_QueueNew (base_t *base, production_queue_t *queue, objDef_t *item, aircraft_t *aircraftTemplate, storedUFO_t *ufo, signed int amount) { int numWorkshops = 0; production_t *prod; const technology_t *tech; - assert((item && !aircraftTemplate) || (!item && aircraftTemplate)); + assert((item && !aircraftTemplate && !ufo) || (!item && aircraftTemplate && !ufo) || (!item && !aircraftTemplate && ufo)); assert(base); if (queue->numItems >= MAX_PRODUCTIONS) @@ -161,13 +146,15 @@ /* self-reference. */ prod->idx = queue->numItems; - if (produceCategory != FILTER_AIRCRAFT) + if (item) tech = item->tech; - else + else if (aircraftTemplate) tech = aircraftTemplate->tech; + else + tech = ufo->ufoTemplate->tech; /* We cannot queue new aircraft if no free hangar space. */ - if (produceCategory == FILTER_AIRCRAFT) { + if (aircraftTemplate) { if (!B_GetBuildingStatus(base, B_COMMAND)) { MN_Popup(_("Hangars not ready"), _("You cannot queue aircraft.\nNo command centre in this base.\n")); return NULL; @@ -184,18 +171,20 @@ prod->item = item; prod->aircraft = aircraftTemplate; - prod->amount = amount; - if (disassembling) { + prod->ufo = ufo; + + if (ufo) { /* Disassembling. */ prod->production = qfalse; + prod->amount = 1; - /* We have to remove amount of items being disassembled from base storage. */ - base->storage.num[item->idx] -= amount; + ufo->disassembly = prod; /* Now find related components definition. */ prod->percentDone = 0.0f; } else { /* Production. */ prod->production = qtrue; + prod->amount = amount; /* Don't try to add to queue an item which is not producible. */ if (tech->produceTime < 0) @@ -227,12 +216,12 @@ /* First add all the queue items ... */ for (i = 0; i < queue->numItems; i++) { const production_t *prod = &queue->items[i]; - if (!prod->aircraft) { + if (prod->item) { const objDef_t *od = prod->item; LIST_AddString(&productionList, va("%s", _(od->name))); LIST_AddString(&productionAmount, va("%i", base->storage.num[prod->item->idx])); LIST_AddString(&productionQueued, va("%i", prod->amount)); - } else { + } else if (prod->aircraft) { const aircraft_t *aircraftTemplate = prod->aircraft; LIST_AddString(&productionList, va("%s", _(aircraftTemplate->name))); for (j = 0, counter = 0; j < ccs.numAircraft; j++) { @@ -243,6 +232,12 @@ } LIST_AddString(&productionAmount, va("%i", counter)); LIST_AddString(&productionQueued, va("%i", prod->amount)); + } else if (prod->ufo) { + const storedUFO_t *ufo = prod->ufo; + + LIST_AddString(&productionList, va("%s (at %s)", _(UFO_TypeToName(ufo->ufoTemplate->ufotype)), ufo->installation->name)); + LIST_AddString(&productionAmount, va("%i", US_UFOsInStorage(ufo->ufoTemplate, ufo->installation))); + LIST_AddString(&productionQueued, "1"); } } @@ -257,18 +252,26 @@ /* Then go through all object definitions ... */ if (produceCategory == FILTER_DISASSEMBLY) { - for (i = 0; i < ccs.numComponents; i++) { - const objDef_t *asOd = ccs.components[i].assemblyItem; - components_t *comp = &ccs.components[i]; - if (!asOd) + /** UFOs at UFO stores */ + for (i = 0; i < ccs.numStoredUFOs; i++) { + storedUFO_t *ufo = US_GetStoredUFOByIDX(i); + + if (!ufo) continue; - if (PR_ConditionsDisassembly(base, comp)) { - LIST_AddPointer(&productionItemList, comp); + /* The UFO is being disassembled aready */ + if (ufo->disassembly) + continue; + /* UFO not researched */ + if (!RS_IsResearched_ptr(ufo->ufoTemplate->tech)) + continue; + /* UFO is being transported */ + if (ufo->arrive.day > ccs.date.day || (ufo->arrive.day == ccs.date.day && ufo->arrive.sec > ccs.date.sec)) + continue; - LIST_AddString(&productionList, va("%s", _(asOd->name))); - LIST_AddString(&productionAmount, va("%i", base->storage.num[asOd->idx])); - LIST_AddString(&productionQueued, ""); - } + LIST_AddPointer(&productionItemList, ufo); + LIST_AddString(&productionList, va("%s (at %s)", _(UFO_TypeToName(ufo->ufoTemplate->ufotype)), ufo->installation->name)); + LIST_AddString(&productionAmount, va("%i", US_UFOsInStorage(ufo->ufoTemplate, ufo->installation))); + LIST_AddString(&productionQueued, ""); } } else if (produceCategory == FILTER_AIRCRAFT) { for (i = 0; i < ccs.numAircraftTemplates; i++) { @@ -343,7 +346,7 @@ Com_sprintf(productionInfo, sizeof(productionInfo), _("No item selected")); Cvar_Set("mn_item", ""); } else { - prodPerHour = PR_CalculateProductionPercentDone(base, od->tech, NULL, qfalse); + prodPerHour = PR_CalculateProductionPercentDone(base, od->tech, NULL); /* If you entered production menu, that means that prodPerHour > 0 (must not divide by 0) */ assert(prodPerHour > 0); time = ceil((1.0f - percentDone) / prodPerHour); @@ -363,35 +366,35 @@ * @param[in] base Pointer to the base where informations should be printed. * @sa PR_ProductionInfo */ -static void PR_DisassemblyInfo (const base_t *base, const objDef_t *od, const components_t *comp, float percentDone) +static void PR_DisassemblyInfo (const base_t *base, const storedUFO_t *ufo, float percentDone) { static char productionInfo[512]; int time, i; float prodPerHour; assert(base); - assert(od); - assert(comp); - assert(od->tech); + assert(ufo); + assert(ufo->ufoTemplate); + assert(ufo->ufoTemplate->tech); - prodPerHour = PR_CalculateProductionPercentDone(base, od->tech, comp, qtrue); + prodPerHour = PR_CalculateProductionPercentDone(base, ufo->ufoTemplate->tech, ufo->comp); /* If you entered production menu, that means that prodPerHour > 0 (must not divide by 0) */ assert(prodPerHour > 0); time = ceil((1.0f - percentDone) / prodPerHour); - Com_sprintf(productionInfo, sizeof(productionInfo), _("%s - disassembly\n"), _(od->name)); - Q_strcat(productionInfo, _("Components: "), sizeof(productionInfo)); + Com_sprintf(productionInfo, sizeof(productionInfo), _("%s - disassembly\n"), _(UFO_TypeToName(ufo->ufoTemplate->ufotype))); + Q_strcat(productionInfo, _("Components:\n"), sizeof(productionInfo)); /* Print components. */ - for (i = 0; i < comp->numItemtypes; i++) { - const objDef_t *compOd = comp->items[i]; + for (i = 0; i < ufo->comp->numItemtypes; i++) { + const objDef_t *compOd = ufo->comp->items[i]; assert(compOd); - Q_strcat(productionInfo, va(_("%s (%i) "), _(compOd->name), comp->itemAmount[i]), + Q_strcat(productionInfo, va(" %s (%i)\n", _(compOd->name), ufo->comp->itemAmount[i]), sizeof(productionInfo)); } Q_strcat(productionInfo, "\n", sizeof(productionInfo)); Q_strcat(productionInfo, va(_("Disassembly time\t%ih\n"), time), sizeof(productionInfo)); - Cvar_Set("mn_item", od->id); MN_RegisterText(TEXT_PRODUCTION_INFO, productionInfo); + Cvar_Set("mn_item", ufo->id); } /** @@ -427,10 +430,15 @@ MN_ExecuteConfunc("prod_taskselected"); if (prod->aircraft) { PR_AircraftInfo(prod->aircraft); - } else if (prod->production) { + MN_ExecuteConfunc("amountsetter enable"); + } else if (prod->item) { PR_ItemProductionInfo(base, prod->item, prod->percentDone); + MN_ExecuteConfunc("amountsetter enable"); + } else if (prod->ufo) { + PR_DisassemblyInfo(base, prod->ufo, prod->percentDone); + MN_ExecuteConfunc("amountsetter disable"); } else { - PR_DisassemblyInfo(base, prod->item, CL_GetComponentsByItem(prod->item), prod->percentDone); + Com_Error(ERR_DROP, "PR_ProductionInfo: Selected production is not item nor aircraft nor ufo.\n"); } Cvar_SetValue("mn_production_amount", selectedProduction->amount); } else { @@ -448,7 +456,7 @@ } else if (selectedItem) { PR_ItemProductionInfo(base, selectedItem, 0.0); } else if (selectedDisassembly) { - PR_DisassemblyInfo(base, selectedDisassembly->assemblyItem, selectedDisassembly, 0.0); + PR_DisassemblyInfo(base, selectedDisassembly, 0.0); } } } @@ -485,10 +493,12 @@ if (selectedProduction->aircraft) { assert(selectedProduction->aircraft->tech); UP_OpenWith(selectedProduction->aircraft->tech->id); - } else { + } else if (selectedProduction->item) { const objDef_t *od = selectedProduction->item; assert(od->tech); UP_OpenWith(od->tech->id); + } else if (selectedProduction->ufo) { + UP_OpenWith(selectedProduction->ufo->ufoTemplate->tech->id); } } else if (num >= queue->numItems + QUEUE_SPACERS) { /* Clicked in the item list. */ @@ -502,9 +512,9 @@ if (aircraftTemplate && aircraftTemplate->tech) UP_OpenWith(aircraftTemplate->tech->id); } else if (produceCategory == FILTER_DISASSEMBLY) { - components_t *comp = (components_t*)LIST_GetByIdx(productionItemList, idx); - if (comp && comp->assemblyItem && comp->assemblyItem->tech) { - UP_OpenWith(comp->assemblyItem->tech->id); + storedUFO_t *ufo = (storedUFO_t*)LIST_GetByIdx(productionItemList, idx); + if (ufo && ufo->ufoTemplate && ufo->ufoTemplate->tech) { + UP_OpenWith(ufo->ufoTemplate->tech->id); } } else { objDef_t *od = (objDef_t*)LIST_GetByIdx(productionItemList, idx); @@ -568,10 +578,10 @@ const int idx = num - queue->numItems - QUEUE_SPACERS; if (produceCategory == FILTER_DISASSEMBLY) { - components_t *comp = (components_t*)LIST_GetByIdx(productionItemList, idx); + storedUFO_t *ufo = (storedUFO_t*)LIST_GetByIdx(productionItemList, idx); PR_ClearSelected(); - selectedDisassembly = comp; + selectedDisassembly = ufo; PR_ProductionInfo(base); } else if (produceCategory == FILTER_AIRCRAFT) { @@ -650,7 +660,7 @@ if (produceCategory == FILTER_AIRCRAFT) selectedAircraft = (aircraft_t*)LIST_GetByIdx(productionItemList, 0); else if (produceCategory == FILTER_DISASSEMBLY) - selectedDisassembly = (components_t*)LIST_GetByIdx(productionItemList, 0); + selectedDisassembly = (storedUFO_t*)LIST_GetByIdx(productionItemList, 0); else selectedItem = (objDef_t*)LIST_GetByIdx(productionItemList, 0); } @@ -702,7 +712,7 @@ */ static void PR_ProductionIncrease_f (void) { - int amount = 1, amountTemp = 0; + int amount = 1; production_queue_t *queue; production_t *prod; base_t *base = B_GetCurrentSelectedBase(); @@ -741,26 +751,8 @@ prod->amount += amount; } } else { /* Disassembling. */ - /* We can disassembly only as many items as we have in base storage. */ - if (base->storage.num[prod->item->idx] > amount) - amountTemp = amount; - else - amountTemp = base->storage.num[prod->item->idx]; - Com_DPrintf(DEBUG_CLIENT, "PR_ProductionIncrease_f: amounts: storage: %i, param: %i, temp: %i\n", base->storage.num[prod->item->idx], amount, amountTemp); - - /* Now check if we can add more items and - * remove the amount we just added to queue from base storage. */ - if (prod->amount + amountTemp > MAX_PRODUCTION_AMOUNT) { - if (MAX_PRODUCTION_AMOUNT - prod->amount >= 0) { - /* Calculate the maximum allowed amount to be added. */ - amountTemp = MAX_PRODUCTION_AMOUNT - prod->amount; - } else { - return; - } - } - - base->storage.num[prod->item->idx] -= amountTemp; - prod->amount += amountTemp; + /* We can disassembly UFOs only one-by-one. */ + return; } } else { if (!selectedDisassembly) { @@ -768,23 +760,10 @@ MN_Popup(_("Hangars not ready"), _("You cannot queue aircraft.\nNo free space in hangars.\n")); return; } - prod = PR_QueueNew(base, queue, selectedItem, selectedAircraft, amount, qfalse); /* Production. (only of of the "selected" pointer can be nonNULL) */ + prod = PR_QueueNew(base, queue, selectedItem, selectedAircraft, NULL, amount); /* Production. (only of of the "selected" pointer can be nonNULL) */ } else { - /* We can disassemble only as many items as we have in base storage. */ - assert(selectedDisassembly && selectedDisassembly->assemblyItem); - - if (base->storage.num[selectedDisassembly->assemblyItem->idx] > amount) - amountTemp = amount; - else - amountTemp = base->storage.num[selectedDisassembly->assemblyItem->idx]; - - /* Check if we can add more items. */ - if (amountTemp > MAX_PRODUCTION_AMOUNT) { - /* Add as many items as possible. */ - amountTemp = MAX_PRODUCTION_AMOUNT; - } - - prod = PR_QueueNew(base, queue, selectedDisassembly->assemblyItem, NULL, amountTemp, qtrue); /* Disassembling. */ + /* We can disassemble UFOs only one-by-one. */ + prod = PR_QueueNew(base, queue, NULL, NULL, selectedDisassembly, 1); /* Disassembling. */ } /** prod is NULL when queue limit is reached @@ -796,7 +775,8 @@ } else MN_ExecuteConfunc("prod_selectline %i", prod->idx); - if (produceCategory != FILTER_AIRCRAFT) { + + if (prod->item) { /* Get technology of the item in the selected queue-entry. */ const objDef_t *od = prod->item; int producibleAmount = amount; @@ -816,13 +796,8 @@ amount, producibleAmount)); } - if (!selectedDisassembly) { - Com_sprintf(cp_messageBuffer, sizeof(cp_messageBuffer), _("Production of %s started"), _(od->name)); - MSO_CheckAddNewMessage(NT_PRODUCTION_STARTED, _("Production started"), cp_messageBuffer, qfalse, MSG_PRODUCTION, od->tech); - } else { - Com_sprintf(cp_messageBuffer, sizeof(cp_messageBuffer), _("Disassembling of %s started"), _(od->name)); - MSO_CheckAddNewMessage(NT_PRODUCTION_STARTED, _("Production started"), cp_messageBuffer, qfalse, MSG_PRODUCTION, od->tech); - } + Com_sprintf(cp_messageBuffer, sizeof(cp_messageBuffer), _("Production of %s started"), _(od->name)); + MSO_CheckAddNewMessage(NT_PRODUCTION_STARTED, _("Production started"), cp_messageBuffer, qfalse, MSG_PRODUCTION, od->tech); /* Now we select the item we just created. */ PR_ClearSelected(); @@ -835,7 +810,7 @@ * This info should also be displayed in the item-info. * -) can can (if possible) change the 'amount' to a vlalue that _can_ be produced (i.e. the maximum amount possible).*/ } - } else { + } else if (prod->aircraft) { const aircraft_t *aircraftTemplate = prod->aircraft; assert(aircraftTemplate); assert(aircraftTemplate == aircraftTemplate->tpl); @@ -846,6 +821,14 @@ /* Now we select the item we just created. */ PR_ClearSelected(); selectedProduction = &queue->items[queue->numItems - 1]; + } else { /* Disassembly */ + storedUFO_t *ufo = prod->ufo; + + Com_sprintf(cp_messageBuffer, sizeof(cp_messageBuffer), _("Disassembling of %s started"), _(UFO_TypeToName(ufo->ufoTemplate->ufotype))); + MSO_CheckAddNewMessage(NT_PRODUCTION_STARTED, _("Production started"), cp_messageBuffer, qfalse, MSG_PRODUCTION, ufo->ufoTemplate->tech); + + PR_ClearSelected(); + selectedProduction = &queue->items[queue->numItems - 1]; } } @@ -860,20 +843,22 @@ { production_queue_t *queue; base_t *base = B_GetCurrentSelectedBase(); + int prodIDX; if (!base || !selectedProduction) return; + prodIDX = selectedProduction->idx; queue = &ccs.productions[base->idx]; - PR_QueueDelete(base, queue, selectedProduction->idx); + PR_QueueDelete(base, queue, prodIDX); if (queue->numItems == 0) { selectedProduction = NULL; MN_ExecuteConfunc("prod_selectline -1"); - } else if (selectedProduction->idx >= queue->numItems) { + } else if (prodIDX >= queue->numItems) { selectedProduction = &queue->items[queue->numItems - 1]; - MN_ExecuteConfunc("prod_selectline %i", selectedProduction->idx); + MN_ExecuteConfunc("prod_selectline %i", prodIDX); } PR_ProductionInfo(base); @@ -904,9 +889,6 @@ amountTemp = prod->amount; prod->amount -= amountTemp; - /* We need to readd items being disassembled to base storage. */ - if (!prod->production) - base->storage.num[prod->item->idx] += amountTemp; if (prod->amount <= 0) { PR_ProductionStop_f(); Index: src/client/campaign/cp_produce.c =================================================================== --- src/client/campaign/cp_produce.c (revision 25739) +++ src/client/campaign/cp_produce.c (working copy) @@ -5,7 +5,7 @@ */ /* -Copyright (C) 2002-2007 UFO: Alien Invasion team. +Copyright (C) 2002-2009 UFO: Alien Invasion team. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License @@ -31,6 +31,7 @@ #include "../menu/m_nodes.h" #include "../mxml/mxml_ufoai.h" #include "cp_campaign.h" +#include "cp_ufo.h" #include "cp_produce_callbacks.h" /** @brief Used in production costs (to allow reducing prices below 1x). */ @@ -56,7 +57,7 @@ * @sa PR_DisassemblyInfo * @return 0 if the production does not make any progress, 1 if the whole item is built in 1 hour */ -float PR_CalculateProductionPercentDone (const base_t *base, const technology_t *tech, const components_t *comp, qboolean disassembly) +float PR_CalculateProductionPercentDone (const base_t *base, const technology_t *tech, const components_t *comp) { signed int allworkers = 0, maxworkers = 0; signed int timeDefault = 0; @@ -72,11 +73,10 @@ maxworkers = allworkers; } - if (!disassembly) { + if (!comp) { assert(tech); timeDefault = tech->produceTime; /* This is the default production time for 10 workers. */ } else { - assert(comp); timeDefault = comp->time; /* This is the default disassembly time for 10 workers. */ } @@ -148,7 +148,6 @@ */ void PR_QueueDelete (base_t *base, production_queue_t *queue, int index) { - int i; const objDef_t *od; production_t *prod; @@ -156,7 +155,9 @@ assert(base); - if (prod->itemsCached && !prod->aircraft) { + if (prod->ufo) { + prod->ufo->disassembly = NULL; + } else if (prod->itemsCached && !prod->aircraft) { /* Get technology of the item in the selected queue-entry. */ od = prod->item; if (od->tech) { @@ -168,20 +169,7 @@ prod->itemsCached = qfalse; } - /* Read disassembly to base storage. */ - if (!prod->production) - base->storage.num[prod->item->idx] += prod->amount; - - queue->numItems--; - if (queue->numItems < 0) - queue->numItems = 0; - - /* Copy up other items. */ - for (i = index; i < queue->numItems; i++) { - queue->items[i] = queue->items[i + 1]; - /* Fix index in list */ - queue->items[i].idx = i; - } + REMOVE_ELEM_ADJUST_IDX(queue->items, index, queue->numItems); } /** @@ -205,17 +193,23 @@ for (i = index; i < newIndex; i++) { queue->items[i] = queue->items[i + 1]; queue->items[i].idx = i; + if (queue->items[i].ufo) + queue->items[i].ufo->disassembly = &(queue->items[i]); } /* copy down */ for (i = index; i > newIndex; i--) { queue->items[i] = queue->items[i - 1]; queue->items[i].idx = i; + if (queue->items[i].ufo) + queue->items[i].ufo->disassembly = &(queue->items[i]); } /* insert item */ queue->items[newIndex] = saved; queue->items[newIndex].idx = newIndex; + if (queue->items[newIndex].ufo) + queue->items[newIndex].ufo->disassembly = &(queue->items[newIndex]); } /** @@ -310,6 +304,7 @@ int i; const objDef_t *od; const aircraft_t *aircraft; + storedUFO_t *ufo; production_t *prod; /* Loop through all founded bases. Then check productions @@ -333,14 +328,20 @@ if (prod->item) { od = prod->item; aircraft = NULL; - } else { + ufo = NULL; + } else if (prod->aircraft) { assert(prod->aircraft); od = NULL; aircraft = prod->aircraft; + ufo = NULL; + } else { + od = NULL; + aircraft = NULL; + ufo = prod->ufo; } if (prod->production) { /* This is production, not disassembling. */ - if (!prod->aircraft) { + if (od) { /* Not enough money to produce more items in this base. */ if (od->price * PRODUCE_FACTOR / PRODUCE_DIVISOR > ccs.credits) { if (!prod->creditMessage) { @@ -384,7 +385,7 @@ } } } else { /* This is disassembling. */ - if (base->capacities[CAP_ITEMS].max - base->capacities[CAP_ITEMS].cur < PR_DisassembleItem(NULL, CL_GetComponentsByItem(prod->item), qtrue)) { + if (base->capacities[CAP_ITEMS].max - base->capacities[CAP_ITEMS].cur < PR_DisassembleItem(NULL, ufo->comp, qtrue)) { if (!prod->spaceMessage) { Com_sprintf(cp_messageBuffer, sizeof(cp_messageBuffer), _("Not enough free storage space in base %s. Disassembling postponed.\n"), base->name); MSO_CheckAddNewMessage(NT_PRODUCTION_FAILED, _("Notice"), cp_messageBuffer, qfalse, MSG_STANDARD, NULL); @@ -395,16 +396,13 @@ } } - if (!prod->aircraft && !od->tech) - Com_Error(ERR_FATAL, "PR_ProductionRun: No tech pointer for object %s ('%s')", prod->item->id, od->id); - if (prod->production) { /* This is production, not disassembling. */ if (!prod->aircraft) - prod->percentDone += (PR_CalculateProductionPercentDone(base, od->tech, NULL, qfalse) / MINUTES_PER_HOUR); + prod->percentDone += (PR_CalculateProductionPercentDone(base, od->tech, NULL) / MINUTES_PER_HOUR); else - prod->percentDone += (PR_CalculateProductionPercentDone(base, aircraft->tech, NULL, qfalse) / MINUTES_PER_HOUR); + prod->percentDone += (PR_CalculateProductionPercentDone(base, aircraft->tech, NULL) / MINUTES_PER_HOUR); } else /* This is disassembling. */ - prod->percentDone += (PR_CalculateProductionPercentDone(base, od->tech, CL_GetComponentsByItem(prod->item), qtrue) / MINUTES_PER_HOUR); + prod->percentDone += (PR_CalculateProductionPercentDone(base, ufo->ufoTemplate->tech, ufo->comp) / MINUTES_PER_HOUR); if (prod->percentDone >= 1.0f) { if (prod->production) { /* This is production, not disassembling. */ @@ -435,28 +433,14 @@ } } } else { /* This is disassembling. */ - base->capacities[CAP_ITEMS].cur += PR_DisassembleItem(base, CL_GetComponentsByItem(prod->item), qfalse); + base->capacities[CAP_ITEMS].cur += PR_DisassembleItem(base, ufo->comp, qfalse); prod->percentDone = 0.0f; prod->amount--; - /* If this is aircraft dummy item, update UFO hangars capacity. */ - if (od->tech->type == RS_CRAFT) { - const aircraft_t *ufocraft = AIR_GetAircraft(od->id); - assert(ufocraft); - if (ufocraft->size == AIRCRAFT_LARGE) { - /* Large UFOs can only be stored in Large UFO Hangar */ - if (base->capacities[CAP_UFOHANGARS_LARGE].cur > 0) - base->capacities[CAP_UFOHANGARS_LARGE].cur--; - } else { - /* Small UFOs can only be stored in Small UFO Hangar */ - if (base->capacities[CAP_UFOHANGARS_SMALL].cur > 0) - base->capacities[CAP_UFOHANGARS_SMALL].cur--; - } - } - if (prod->amount <= 0) { - Com_sprintf(cp_messageBuffer, sizeof(cp_messageBuffer), _("The disassembling of %s has finished."), _(od->name)); - MSO_CheckAddNewMessage(NT_PRODUCTION_FINISHED, _("Production finished"), cp_messageBuffer, qfalse, MSG_PRODUCTION, od->tech); - PR_QueueNext(base); - } + + Com_sprintf(cp_messageBuffer, sizeof(cp_messageBuffer), _("The disassembling of %s has finished."), _(UFO_TypeToName(ufo->ufoTemplate->ufotype))); + MSO_CheckAddNewMessage(NT_PRODUCTION_FINISHED, _("Production finished"), cp_messageBuffer, qfalse, MSG_PRODUCTION, ufo->ufoTemplate->tech); + + US_RemoveStoredUFO(ufo); } } } @@ -517,6 +501,23 @@ } /** + * @brief Returns the base pointer the production belongs to + * @param[in] production pointer to the production entry + * @returns base_t pointer to the base + */ +base_t *PR_ProductionBase (production_t *production) { + int i; + for (i = 0; i < ccs.numBases; i++) { + base_t *base = B_GetBaseByIDX(i); + const ptrdiff_t diff = ((ptrdiff_t)((production) - ccs.productions[i].items)); + + if (diff >= 0 && diff < MAX_PRODUCTIONS) + return base; + } + return NULL; +} + +/** * @brief Save callback for savegames in XML Format * @sa PR_LoadXML * @sa SAV_GameSaveXML @@ -528,21 +529,25 @@ for (i = 0; i < MAX_BASES; i++) { const production_queue_t *pq = &ccs.productions[i]; int j; - mxml_node_t * snode = mxml_AddNode(node, "queue"); + mxml_node_t *snode = mxml_AddNode(node, "queue"); + mxml_AddInt(snode, "numitems", pq->numItems); + for (j = 0; j < pq->numItems; j++) { - /** @todo This will crash */ const objDef_t *item = pq->items[j].item; const aircraft_t *aircraft = pq->items[j].aircraft; + const storedUFO_t *ufo = pq->items[j].ufo; + mxml_node_t * ssnode = mxml_AddNode(snode, "item"); - assert(item || aircraft); + assert(item || aircraft || ufo); if (item) mxml_AddString(ssnode, "itemid", item->id); + else if (aircraft) + mxml_AddString(ssnode, "aircraftid", aircraft->id); + else if (ufo) + mxml_AddInt(ssnode, "ufoidx", ufo->idx); mxml_AddInt(ssnode, "amount", pq->items[j].amount); mxml_AddFloat(ssnode, "percentdone", pq->items[j].percentDone); - mxml_AddBool(ssnode, "prod", pq->items[j].production); - if (aircraft) - mxml_AddString(ssnode, "aircraftid", aircraft->id); mxml_AddBool(ssnode, "items_cached", pq->items[j].itemsCached); } } @@ -566,23 +571,84 @@ int j; mxml_node_t *ssnode; production_queue_t *pq = &ccs.productions[i]; + pq->numItems = mxml_GetInt(snode, "numitems", 0); + + if (pq->numItems > MAX_PRODUCTIONS) { + Com_Printf("PR_Load: Too much productions (%i), last %i dropped (baseidx=%i).\n", pq->numItems, pq->numItems - MAX_PRODUCTIONS, i); + pq->numItems = MAX_PRODUCTIONS; + } + for (j = 0, ssnode = mxml_GetNode(snode, "item"); j < pq->numItems && ssnode; j++, ssnode = mxml_GetNextNode(ssnode, snode, "item")) { const char *s1 = mxml_GetString(ssnode, "itemid"); const char *s2; + int ufoIDX; + + pq->items[j].idx = j; + pq->items[j].amount = mxml_GetInt(ssnode, "amount", 0); + pq->items[j].percentDone = mxml_GetFloat(ssnode, "percentdone", 0.0); + if (s1 && s1[0] != '\0') pq->items[j].item = INVSH_GetItemByID(s1); - pq->items[j].idx = j; - pq->items[j].amount = mxml_GetInt(ssnode, "amount", 0); + + /* This if block is for keeping compatibility with base-stored ufos */ + /* @todo remove this on release (or after time) */ + if (!mxml_GetBool(ssnode, "prod", qtrue)) { + const int amount = pq->items[j].amount; + int k; + aircraft_t *ufoTemplate = (pq->items[j].item) ? AIR_GetAircraft(pq->items[j].item->id) : NULL; + + for (k = 0; k < amount; k++) { + installation_t *installation = INS_GetFirstUFOYard(qtrue); + storedUFO_t *ufo = NULL; + + if (ufoTemplate && installation) + ufo = US_StoreUFO(ufoTemplate, installation, ccs.date); + + if (ufo) { + pq->items[j + k].idx = j + k; + pq->items[j + k].item = NULL; + pq->items[j + k].ufo = ufo; + pq->items[j + k].amount = 1; + pq->items[j + k].percentDone = (k) ? 0.0 : pq->items[j + k].percentDone; + pq->items[j + k].production = qfalse; + ufo->disassembly = &(pq->items[j + k]); + } else { + Com_Printf("PR_Load: Could not add ufo to the UFO Yards, disassembly dropped (baseidx=%i, production idx=%i).\n", i, j); + j--; + pq->numItems--; + continue; + } + } + j += k - 1; + pq->numItems += k - 1; + continue; + } + if (pq->items[j].amount <= 0) { Com_Printf("PR_Load: Production with amount <= 0 dropped (baseidx=%i, production idx=%i).\n", i, j); j--; pq->numItems--; continue; } - pq->items[j].percentDone = mxml_GetFloat(ssnode, "percentdone", 0.0); - pq->items[j].production = mxml_GetBool(ssnode, "prod", qfalse); + + ufoIDX = mxml_GetInt(ssnode, "ufoidx", MAX_STOREDUFOS); + if (ufoIDX != MAX_STOREDUFOS) { + storedUFO_t *ufo = US_GetStoredUFOByIDX(ufoIDX); + + if (!ufo) { + Com_Printf("PR_Load: Could not find ufo idx: %i\n", ufoIDX); + return qfalse; + } + + pq->items[j].ufo = ufo; + pq->items[j].production = qfalse; + pq->items[j].ufo->disassembly = &(pq->items[j]); + } else { + pq->items[j].production = qtrue; + } + s2 = mxml_GetString(ssnode, "aircraftid"); if (s2 && s2[0] != '\0') pq->items[j].aircraft = AIR_GetAircraft(s2); Index: src/client/campaign/cp_save.h =================================================================== --- src/client/campaign/cp_save.h (revision 25739) +++ src/client/campaign/cp_save.h (working copy) @@ -4,7 +4,7 @@ */ /* -Copyright (C) 2002-2007 UFO: Alien Invasion team. +Copyright (C) 2002-2009 UFO: Alien Invasion team. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License @@ -87,6 +87,8 @@ qboolean INS_LoadXML(mxml_node_t *parent); qboolean MSO_SaveXML(mxml_node_t *parent); qboolean MSO_LoadXML(mxml_node_t *parent); +qboolean US_SaveXML(mxml_node_t *parent); +qboolean US_LoadXML(mxml_node_t *parent); void B_PostLoadInit(void); Index: src/client/campaign/cp_missions.c =================================================================== --- src/client/campaign/cp_missions.c (revision 25739) +++ src/client/campaign/cp_missions.c (working copy) @@ -342,7 +342,7 @@ shortUFOType = Com_UFOTypeToShortName(ccs.selectedMission->ufo->ufotype); } - Com_sprintf(mission->onwin, sizeof(mission->onwin), "cp_uforecovery_init %i;", mission->ufo->ufotype); + Com_sprintf(mission->onwin, sizeof(mission->onwin), "cp_uforecovery_init %s", mission->ufo->id); /* Set random map UFO if this is a random map */ if (mission->mapDef->map[0] == '+') { /* set rm_ufo to the ufo type used */ Index: src/client/campaign/cp_produce_callbacks.h =================================================================== --- src/client/campaign/cp_produce_callbacks.h (revision 25739) +++ src/client/campaign/cp_produce_callbacks.h (working copy) @@ -4,7 +4,7 @@ */ /* - Copyright (C) 1997-2008 UFO:AI Team + Copyright (C) 1997-2009 UFO:AI Team This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License @@ -23,10 +23,10 @@ */ -#ifndef CLIENT_CL_PRODUCE_CALLBACKS_H -#define CLIENT_CL_PRODUCE_CALLBACKS_H +#ifndef CLIENT_CP_PRODUCE_CALLBACKS_H +#define CLIENT_CP_PRODUCE_CALLBACKS_H void PR_InitCallbacks (void); void PR_ShutdownCallbacks (void); -#endif /* CLIENT_CL_PRODUCE_CALLBACKS_H */ +#endif /* CLIENT_CP_PRODUCE_CALLBACKS_H */ Index: src/client/campaign/cp_produce.h =================================================================== --- src/client/campaign/cp_produce.h (revision 25739) +++ src/client/campaign/cp_produce.h (working copy) @@ -4,7 +4,7 @@ */ /* -Copyright (C) 2002-2007 UFO: Alien Invasion team. +Copyright (C) 2002-2009 UFO: Alien Invasion team. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License @@ -23,8 +23,8 @@ */ -#ifndef CLIENT_CL_PROOUCE -#define CLIENT_CL_PROOUCE +#ifndef CLIENT_CP_PRODUCE +#define CLIENT_CP_PRODUCE #include "../cl_inventory.h" @@ -50,7 +50,8 @@ * @todo Make this a union */ objDef_t *item; /**< Item to be produced. */ - struct aircraft_s *aircraft; /**< Aircraft (sample) to be produced. @todo Is there any way to make this const without cl_produce.c to break?*/ + struct aircraft_s *aircraft; /**< Aircraft (sample) to be produced. @todo Is there any way to make this const without cp_produce.c to break?*/ + struct storedUFO_s *ufo; signed int amount; /**< How much are we producing. */ float percentDone; /**< Fraction of the item which is already produced. @@ -80,6 +81,7 @@ void PR_QueueMove (production_queue_t *queue, int index, int dir); void PR_QueueDelete (base_t *base, production_queue_t *queue, int index); void PR_UpdateRequiredItemsInBasestorage (base_t *base, int amount, requirements_t *reqs); -float PR_CalculateProductionPercentDone (const base_t *base, const technology_t *tech, const components_t *comp, qboolean disassembly); +float PR_CalculateProductionPercentDone (const base_t *base, const technology_t *tech, const components_t *comp); +base_t *PR_ProductionBase(production_t *production); -#endif /* CLIENT_CL_PROOUCE */ +#endif /* CLIENT_CP_PRODUCE */ Index: src/client/campaign/cp_uforecovery_callbacks.c =================================================================== --- src/client/campaign/cp_uforecovery_callbacks.c (revision 25739) +++ src/client/campaign/cp_uforecovery_callbacks.c (working copy) @@ -1,11 +1,12 @@ /** * @file cp_uforecovery_callbacks.c - * @brief UFO recovery callback functions - * @note Functions with UR_* + * @brief UFO recovery and storing callback functions + * @note UFO recovery functions with UR_* + * @note UFO storing functions with US_* */ /* -Copyright (C) 2002-2007 UFO: Alien Invasion team. +Copyright (C) 2002-2009 UFO: Alien Invasion team. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License @@ -33,24 +34,18 @@ #include "../menu/m_main.h" #include "../menu/node/m_node_text.h" -typedef struct ufoRecoveryStorage_s { - base_t *base; /**< base where actual ufo is located */ - aircraft_t *ufoTemplate; /**< template of ufo for storage id and name */ - objDef_t *ufoItem; /**< object definition of aircraft, used for capacity check */ -} ufoRecoveryStorage_t; +#ifdef DEBUG +#include "cp_time.h" +#endif /** @sa ufoRecoveries_t */ typedef struct ufoRecovery_s { - base_t *base; /**< selected base for current selected ufo recovery */ - ufoType_t ufoType; /**< the ufo type of the current ufo recovery */ + installation_t *installation; /**< selected ufoyard for current selected ufo recovery */ + aircraft_t *ufoTemplate; /**< the ufo type of the current ufo recovery */ nation_t *nation; /**< selected nation to sell to for current ufo recovery */ qboolean recoveryDone; /**< recoveryDone? Then the buttons are disabled */ - base_t *UFObases[MAX_BASES]; /**< Array of base indexes where we can store UFO. */ - int baseHasUFOHangarCount; /**< number of entries in the UFObases array */ + int UFOprices[MAX_NATIONS]; /**< Array of prices proposed by nation. */ - ufoRecoveryStorage_t replaceUFOs[MAX_BASES * UFO_MAX]; /**< Array of replaceable ufos in all bases */ - ufoRecoveryStorage_t *selectedStorage; /**< selected storage for replace */ - int replaceCount; /**< number of replaceable ufos in all bases */ } ufoRecovery_t; static ufoRecovery_t ufoRecovery; @@ -64,365 +59,132 @@ } /** - * @brief Checks conditions for storing given ufo in given base. - * @param[in] base Pointer to the base, where we are going to store UFO. - * @param[in] ufocraft Pointer to ufocraft which we are going to store. - * @return qtrue if given base can store given ufo. - */ -static int UR_GetBaseCapacityForUfotype (const base_t *base, const aircraft_t *ufocraft) -{ - assert(base && ufocraft); - - if (ufocraft->size == AIRCRAFT_LARGE) { - /* Large UFOs can only fit large UFO hangars */ - if (!B_GetBuildingStatus(base, B_UFO_HANGAR)) - return -1; - - /* Check there is still enough room for this UFO */ - return base->capacities[CAP_UFOHANGARS_LARGE].max; - } else { - /* This is a small UFO, can only fit in small hangar */ - - /* There's no small hangar functional */ - if (!B_GetBuildingStatus(base, B_UFO_SMALL_HANGAR)) - return -1; - - /* Check there is still enough room for this UFO */ - return base->capacities[CAP_UFOHANGARS_SMALL].max; - } -} - - -/** - * @brief Select appropriate the base or replace option from list - * @param num selected index must be within valid range - */ -static void UR_DialogSelectStorageBase (const int num) -{ - qboolean useBase = qtrue; - if (num < ufoRecovery.baseHasUFOHangarCount) { - if (!ufoRecovery.UFObases[num]) - return; - } else { - useBase = qfalse; - if (!ufoRecovery.replaceUFOs[num - ufoRecovery.baseHasUFOHangarCount].base) - return; - } - - if (useBase) { - ufoRecovery.base = ufoRecovery.UFObases[num]; - Com_DPrintf(DEBUG_CLIENT, "CP_UFORecoveryBaseSelectPopup_f: picked base: %s\n", ufoRecovery.base->name); - MN_ExecuteConfunc("btbasesel_enable"); - } else { - ufoRecovery.selectedStorage = &ufoRecovery.replaceUFOs[num - ufoRecovery.baseHasUFOHangarCount]; - Com_DPrintf(DEBUG_CLIENT, "CP_UFORecoveryBaseSelectPopup_f: picked replace ufo %s from base %s\n", - ufoRecovery.selectedStorage->ufoTemplate->id, ufoRecovery.selectedStorage->base->name); - ufoRecovery.base = ufoRecovery.selectedStorage->base; - MN_ExecuteConfunc("btbaserepl_enable"); - } - Cvar_Set("mission_recoverybase", ufoRecovery.base->name); -} - -/** * @brief Function to trigger UFO Recovered event. * @note This function prepares related cvars for the recovery dialog. * @note Command to call this: cp_uforecovery_init. */ static void UR_DialogInit_f (void) { - int i; - ufoType_t UFOtype; - aircraft_t *ufocraft; - qboolean ufofound = qfalse; + char ufoID[MAX_VAR]; + aircraft_t *ufoCraft = NULL; if (Cmd_Argc() < 2) { - Com_Printf("Usage: %s \n", Cmd_Argv(0)); + Com_Printf("Usage: %s \n", Cmd_Argv(0)); return; } - if ((atoi(Cmd_Argv(1)) >= 0) && (atoi(Cmd_Argv(1)) < UFO_MAX)) { - UFOtype = atoi(Cmd_Argv(1)); - } else { - Com_Printf("CP_UFORecovered: UFOType: %i does not exist!\n", atoi(Cmd_Argv(1))); - return; - } - - ufocraft = NULL; - /* Find ufo sample of given ufotype. */ - for (i = 0; i < ccs.numAircraftTemplates; i++) { - ufocraft = &ccs.aircraftTemplates[i]; - if (ufocraft->type != AIRCRAFT_UFO) - continue; - if (ufocraft->ufotype == UFOtype) { - ufofound = qtrue; - break; - } - } + Q_strncpyz(ufoID, Cmd_Argv(1), sizeof(ufoID)); + ufoCraft = AIR_GetAircraft(ufoID); /* Do nothing without UFO of this type. */ - if (!ufofound) { - Com_Printf("CP_UFORecovered: UFOType: %i does not have valid craft definition!\n", atoi(Cmd_Argv(1))); + if (!ufoCraft) { + Com_Printf("CP_UFORecovered: UFO Type: %s is not a valid craft definition!\n", ufoID); return; } /* Put relevant info into missionResults array. */ ccs.missionResults.recovery = qtrue; ccs.missionResults.crashsite = qfalse; - ccs.missionResults.ufotype = ufocraft->ufotype; + ccs.missionResults.ufotype = ufoCraft->ufotype; /* Prepare related cvars. */ Cvar_SetValue("mission_uforecovered", 1); /* This is used in menus to enable UFO Recovery nodes. */ memset(&ufoRecovery, 0, sizeof(ufoRecovery)); - ufoRecovery.ufoType = UFOtype; + ufoRecovery.ufoTemplate = ufoCraft; + Cvar_Set("mn_uforecovery_actualufo", UFO_MissionResultToString()); } /** - * @brief Function calculates prices for actual ufo to sell and initializes menu text. - * @param ufocraft the ufo type to sell - * @note called by UR_DialogInitSell_f and UR_DialogStartReplace - */ -static void UR_DialogInitSell (aircraft_t *ufocraft) -{ - int i, nations = 0; - static char recoveryNationSelectPopup[MAX_SMALLMENUTEXTLEN]; - - recoveryNationSelectPopup[0] = '\0'; - - for (i = 0; i < ccs.numNations; i++) { - const nation_t *nation = &ccs.nations[i]; - nations++; - /* Calculate price offered by nation only if this is first popup opening. */ - if (!ufoRecovery.nation) { - ufoRecovery.UFOprices[i] = (int) (ufocraft->price * (.85f + frand() * .3f)); - /* Nation will pay less if corrupted */ - ufoRecovery.UFOprices[i] = (int) (ufoRecovery.UFOprices[i] * exp(-nation->stats[0].xviInfection / 20.0f)); - } - Com_sprintf(recoveryNationSelectPopup + strlen(recoveryNationSelectPopup), sizeof(recoveryNationSelectPopup), - "%s\t\t\t%i\t\t%s\n", _(nation->name), ufoRecovery.UFOprices[i], NAT_GetHappinessString(nation)); - } - - /* Do nothing without at least one nation. */ - if (nations == 0) - return; - - MN_RegisterText(TEXT_UFORECOVERY_NATIONS, recoveryNationSelectPopup); -} - -/** * @brief Function to initialize list of storage locations for recovered UFO. * @note Command to call this: cp_uforecovery_store_init. * @sa UR_ConditionsForStoring */ static void UR_DialogInitStore_f (void) { - int i,j, replaceTemplates = 0; - aircraft_t *ufocraft; - linkedList_t *recoveryBaseSelectPopup = NULL; - qboolean ufofound = qfalse; - linkedList_t *validReplaceTemplateList = NULL, *replaceObjDefList = NULL; + int i; + int count = 0; + linkedList_t *recoveryYardSelectPopup = NULL; - /* Do nothing without any base. */ - if (!ccs.numBases) - return; - /* Do nothing if recovery process is finished. */ if (ufoRecovery.recoveryDone) return; - /* Find ufo sample of given ufotype. */ - for (i = 0; i < ccs.numAircraftTemplates; i++) { - ufocraft = &ccs.aircraftTemplates[i]; - if (ufocraft->type != AIRCRAFT_UFO) - continue; - if (ufocraft->ufotype == ufoRecovery.ufoType) { - ufofound = qtrue; - break; - } - } + /* Check how many bases can store this UFO. */ + for (i = 0; i < ccs.numInstallations; i++) { + installation_t *installation = INS_GetFoundedInstallationByIDX(i); - /* Do nothing without UFO of this type. */ - if (!ufofound) { - Com_Printf("CP_UFORecoveredStore_f: UFOType: %i does not have valid craft definition!\n", ufoRecovery.ufoType); - return; - } - - /* find valid replace templates */ - for (i = 0; i < ccs.numAircraftTemplates; i++) { - aircraft_t *tempcraft = &ccs.aircraftTemplates[i]; - if (tempcraft == ufocraft) + if (!installation) continue; - if (tempcraft->type != AIRCRAFT_UFO) - continue; - if (tempcraft->size == ufocraft->size) { - LIST_AddPointer(&validReplaceTemplateList,tempcraft); - LIST_AddPointer(&replaceObjDefList,INVSH_GetItemByID(tempcraft->id)); - replaceTemplates++; - } - } - /* Clear UFObases. */ - memset(ufoRecovery.UFObases, 0, sizeof(ufoRecovery.UFObases)); - - /* Check how many bases can store this UFO. */ - for (i = 0; i < MAX_BASES; i++) { - base_t *base = B_GetFoundedBaseByIDX(i); - if (!base) - continue; - if (UR_ConditionsForStoring(base, ufocraft)) { - LIST_AddString(&recoveryBaseSelectPopup, base->name); - ufoRecovery.UFObases[ufoRecovery.baseHasUFOHangarCount++] = base; - } else { - int baseCapacity = UR_GetBaseCapacityForUfotype(base, ufocraft); - if (baseCapacity > 0) - { - linkedList_t *curTemplate = validReplaceTemplateList; - linkedList_t *curObjID = replaceObjDefList; - for (j = 0; j < replaceTemplates; j++) { - objDef_t *obj = (objDef_t*)curObjID->data; - aircraft_t *craft = (aircraft_t *)curTemplate->data; - if (base->storage.num[obj->idx] > 0) { - /* found a valid replace, add it */ - ufoRecoveryStorage_t *store = &ufoRecovery.replaceUFOs[ufoRecovery.replaceCount]; - store->base = base; - store->ufoItem = obj; - store->ufoTemplate = craft; - ufoRecovery.replaceCount++; - } - curObjID = curObjID->next; - curTemplate = curTemplate->next; - } - } + if (installation->ufoCapacity.max > 0 + && installation->ufoCapacity.max > installation->ufoCapacity.cur) { + LIST_AddString(&recoveryYardSelectPopup, installation->name); + count++; } } - for (i = 0; i < ufoRecovery.replaceCount; i++) - { - ufoRecoveryStorage_t store = ufoRecovery.replaceUFOs[i]; - LIST_AddString(&recoveryBaseSelectPopup, va(_("Replace %s at %s (%i stored available)\n"), _(store.ufoItem->name), store.base->name, store.base->storage.num[store.ufoItem->idx] )); - } - - /* If only one base with UFO hangars, the recovery will be done in this base. */ - switch (ufoRecovery.baseHasUFOHangarCount + ufoRecovery.replaceCount) { - case 0: + Cvar_Set("mn_uforecovery_actualufo", UFO_MissionResultToString()); + if (count == 0) { /* No UFO base with proper conditions, show a hint and disable list. */ - LIST_AddString(&recoveryBaseSelectPopup, _("No ufo hangar or ufo yard available.")); - MN_ExecuteConfunc("cp_basesel_disable"); - break; - case 1: - /* there should only be one entry in UFObases - so use that one. */ - if (ufoRecovery.baseHasUFOHangarCount) - ufoRecovery.base = ufoRecovery.UFObases[0]; - else - ufoRecovery.selectedStorage = &ufoRecovery.replaceUFOs[0]; - UR_DialogSelectStorageBase(0); - /** @todo some better way to do so without the need of a confunc? */ - MN_ExecuteConfunc("cp_basesel_select"); - break; - default: - /** @todo check whether this is needed */ - if (!ufoRecovery.base) - ufoRecovery.base = ufoRecovery.UFObases[0]; - if (ufoRecovery.base) - Cvar_Set("mission_recoverybase", ufoRecovery.base->name); - break; + LIST_AddString(&recoveryYardSelectPopup, _("No free ufo yard available.")); + MN_ExecuteConfunc("uforecovery_tabselect sell"); + MN_ExecuteConfunc("btbasesel disable"); + } else { + MN_ExecuteConfunc("cp_basesel_select 0"); + MN_ExecuteConfunc("btbasesel enable"); } - Cvar_Set("mn_uforecovery_actualufo",UFO_MissionResultToString()); - MN_RegisterLinkedListText(TEXT_UFORECOVERY_BASESTORAGE, recoveryBaseSelectPopup); + MN_RegisterLinkedListText(TEXT_UFORECOVERY_BASESTORAGE, recoveryYardSelectPopup); } /** - * @brief Finds the destination base for UFO recovery. - * @note The base selection is being done here. - * @note Callback command: cp_uforecovery_baselist_click. + * @brief Function to start UFO recovery process. + * @note Command to call this: cp_uforecovery_store_start. */ -static void UR_DialogSelectStorageBase_f (void) +static void UR_DialogStartStore_f (void) { - int num; + installation_t *UFOYard = NULL; + int idx; + int i; + int count = 0; + date_t date; if (Cmd_Argc() < 2) { - Com_Printf("Usage: %s \n", Cmd_Argv(0)); + Com_Printf("Usage: %s \n", Cmd_Argv(0)); return; } - num = atoi(Cmd_Argv(1)); - if (num < 0 || num >= ufoRecovery.replaceCount + ufoRecovery.baseHasUFOHangarCount) - return; + idx = atoi(Cmd_Argv(1)); - UR_DialogSelectStorageBase(num); -} + for (i = 0; i < ccs.numInstallations; i++) { + installation_t *installation = INS_GetFoundedInstallationByIDX(i); -/** - * @brief Function starts the UFO recovery process. - * @note Called by UR_DialogStartStore_f and UR_DialogStartReplace_f - * @note uforecovery base value must be set - * @param finish flag indicating whether uforecovery should me marked as done - */ -static void UR_DialogStartStore (qboolean finish) -{ - base_t *base = ufoRecovery.base; - Com_sprintf(cp_messageBuffer, sizeof(cp_messageBuffer), _("Recovered %s from the battlefield. UFO is being transported to base %s."), UFO_TypeToName( - ufoRecovery.ufoType), base->name); - MS_AddNewMessage(_("UFO Recovery"), cp_messageBuffer, qfalse, MSG_STANDARD, NULL); - UR_Prepare(base, ufoRecovery.ufoType); + if (!installation) + continue; - if (finish) - /* UFO recovery process is done, disable buttons. */ - UR_DialogRecoveryDone(); -} + if (installation->ufoCapacity.max <= 0 + || installation->ufoCapacity.max <= installation->ufoCapacity.cur) + continue; + + if (count == idx) { + UFOYard = installation; + break; + } + count++; + } -/** - * @brief Function to start UFO recovery process. - * @note Command to call this: cp_uforecovery_store_start. - * @note This needs the ufoRecovery base value set - */ -static void UR_DialogStartStore_f (void) -{ - base_t *base = ufoRecovery.base; - if (!base) + if (!UFOYard) return; - UR_DialogStartStore(qtrue); -} + Com_sprintf(cp_messageBuffer, lengthof(cp_messageBuffer), _("Recovered %s from the battlefield. UFO is being transported to %s."), _(UFO_TypeToName(ufoRecovery.ufoTemplate->ufotype)), UFOYard->name); + MS_AddNewMessage(_("UFO Recovery"), cp_messageBuffer, qfalse, MSG_STANDARD, NULL); +/* UR_Prepare(UFOYard, ufoRecovery.ufoTemplate->ufotype); */ + date = ccs.date; + date.day += (int) RECOVERY_DELAY; +/* date.sec += (RECOVERY_DELAY - (int) RECOVERY_DELAY) % SECONDS_PER_DAY;*/ -/** - * @brief Function to start replace process - * @note Command to call this: cp_uforecovery_replace - * @note This needs the uforecovery selectedStorage value set - */ -static void UR_DialogStartReplace_f (void) -{ - linkedList_t *replacetext = NULL; - ufoRecoveryStorage_t *storage = ufoRecovery.selectedStorage; - if (!storage) - return; - /** @todo can these asserts be merged into one? */ - assert(storage->base); - assert(storage->ufoItem); - assert(storage->ufoTemplate); + US_StoreUFO(ufoRecovery.ufoTemplate, UFOYard, date); - /* update hangar cap, remove old ufo*/ - storage->base->storage.num[storage->ufoItem->idx]--; - if (storage->ufoTemplate->size == AIRCRAFT_LARGE) { - if (storage->base->capacities[CAP_UFOHANGARS_LARGE].cur > 0) - storage->base->capacities[CAP_UFOHANGARS_LARGE].cur--; - else - Com_Printf("UR_DialogStartReplace_f: Large UFO-hangar capacity is not greater than zero for replacing a UFO."); - } else { - if (storage->base->capacities[CAP_UFOHANGARS_SMALL].cur > 0) - storage->base->capacities[CAP_UFOHANGARS_SMALL].cur--; - else - Com_Printf("UR_DialogStartReplace_f: Small UFO-hangar capacity is not greater than zero for replacing a UFO."); - } - /* reinit sell values for replaced ufo */ - memset(ufoRecovery.UFOprices,-1,sizeof(ufoRecovery.UFOprices)); - ufoRecovery.nation = NULL; - UR_DialogInitSell(storage->ufoTemplate); - /* update other menu texts */ - Cvar_Set("mn_uforecovery_actualufo",va(_("previously recovered %s from base %s"),UFO_TypeToName(storage->ufoTemplate->type), storage->base->name)); - LIST_AddString(&replacetext, va(_("Storage process for captured %s ongoing."), UFO_TypeToName(ufoRecovery.ufoType))); - MN_RegisterLinkedListText(TEXT_UFORECOVERY_BASESTORAGE, replacetext); - /* start storage process for recovered ufo */ - UR_DialogStartStore(qfalse); + UR_DialogRecoveryDone(); } /** @@ -432,27 +194,39 @@ static void UR_DialogInitSell_f (void) { int i; - aircraft_t *ufocraft; + int nations = 0; + static char recoveryNationSelectPopup[MAX_SMALLMENUTEXTLEN]; /* Do nothing if recovery process is finished. */ if (ufoRecovery.recoveryDone) return; - ufocraft = NULL; - /* Find ufo sample of given ufotype. */ - for (i = 0; i < ccs.numAircraftTemplates; i++) { - ufocraft = &ccs.aircraftTemplates[i]; - if (ufocraft->type != AIRCRAFT_UFO) - continue; - if (ufocraft->ufotype == ufoRecovery.ufoType) - break; - } - if (!ufocraft) + if (!ufoRecovery.ufoTemplate) return; memset(ufoRecovery.UFOprices, -1, sizeof(ufoRecovery.UFOprices)); - UR_DialogInitSell(ufocraft); + recoveryNationSelectPopup[0] = '\0'; + + for (i = 0; i < ccs.numNations; i++) { + const nation_t *nation = &ccs.nations[i]; + nations++; + /* Calculate price offered by nation only if this is first popup opening. */ + if (!ufoRecovery.nation) { + ufoRecovery.UFOprices[i] = (int) (ufoRecovery.ufoTemplate->price * (.85f + frand() * .3f)); + /* Nation will pay less if corrupted */ + ufoRecovery.UFOprices[i] = (int) (ufoRecovery.UFOprices[i] * exp(-nation->stats[0].xviInfection / 20.0f)); + } + Com_sprintf(recoveryNationSelectPopup + strlen(recoveryNationSelectPopup), lengthof(recoveryNationSelectPopup), + "%s\t\t\t%i\t\t%s\n", _(nation->name), ufoRecovery.UFOprices[i], NAT_GetHappinessString(nation)); + } + + /* Do nothing without at least one nation. */ + if (nations == 0) + return; + + MN_RegisterText(TEXT_UFORECOVERY_NATIONS, recoveryNationSelectPopup); + MN_ExecuteConfunc("btnatsel disable"); } /** @@ -481,7 +255,7 @@ Com_DPrintf(DEBUG_CLIENT, "CP_UFORecoveryNationSelectPopup_f: picked nation: %s\n", nation->name); Cvar_Set("mission_recoverynation", _(nation->name)); - MN_ExecuteConfunc("btnatsel_enable"); + MN_ExecuteConfunc("btnatsel enable"); } /** @@ -502,13 +276,15 @@ Com_Printf("CP_UFOSellStart_f: Error: ufo price of -1 - nation: '%s'\n", nation->id); return; } +#if 0 if (ufoRecovery.selectedStorage) { Com_sprintf(cp_messageBuffer, sizeof(cp_messageBuffer), _("Sold previously recovered %s from %s to nation %s, gained %i credits."), UFO_TypeToName( ufoRecovery.selectedStorage->ufoTemplate->ufotype), ufoRecovery.selectedStorage->base->name, _(nation->name), ufoRecovery.UFOprices[nation->idx]); } else { +#endif Com_sprintf(cp_messageBuffer, sizeof(cp_messageBuffer), _("Recovered %s from the battlefield. UFO sold to nation %s, gained %i credits."), UFO_TypeToName( - ufoRecovery.ufoType), _(nation->name), ufoRecovery.UFOprices[nation->idx]); - } + ufoRecovery.ufoTemplate->ufotype), _(nation->name), ufoRecovery.UFOprices[nation->idx]); +/* }*/ MS_AddNewMessage(_("UFO Recovery"), cp_messageBuffer, qfalse, MSG_STANDARD, NULL); CL_UpdateCredits(ccs.credits + ufoRecovery.UFOprices[nation->idx]); @@ -526,16 +302,121 @@ UR_DialogRecoveryDone(); } + +/* === UFO Storing === */ + +#ifdef DEBUG +/** + * @brief Debug callback to list ufostores + */ +static void US_ListStoredUFOs_f (void) +{ + int i; + + Com_Printf("Number of stored UFOs: %i\n", ccs.numStoredUFOs); + for (i = 0; i < ccs.numStoredUFOs; i++) { + const storedUFO_t *ufo = US_GetStoredUFOByIDX(i); + const base_t *prodBase = PR_ProductionBase(ufo->disassembly); + dateLong_t date; + + Com_Printf("IDX: %i\n", ufo->idx); + Com_Printf("id: %s\n", ufo->id); + Com_Printf("stored at %s\n", (ufo->installation) ? ufo->installation->name : "NOWHERE"); + + CL_DateConvertLong(&(ufo->arrive), &date); + Com_Printf("arrived at: %i %s %02i, %02i:%02i\n", date.year, + Date_GetMonthName(date.month - 1), date.day, date.hour, date.min); + + if (ufo->ufoTemplate->tech->base) + Com_Printf("tech being researched at %s\n", ufo->ufoTemplate->tech->base->name); + if (prodBase) + Com_Printf("being disassembled at %s\n", prodBase->name); + } +} + +/** + * @brief Adds an UFO to the stores + */ +static void US_StoreUFO_f (void) +{ + char ufoId[MAX_VAR]; + int installationIDX; + installation_t *installation; + aircraft_t *ufoType = NULL; + int i; + + if (Cmd_Argc() <= 2) { + Com_Printf("Usage: %s \n", Cmd_Argv(0)); + return; + } + + Q_strncpyz(ufoId, Cmd_Argv(1), sizeof(ufoId)); + installationIDX = atoi(Cmd_Argv(2)); + + /* Get The UFO Yard */ + if (installationIDX < 0 || installationIDX >= MAX_INSTALLATIONS) { + Com_Printf("US_StoreUFO_f: Invalid Installation index.\n"); + return; + } + installation = INS_GetFoundedInstallationByIDX(installationIDX); + if (!installation) { + Com_Printf("US_StoreUFO_f: There is no Installation: idx=%i.\n", installationIDX); + return; + } + + /** Get UFO Type */ + for (i = 0; i < ccs.numAircraftTemplates; i++) { + if (strstr(ccs.aircraftTemplates[i].id, ufoId)) { + ufoType = &ccs.aircraftTemplates[i]; + break; + } + } + if (ufoType == NULL) { + Com_Printf("US_StoreUFO_f: In valid UFO Id.\n"); + return; + } + + US_StoreUFO(ufoType, installation, ccs.date); +} + +/** + * @brief Removes an UFO from the stores + */ +static void US_RemoveStoredUFO_f (void) +{ + int idx; + + if (Cmd_Argc() < 2) { + Com_Printf("Usage: %s \n", Cmd_Argv(0)); + return; + } + idx = atoi(Cmd_Argv(1)); + + if (idx < 0 || idx >= ccs.numStoredUFOs) { + Com_Printf("US_RemoveStoredUFO_f: No such ufo index.\n"); + return; + } + + US_RemoveStoredUFO(US_GetStoredUFOByIDX(idx)); +} + + +#endif + void UR_InitCallbacks (void) { Cmd_AddCommand("cp_uforecovery_init", UR_DialogInit_f, "Function to trigger UFO Recovered event"); Cmd_AddCommand("cp_uforecovery_sell_init", UR_DialogInitSell_f, "Function to initialize sell recovered UFO to desired nation."); Cmd_AddCommand("cp_uforecovery_store_init", UR_DialogInitStore_f, "Function to initialize store recovered UFO in desired base."); Cmd_AddCommand("cp_uforecovery_nationlist_click", UR_DialogSelectSellNation_f, "Callback for recovery sell to nation list."); - Cmd_AddCommand("cp_uforecovery_baselist_click", UR_DialogSelectStorageBase_f, "Callback for recovery store in base list."); Cmd_AddCommand("cp_uforecovery_store_start", UR_DialogStartStore_f, "Function to start UFO recovery processing."); Cmd_AddCommand("cp_uforecovery_sell_start", UR_DialogStartSell_f, "Function to start UFO selling processing."); - Cmd_AddCommand("cp_uforecovery_replace", UR_DialogStartReplace_f, "Function to start replace UFO in base processing. Sell is needed for replaced UFO."); + +#ifdef DEBUG + Cmd_AddCommand("debug_liststoredufos", US_ListStoredUFOs_f, "Debug function to list UFOs in Hangars."); + Cmd_AddCommand("debug_storeufo", US_StoreUFO_f, "Debug function to Add UFO to Hangars."); + Cmd_AddCommand("debug_removestoredufo", US_RemoveStoredUFO_f, "Debug function to Remove UFO from Hangars."); +#endif } void UR_ShutdownCallbacks (void) @@ -544,8 +425,11 @@ Cmd_RemoveCommand("cp_uforecovery_sell_init"); Cmd_RemoveCommand("cp_uforecovery_store_init"); Cmd_RemoveCommand("cp_uforecovery_nationlist_click"); - Cmd_RemoveCommand("cp_uforecovery_baselist_click"); Cmd_RemoveCommand("cp_uforecovery_store_start"); Cmd_RemoveCommand("cp_uforecovery_sell_start"); - Cmd_RemoveCommand("cp_uforecovery_replace"); +#ifdef DEBUG + Cmd_RemoveCommand("debug_liststoredufos"); + Cmd_RemoveCommand("debug_storeufo"); + Cmd_RemoveCommand("debug_removestoredufo"); +#endif } Index: src/client/campaign/cp_uforecovery_callbacks.h =================================================================== --- src/client/campaign/cp_uforecovery_callbacks.h (revision 25739) +++ src/client/campaign/cp_uforecovery_callbacks.h (working copy) @@ -1,10 +1,10 @@ /** * @file cp_uforecovery_callbacks.h - * @brief UFO recovery callback header file + * @brief UFO recovery and storing callback header file */ /* -Copyright (C) 2002-2007 UFO: Alien Invasion team. +Copyright (C) 2002-2009 UFO: Alien Invasion team. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License @@ -23,8 +23,8 @@ */ -#ifndef CLIENT_CL_UFORECOVERY_CALLBACKS_H -#define CLIENT_CL_UFORECOVERY_CALLBACKS_H +#ifndef CLIENT_CP_UFORECOVERY_CALLBACKS_H +#define CLIENT_CP_UFORECOVERY_CALLBACKS_H void UR_InitCallbacks(void); void UR_ShutdownCallbacks(void); Index: src/client/campaign/cp_alienbase.c =================================================================== --- src/client/campaign/cp_alienbase.c (revision 25739) +++ src/client/campaign/cp_alienbase.c (working copy) @@ -80,6 +80,8 @@ counter++; } + if (counter == maxLoopPosition) + Vector2Copy(randomPos, pos); } /** Index: src/client/campaign/cp_research.c =================================================================== --- src/client/campaign/cp_research.c (revision 25739) +++ src/client/campaign/cp_research.c (working copy) @@ -10,7 +10,7 @@ */ /* -Copyright (C) 2002-2007 UFO: Alien Invasion team. +Copyright (C) 2002-2009 UFO: Alien Invasion team. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License @@ -88,6 +88,18 @@ } /** + * @brief Stops a research (Removes scientists from it) + * @param[in] tech The technology that is being researched. + */ +void RS_StopResearch (technology_t* tech) +{ + assert(tech); + while (tech->scientists > 0) + RS_RemoveScientist(tech, NULL); + assert(tech->statusResearch == RS_PAUSED); +} + +/** * @brief Marks one tech as researchable. * @param tech The technology to be marked. * @sa RS_MarkCollected @@ -197,6 +209,11 @@ if (AL_CountAll() < req->amount) met_AND = qfalse; break; + case RS_LINK_UFO: + assert(req->link); + if (US_UFOsInStorage(req->link, NULL) < req->amount) + met_AND = qfalse; + break; default: break; } @@ -243,6 +260,11 @@ if (AL_CountAll() >= req->amount) met_OR = qtrue; break; + case RS_LINK_UFO: + assert(req->link); + if (US_UFOsInStorage(req->link, NULL) >= req->amount) + met_OR = qtrue; + break; default: break; } @@ -410,6 +432,9 @@ if (!req->link) Com_Error(ERR_DROP, "RS_AssignTechLinks: Could not get alien type (alien or alien_dead) definition for '%s'", req->id); break; + case RS_LINK_UFO: + req->link = AIR_GetAircraft(req->id); + break; default: break; } @@ -872,6 +897,8 @@ return ((teamDef_t*)req->link)->id; case RS_LINK_ALIEN_GLOBAL: return "global alien count"; + case RS_LINK_UFO: + return ((aircraft_t*)req->link)->id; default: return "unknown"; } @@ -894,6 +921,8 @@ return "alien_dead"; case RS_LINK_ALIEN_GLOBAL: return "alienglobal"; + case RS_LINK_UFO: + return "ufo"; default: return "unknown"; } @@ -1341,6 +1370,20 @@ } else { Com_Printf("RS_ParseTechnologies: \"%s\" Too many 'required' defined. Limit is %i - ignored.\n", name, MAX_TECHLINKS); } + } else if (!strcmp(token, "ufo")) { + /* Defines what ufos need to be collected for this item to be researchable. */ + if (requiredTemp->numLinks < MAX_TECHLINKS) { + /* Set requirement-type. */ + requiredTemp->links[requiredTemp->numLinks].type = RS_LINK_UFO; + /* Set requirement-name (id). */ + token = Com_Parse(text); + requiredTemp->links[requiredTemp->numLinks].id = Mem_PoolStrDup(token, cp_campaignPool, 0); + /* Set requirement-amount of item. */ + token = Com_Parse(text); + requiredTemp->links[requiredTemp->numLinks].amount = atoi(token); + Com_DPrintf(DEBUG_CLIENT, "RS_ParseTechnologies: require-ufo - %s - %i\n", requiredTemp->links[requiredTemp->numLinks].id, requiredTemp->links[requiredTemp->numLinks].amount); + requiredTemp->numLinks++; + } } else { Com_Printf("RS_ParseTechnologies: \"%s\" unknown requirement-type: \"%s\" - ignored.\n", name, token); } Index: src/client/campaign/cp_research.h =================================================================== --- src/client/campaign/cp_research.h (revision 25739) +++ src/client/campaign/cp_research.h (working copy) @@ -70,6 +70,7 @@ RS_LINK_ALIEN, RS_LINK_ALIEN_DEAD, RS_LINK_ALIEN_GLOBAL, + RS_LINK_UFO, MAX_RS_LINKTYPES } requirementType_t; @@ -84,6 +85,7 @@ * RS_LINK_ALIEN -> teamDef_t* * RS_LINK_ALIEN_DEAD -> teamDef_t* * RS_LINK_ALIEN_GLOBAL -> NULL + * RS_LINK_UFO -> aircraft_t* */ int amount; /**< How many items are needed for research ... if any. (fix) */ int collected; /**< How many items have been collected. (varies) */ @@ -219,6 +221,7 @@ void RS_MarkCollected(technology_t *tech) __attribute__((nonnull)); void RS_MarkResearchable(qboolean init, const struct base_s *base); void RS_ResearchFinish(technology_t* tech); +void RS_StopResearch(technology_t* tech); void RS_MarkOneResearchable(technology_t *tech); void RS_AssignScientist(technology_t* tech, struct base_s *base); Index: src/client/campaign/cp_base.c =================================================================== --- src/client/campaign/cp_base.c (revision 25739) +++ src/client/campaign/cp_base.c (working copy) @@ -523,10 +523,6 @@ if (B_GetBuildingStatus(base, B_GetBuildingTypeByCapacity(CAP_WORKSPACE))) PR_UpdateProductionCap(base); - if (B_GetBuildingStatus(base, B_GetBuildingTypeByCapacity(CAP_UFOHANGARS_SMALL)) || - B_GetBuildingStatus(base, B_GetBuildingTypeByCapacity(CAP_UFOHANGARS_LARGE))) - UR_UpdateUFOHangarCapForAll(base); - if (B_GetBuildingStatus(base, B_GetBuildingTypeByCapacity(CAP_ANTIMATTER))) B_UpdateAntimatterCap(base); @@ -2575,8 +2571,8 @@ case CAP_ITEMS: /**< Update items capacity in base. */ case CAP_AIRCRAFT_SMALL: /**< Update aircraft capacity in base. */ case CAP_AIRCRAFT_BIG: /**< Update aircraft capacity in base. */ - case CAP_UFOHANGARS_SMALL: /**< Base capacities for UFO hangars. */ - case CAP_UFOHANGARS_LARGE: /**< Base capacities for UFO hangars. */ + case CAP_UFOHANGARS_SMALL: /**< Base capacities for UFO hangars. */ + case CAP_UFOHANGARS_LARGE: /**< Base capacities for UFO hangars. */ case CAP_ANTIMATTER: /**< Update antimatter capacity in base. */ /* Reset given capacity in current base. */ base->capacities[cap].max = 0; @@ -2789,12 +2785,52 @@ } /** + * @brief Moves the UFOs stored in the base's storage structure to UFO Yards' storedUFOs structure + * @note this is a temporary function to keep savegame compatibility + * @todo remove this before release (or after a time) + */ +static void B_PostLoadMoveUFOsToUFOYards (void) +{ + int i; + + /* loop throught bases */ + for (i = 0; i < ccs.numBases; i++) { + int j; + base_t * base = B_GetFoundedBaseByIDX(i); + + if (!base) + continue; + + /* find ufos in storage */ + for (j = 0; j < csi.numODs; j++) { + objDef_t *od = INVSH_GetItemByIDX(j); + + if (!od) + continue; + assert(od->tech); + if (od->tech->type != RS_CRAFT) + continue; + + /* add each to an ufoyard */ + for (;0 < base->storage.num[od->idx];) { + installation_t *UFOYard = INS_GetFirstUFOYard(qtrue); + + if (UFOYard) + US_StoreUFO(AIR_GetAircraft(od->id), UFOYard, ccs.date); + base->storage.num[od->idx]--; + } + } + } +} + +/** * @brief Set the capacity stuff for all the bases after loading a savegame * @sa SAV_GameActionsAfterLoad */ void B_PostLoadInit (void) { B_PostLoadInitCapacity(); + B_PostLoadMoveUFOsToUFOYards(); } qboolean B_LoadStorageXML (mxml_node_t *parent, equipDef_t *equip) @@ -2874,6 +2910,8 @@ } else *building = *B_GetBuildingTemplate(buildingType); + if (!building) + continue; building->idx = B_GetBuildingIDX(b, building); if (building->idx != buildId) { Com_Printf("building ID doesn't match\n"); @@ -2944,6 +2982,7 @@ /** @todo can't we use something like INVSH_DestroyInventory here? */ /* clear the mess of stray loaded pointers */ memset(&b->bEquipment, 0, sizeof(b->bEquipment)); + /* reset capacities */ } ccs.numBases = B_GetFoundedBaseCount(); @@ -2963,6 +3002,7 @@ if (!strcmp(obj->id, ANTIMATTER_TECH_ID)) return qfalse; + /* @todo: remove this condition - I keep it for compatibility for now. -geever */ /* aircraft are stored in hangars */ assert(obj->tech); if (obj->tech->type == RS_CRAFT) @@ -3118,74 +3158,6 @@ } /** - * @brief Remove ufos until everything fits in ufo hangars. - * @param[in] base Pointer to the base - * @param[in] buildingType type - */ -void B_RemoveUFOsExceedingCapacity (base_t *base, const buildingType_t buildingType) -{ - const baseCapacities_t capacityType = B_GetCapacityFromBuildingType(buildingType); - int i; - int objIdx[MAX_OBJDEFS]; /**< Will contain idx of items that can be removed */ - int num; - - if (capacityType != CAP_UFOHANGARS_SMALL && capacityType != CAP_UFOHANGARS_LARGE) - return; - - if (base->capacities[capacityType].cur <= base->capacities[capacityType].max) - return; - - for (i = 0, num = 0; i < csi.numODs; i++) { - const objDef_t *obj = &csi.ods[i]; - aircraft_t *ufocraft; - - /* Don't count what isn't an aircraft */ - assert(obj->tech); - if (obj->tech->type != RS_CRAFT) - continue; - - /* look for corresponding aircraft in global array */ - ufocraft = AIR_GetAircraft(obj->id); - if (!ufocraft) { - Com_DPrintf(DEBUG_CLIENT, "B_RemoveUFOsExceedingCapacity: Did not find UFO %s\n", obj->id); - continue; - } - - if (ufocraft->size == AIRCRAFT_LARGE && capacityType != CAP_UFOHANGARS_LARGE) - continue; - if (ufocraft->size == AIRCRAFT_SMALL && capacityType != CAP_UFOHANGARS_SMALL) - continue; - - /* Don't count item that we don't have in base */ - if (!base->storage.num[i]) - continue; - - objIdx[num++] = i; - } - - while (num && base->capacities[capacityType].cur > base->capacities[capacityType].max) { - /* Select the item to remove */ - const int randNumber = rand() % num; - /* 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]; - - assert(idx >= 0); - assert(idx < MAX_OBJDEFS); - B_UpdateStorageAndCapacity(base, &csi.ods[idx], -base->storage.num[idx], qfalse, qfalse); - - REMOVE_ELEM(objIdx, randNumber, num); - UR_UpdateUFOHangarCapForAll(base); - - /* Make sure that we don't have an infinite loop */ - if (num <= 0) - break; - } - Com_DPrintf(DEBUG_CLIENT, "B_RemoveUFOsExceedingCapacity: Remains %i in storage for a maximum of %i\n", - base->capacities[capacityType].cur, base->capacities[capacityType].max); -} - -/** * @brief Update Storage Capacity. * @param[in] base Pointer to the base * @sa B_ResetAllStatusAndCapacities_f Index: base/ufos/installation.ufo =================================================================== --- base/ufos/installation.ufo (revision 25739) +++ base/ufos/installation.ufo (working copy) @@ -24,7 +24,7 @@ cost 30000 radar_range 0 max_batteries 0 - max_ufo_stored 10 + max_ufo_stored 6 max_damage 100 buildtime 11 } Index: base/ufos/research.ufo =================================================================== --- base/ufos/research.ufo (revision 25739) +++ base/ufos/research.ufo (working copy) @@ -627,15 +627,15 @@ require_OR { - item craft_ufo_ripper 1 - item craft_ufo_gunboat 1 - item craft_ufo_scout 1 - item craft_ufo_fighter 1 - item craft_ufo_supply 1 - item craft_ufo_carrier 1 - item craft_ufo_bomber 1 - item craft_ufo_corrupter 1 - item craft_ufo_harvester 1 + ufo craft_ufo_ripper 1 + ufo craft_ufo_gunboat 1 + ufo craft_ufo_scout 1 + ufo craft_ufo_fighter 1 + ufo craft_ufo_supply 1 + ufo craft_ufo_carrier 1 + ufo craft_ufo_bomber 1 + ufo craft_ufo_corrupter 1 + ufo craft_ufo_harvester 1 } time 3000 @@ -3503,7 +3503,7 @@ require_AND { - item craft_ufo_scout 1 + ufo craft_ufo_scout 1 tech rs_alien_ufo_theory } @@ -3546,7 +3546,7 @@ // FIXME require_AND { - item craft_ufo_fighter 1 + ufo craft_ufo_fighter 1 tech rs_alien_ufo_theory } @@ -3590,7 +3590,7 @@ require_AND { tech rs_alien_ufo_theory - item craft_ufo_supply 1 + ufo craft_ufo_supply 1 } provides "craft_ufo_supply" @@ -3633,7 +3633,7 @@ require_AND { tech rs_alien_ufo_theory - item craft_ufo_carrier 1 + ufo craft_ufo_carrier 1 } provides "craft_ufo_carrier" @@ -3676,7 +3676,7 @@ require_AND { tech rs_alien_ufo_theory - item craft_ufo_bomber 1 + ufo craft_ufo_bomber 1 } provides "craft_ufo_bomber" @@ -3719,7 +3719,7 @@ require_AND { tech rs_alien_ufo_theory - item craft_ufo_corrupter 1 + ufo craft_ufo_corrupter 1 } provides "craft_ufo_corrupter" @@ -3762,7 +3762,7 @@ require_AND { tech rs_alien_ufo_theory - item craft_ufo_gunboat 1 + ufo craft_ufo_gunboat 1 } provides "craft_ufo_gunboat" @@ -3805,7 +3805,7 @@ require_AND { tech rs_alien_ufo_theory - item craft_ufo_ripper 1 + ufo craft_ufo_ripper 1 } provides "craft_ufo_ripper" @@ -3848,7 +3848,7 @@ require_AND { tech rs_alien_ufo_theory - item craft_ufo_harvester 1 + ufo craft_ufo_harvester 1 } provides "craft_ufo_harvester" @@ -3891,7 +3891,7 @@ require_AND { tech rs_alien_ufo_theory - item craft_ufo_mothership 1 + ufo craft_ufo_mothership 1 } provides "craft_ufo_mothership" Index: base/ufos/basemanagement.ufo =================================================================== --- base/ufos/basemanagement.ufo (revision 25739) +++ base/ufos/basemanagement.ufo (working copy) @@ -233,6 +233,7 @@ ondestroy "building_ondestroy" capacity 1 type smallufohangar + max_count 0 } building building_large_ufo_hangar @@ -249,6 +250,7 @@ pedia rs_building_large_ufo_hangar needs building_large_ufo_hangar2 type ufohangar + max_count 0 } building building_large_ufo_hangar2 Index: base/ufos/ui/lostwon.ufo =================================================================== --- base/ufos/ui/lostwon.ufo (revision 25739) +++ base/ufos/ui/lostwon.ufo (working copy) @@ -166,17 +166,17 @@ pos "17 125" size "667 38" cvar *cvar:mn_uforecovery_tab + onChange { + cmd "uforecovery_tabselect ;" + } option store { label "_Store UFO" - value "0" + value "store" } option sell { label "_Sell UFO" - value "1" + value "sell" } - onChange { - cmd "uforecovery_tabchange;" - } } string actualufo @@ -214,7 +214,6 @@ lineheight 16 tabwidth 100 rows "8" - onClick { cmd "cp_uforecovery_baselist_click " } onViewChange { *(abstractscrollbar)store_panel.cp_ur_baselist_scroll@fullsize *(abstractscrollbar)store_panel.cp_ur_baselist_scroll@current @@ -232,18 +231,6 @@ onChange { *store_panel.cp_uforecovery_baselist@viewpos } } - string selected_base_str - { - string "Selected base:" - pos "30 186" - } - - string selected_base_val - { - string *cvar:mission_recoverybase - pos "160 186" - } - button bt_basesel { string "_Recover" @@ -253,26 +240,15 @@ size "176 32" font f_menu color "0 0.5 0 1" - onClick { cmd "cp_uforecovery_store_start;mn_pop;" } + onClick { cmd "cp_uforecovery_store_start ; mn_pop;" } } - - button bt_basereplace - { - string "_Replace UFO" - tooltip "_Replace stored UFO with actual recovered one" - image ui/button_green_small - pos "375 210" - size "176 32" - font f_menu - color "0 0.5 0 1" - onClick { cmd "start_replace;" } - } } // // sell panel // - panel sell_panel { + panel sell_panel + { { pos "20 165" size "660 255" @@ -360,66 +336,56 @@ } } - // - // function to manage tab - // - confunc uforecovery_tabchange { - *store_panel@invis true - *sell_panel@invis true - if ( *cvar:mn_uforecovery_tab == 0 ) { + /* + * function to manage tab + */ + confunc uforecovery_tabselect { + *cvar:mn_uforecovery_tab = <1> + if ( *cvar:mn_uforecovery_tab eq "store" ) { *store_panel@invis false + *sell_panel@invis true } - if ( *cvar:mn_uforecovery_tab == 1 ) { + if ( *cvar:mn_uforecovery_tab eq "sell" ) { *sell_panel@invis false + *store_panel@invis true } } - confunc btnatsel_enable { - *sell_panel.bt_natsel@disabled false + confunc btnatsel { + *cvar:tmp = <1> + if ( *cvar:tmp eq "enable" ) { + *sell_panel.bt_natsel@disabled false + } else { + *sell_panel.bt_natsel@disabled true + } + delete *cvar:tmp } - - confunc btbasesel_enable { - *store_panel.bt_basesel@disabled false + + confunc btbasesel { + *cvar:tmp = <1> + if ( *cvar:tmp eq "enable" ) { + *store_panel.bt_basesel@disabled false + } else { + *store_panel.bt_basesel@disabled true + } + delete *cvar:tmp } - confunc btbaserepl_enable { - *store_panel.bt_basereplace@disabled false - } - confunc cp_basesel_disable { - *store_panel.cp_uforecovery_baselist@disabled true - } - - // @todo perhaps a shorter way to do so? confunc cp_basesel_select { - *store_panel.cp_uforecovery_baselist@lineselected 0 + *store_panel.cp_uforecovery_baselist@lineselected <1> } - confunc start_replace { - *cvar:mn_uforecovery_tab 1 - call uforecovery_tabchange - cmd "cp_uforecovery_replace;" - *store_panel.cp_uforecovery_baselist@disabled true - *store_panel.cp_uforecovery_baselist@lineselected -1 - *store_panel.bt_basesel@disabled true - *store_panel.bt_basereplace@disabled true - *sell_panel.cp_uforecovery_nationlist@lineselected -1 - *sell_panel.bt_natsel@disabled true - *cvar:mission_recoverynation "" - } - func onInit { - *store_panel.cp_uforecovery_baselist@lineselected -1 - *store_panel.cp_uforecovery_baselist@disabled false - *store_panel.bt_basereplace@disabled true - *store_panel.bt_basesel@disabled true - *sell_panel.cp_uforecovery_nationlist@lineselected -1 - *sell_panel.bt_natsel@disabled true - *cvar:mn_uforecovery_tab 0 - cmd "del mission_recoverybase;del mission_recoverynation;uforecovery_tabchange;cp_uforecovery_sell_init;cp_uforecovery_store_init;" + cmd "uforecovery_tabselect store" + *store_panel.cp_uforecovery_baselist@lineselected = -1 + *sell_panel.cp_uforecovery_nationlist@lineselected = -1 + *cvar:mission_recoverynation = "" + cmd "cp_uforecovery_sell_init;cp_uforecovery_store_init;" } func onClose { - cmd "del mn_uforecovery_tab;del mn_uforecovery_actualufo;" + delete *cvar:mn_uforecovery_tab + delete *cvar:mn_uforecovery_actualufo } } Index: base/ufos/ui/production.ufo =================================================================== --- base/ufos/ui/production.ufo (revision 25739) +++ base/ufos/ui/production.ufo (working copy) @@ -528,10 +528,10 @@ { dataid TEXT_PRODUCTION_INFO pos "610 528" - size "346 100" + size "346 200" lineheight 20 tabwidth 260 - rows "5" + rows "10" onViewChange { *(abstractscrollbar)description_scroll@fullsize *(abstractscrollbar)description_scroll@current @@ -544,8 +544,8 @@ pos "960 528" height "100" current 0 - viewsize 5 - fullsize 5 + viewsize 10 + fullsize 10 hidewhenunused true onChange { *description@viewpos } } @@ -554,6 +554,18 @@ // init function // ================== + confunc amountsetter { + *cvar:tmp = <1> + if ( *cvar:tmp eq "disable" ) { + *prod_change@disabled = true + *prod_change_10@disabled = true + } else { + *prod_change@disabled = false + *prod_change_10@disabled = false + } + delete *cvar:tmp + } + func onInit { cmd "prod_init; prod_type *mn_itemtype;"