rendered paste bodyIndex: src/client/campaign/cp_base.h===================================================================--- src/client/campaign/cp_base.h (revision 25738)+++ 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 25738)+++ 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 25738)+++ 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_ResetMenusIndex: src/client/campaign/cp_installation.h===================================================================--- src/client/campaign/cp_installation.h (revision 25738)+++ 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 25738)+++ 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 25738)+++ 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 serverIndex: src/client/campaign/cp_uforecovery.h===================================================================--- src/client/campaign/cp_uforecovery.h (revision 25738)+++ 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);+ #endifIndex: src/client/campaign/cp_parse.h===================================================================--- src/client/campaign/cp_parse.h (revision 25738)+++ 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); #endifIndex: src/client/campaign/cp_research_callbacks.c===================================================================--- src/client/campaign/cp_research_callbacks.c (revision 25738)+++ 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_aircraft.c===================================================================--- src/client/campaign/cp_aircraft.c (revision 25738)+++ 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_base_callbacks.c===================================================================--- src/client/campaign/cp_base_callbacks.c (revision 25738)+++ 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 25738)+++ 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 25738)+++ 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 25738)+++ 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 25738)+++ 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 25738)+++ 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 25738)+++ 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 25738)+++ 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 25738)+++ 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 25738)+++ 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,14 @@ #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;- /** @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 +55,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 <UFOType>\n", Cmd_Argv(0));+ Com_Printf("Usage: %s <ufoID>\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 <baseid>\n", Cmd_Argv(0));+ Com_Printf("Usage: %s <installationIDX>\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 +190,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 +251,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 +272,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 +298,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->arrived, &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 <ufoType> <installationIdx>\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 <idx>\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 +421,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 25738)+++ 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 25738)+++ 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 25738)+++ 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 25738)+++ 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 25738)+++ 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_fIndex: base/ufos/installation.ufo===================================================================--- base/ufos/installation.ufo (revision 25738)+++ 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 25738)+++ 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 25738)+++ 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_hangar2Index: base/ufos/ui/lostwon.ufo===================================================================--- base/ufos/ui/lostwon.ufo (revision 25738)+++ 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 <cvar:mn_uforecovery_tab> ;"+ } 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 <lineselected>" } onViewChange { *(abstractscrollbar)store_panel.cp_ur_baselist_scroll@fullsize <fullsize> *(abstractscrollbar)store_panel.cp_ur_baselist_scroll@current <viewpos>@@ -232,18 +231,6 @@ onChange { *store_panel.cp_uforecovery_baselist@viewpos <current> } } - 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 <node:root.store_panel.cp_uforecovery_baselist@lineselected>; 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 25738)+++ 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 <fullsize> *(abstractscrollbar)description_scroll@current <viewpos>@@ -544,8 +544,8 @@ pos "960 528" height "100" current 0- viewsize 5- fullsize 5+ viewsize 10+ fullsize 10 hidewhenunused true onChange { *description@viewpos <current> } }@@ -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;"