All pastes #1537309 Raw Edit

Stuff

public text v1 · immutable
#1537309 ·published 2009-08-21 05:00 UTC
rendered paste body
Index: src/client/campaign/cp_base.h
===================================================================
--- src/client/campaign/cp_base.h	(revision 25743)
+++ 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 25743)
+++ 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 25743)
+++ src/client/campaign/cp_installation.c	(working copy)
@@ -151,18 +151,36 @@
  */
 void INS_DestroyInstallation (installation_t *installation)
 {
+	int i;
+
 	if (!installation)
 		return;
 	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);
 	MSO_CheckAddNewMessage(NT_INSTALLATION_DESTROY, _("Installation destroyed"), cp_messageBuffer, qfalse, MSG_CONSTRUCTION, NULL);
 
 	REMOVE_ELEM_ADJUST_IDX(ccs.installations, installation->idx, ccs.numInstallations);
+	/* Correct UFO store positions */
+	for (i = 0; i < ccs.numStoredUFOs; i++) {
+		storedUFO_t *ufo = US_GetStoredUFOByIDX(i);
+
+		if (!ufo)
+			continue;
+		if (ufo->installation >= installation)
+			ufo->installation--;
+	}
 	Cvar_Set("mn_installation_count", va("%i", ccs.numInstallations));
 }
 
@@ -306,7 +324,33 @@
 }
 #endif
 
+/**
+ * @brief returns the first installation with (free) ufostoring capacity
+ * @param[in] free On qtrue it gives the first UFO Yard with free space
+ * @return installation_t Pointer to the UFO Yard
+ **/
+installation_t *INS_GetFirstUFOYard (qboolean free)
+{
+	int i;
 
+	for (i = 0; i < ccs.numInstallations; i++) {
+		installation_t *installation = INS_GetFoundedInstallationByIDX(i);
+
+		if (!installation)
+			continue;
+
+		if (INS_GetType(installation) != INSTALLATION_UFOYARD)
+			continue;
+
+		if (free && installation->ufoCapacity.cur >= installation->ufoCapacity.max)
+			continue;
+
+		return installation;
+	}
+
+	return NULL;
+}
+
 /**
  * @brief Resets console commands.
  * @sa MN_ResetMenus
Index: src/client/campaign/cp_installation.h
===================================================================
--- src/client/campaign/cp_installation.h	(revision 25743)
+++ 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 25743)
+++ 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 25743)
+++ src/client/campaign/cp_parse.c	(working copy)
@@ -605,6 +605,24 @@
 }
 
 /**
+ * @brief Returns components definition by ID.
+ * @param[in] id assemblyId of the component definition.
+ * @return comp Pointer to components_t definition.
+ */
+components_t *CL_GetComponentsByID(const char *id)
+{
+	int i;
+
+	for (i = 0; i < ccs.numComponents; i++) {
+		components_t *comp = &ccs.components[i];
+		if (!strcmp(comp->assemblyId, id)) {
+			return comp;
+		}
+	}
+	Com_Error(ERR_DROP, "CL_GetComponentsByItem: could not find components id for: %s", id);
+}
+
+/**
  * @brief Parsing only for singleplayer
  *
  * parsed if we are no dedicated server
Index: src/client/campaign/cp_uforecovery.h
===================================================================
--- src/client/campaign/cp_uforecovery.h	(revision 25743)
+++ src/client/campaign/cp_uforecovery.h	(working copy)
@@ -1,10 +1,10 @@
 /**
  * @file cp_uforecovery.h
- * @brief UFO recovery
+ * @brief UFO recovery and storing
  */
 
 /*
-Copyright (C) 2002-2007 UFO: Alien Invasion team.
+Copyright (C) 2002-2009 UFO: Alien Invasion team.
 
 This program is free software; you can redistribute it and/or
 modify it under the terms of the GNU General Public License
@@ -33,15 +33,42 @@
  * @sa ufoRecovery_t */
 typedef struct ufoRecoveries_s {
 	qboolean active;			/**< True if the recovery is under processing. */
-	base_t *base;				/**< Base where the recovery will be processed. */
+	installation_t *installation;	/**< UFO Yard where the recovery will be processed. */
 	aircraft_t *ufoTemplate;	/**< Entry of UFO in aircraftTemplates array. */
 	date_t event;				/**< When the process will start (UFO got transported to base). */
 } ufoRecoveries_t;
 
-void UR_Prepare(base_t *base, ufoType_t ufoType);
-void UR_UpdateUFOHangarCapForAll(base_t *base);
+#define MAX_STOREDUFOS 128
+
+/* time the recovery takes in days */
+#define RECOVERY_DELAY 2.0f
+
+/** @brief Structure for stored UFOs */
+typedef struct storedUFO_s {
+	int idx;
+	char id[MAX_VAR];
+	components_t *comp;
+	aircraft_t *ufoTemplate;
+
+	/* arrival date (recovery/transfer) */
+	date_t arrive;
+
+	/* installation UFO is stored */
+	installation_t *installation;
+
+	/* link to disassembly item */
+	production_t *disassembly;
+} storedUFO_t;
+
 void UR_ProcessActive(void);
-qboolean UR_ConditionsForStoring(const base_t *base, const aircraft_t *ufocraft);
 void UR_InitStartup(void);
 
+storedUFO_t *US_StoreUFO(aircraft_t *ufoTemplate, installation_t *installation, date_t date);
+storedUFO_t *US_GetStoredUFOPlaceByIDX(const int idx);
+storedUFO_t *US_GetStoredUFOByIDX(const int idx);
+storedUFO_t *US_GetClosestStoredUFO(const aircraft_t *ufoTemplate, const base_t *base);
+void US_RemoveStoredUFO(storedUFO_t *ufo);
+int US_UFOsInStorage(const aircraft_t *ufoTemplate, const installation_t *installation);
+void US_RemoveUFOsExceedingCapacity(installation_t *installation);
+
 #endif
Index: src/client/campaign/cp_parse.h
===================================================================
--- src/client/campaign/cp_parse.h	(revision 25743)
+++ src/client/campaign/cp_parse.h	(working copy)
@@ -47,5 +47,6 @@
 void CL_ParseCampaign(const char *name, const char **text);
 void CL_ScriptSanityCheck(void);
 components_t *CL_GetComponentsByItem(const objDef_t *item);
+components_t *CL_GetComponentsByID(const char *id);
 
 #endif
Index: src/client/campaign/cp_research_callbacks.c
===================================================================
--- src/client/campaign/cp_research_callbacks.c	(revision 25743)
+++ 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 25743)
+++ 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 25743)
+++ 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 25743)
+++ 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 25743)
+++ 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 25743)
+++ 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 25743)
+++ 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 25743)
+++ 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 25743)
+++ 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 25743)
+++ 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 25743)
+++ 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 25743)
+++ src/client/campaign/cp_uforecovery_callbacks.c	(working copy)
@@ -1,11 +1,12 @@
 /**
  * @file cp_uforecovery_callbacks.c
- * @brief UFO recovery callback functions
- * @note Functions with UR_*
+ * @brief UFO recovery and storing callback functions
+ * @note UFO recovery functions with UR_*
+ * @note UFO storing functions with US_*
  */
 
 /*
-Copyright (C) 2002-2007 UFO: Alien Invasion team.
+Copyright (C) 2002-2009 UFO: Alien Invasion team.
 
 This program is free software; you can redistribute it and/or
 modify it under the terms of the GNU General Public License
@@ -33,24 +34,18 @@
 #include "../menu/m_main.h"
 #include "../menu/node/m_node_text.h"
 
-typedef struct ufoRecoveryStorage_s {
-	base_t *base; /**< base where actual ufo is located */
-	aircraft_t *ufoTemplate; /**< template of ufo for storage id and name */
-	objDef_t *ufoItem; /**< object definition of aircraft, used for capacity check */
-} ufoRecoveryStorage_t;
+#ifdef DEBUG
+#include "cp_time.h"
+#endif
 
 /** @sa ufoRecoveries_t */
 typedef struct ufoRecovery_s {
-	base_t *base;			/**< selected base for current selected ufo recovery */
-	ufoType_t ufoType;		/**< the ufo type of the current ufo recovery */
+	installation_t *installation;			/**< selected ufoyard for current selected ufo recovery */
+	aircraft_t *ufoTemplate;		/**< the ufo type of the current ufo recovery */
 	nation_t *nation;		/**< selected nation to sell to for current ufo recovery */
 	qboolean recoveryDone;	/**< recoveryDone? Then the buttons are disabled */
-	base_t *UFObases[MAX_BASES];	/**< Array of base indexes where we can store UFO. */
-	int baseHasUFOHangarCount;		/**< number of entries in the UFObases array */
+
 	int UFOprices[MAX_NATIONS];		/**< Array of prices proposed by nation. */
-	ufoRecoveryStorage_t replaceUFOs[MAX_BASES * UFO_MAX]; /**< Array of replaceable ufos in all bases */
-	ufoRecoveryStorage_t *selectedStorage; /**< selected storage for replace */
-	int replaceCount; 		/**< number of replaceable ufos in all bases */
 } ufoRecovery_t;
 
 static ufoRecovery_t ufoRecovery;
@@ -64,365 +59,132 @@
 }
 
 /**
- * @brief Checks conditions for storing given ufo in given base.
- * @param[in] base Pointer to the base, where we are going to store UFO.
- * @param[in] ufocraft Pointer to ufocraft which we are going to store.
- * @return qtrue if given base can store given ufo.
- */
-static int UR_GetBaseCapacityForUfotype (const base_t *base, const aircraft_t *ufocraft)
-{
-	assert(base && ufocraft);
-
-	if (ufocraft->size == AIRCRAFT_LARGE) {
-		/* Large UFOs can only fit large UFO hangars */
-		if (!B_GetBuildingStatus(base, B_UFO_HANGAR))
-			return -1;
-
-		/* Check there is still enough room for this UFO */
-		return base->capacities[CAP_UFOHANGARS_LARGE].max;
-	} else {
-		/* This is a small UFO, can only fit in small hangar */
-
-		/* There's no small hangar functional */
-		if (!B_GetBuildingStatus(base, B_UFO_SMALL_HANGAR))
-			return -1;
-
-		/* Check there is still enough room for this UFO */
-		return base->capacities[CAP_UFOHANGARS_SMALL].max;
-	}
-}
-
-
-/**
- * @brief Select appropriate the base or replace option from list
- * @param num selected index must be within valid range
- */
-static void UR_DialogSelectStorageBase (const int num)
-{
-	qboolean useBase = qtrue;
-	if (num < ufoRecovery.baseHasUFOHangarCount) {
-		if (!ufoRecovery.UFObases[num])
-			return;
-	} else {
-		useBase = qfalse;
-		if (!ufoRecovery.replaceUFOs[num - ufoRecovery.baseHasUFOHangarCount].base)
-			return;
-	}
-
-	if (useBase) {
-		ufoRecovery.base = ufoRecovery.UFObases[num];
-		Com_DPrintf(DEBUG_CLIENT, "CP_UFORecoveryBaseSelectPopup_f: picked base: %s\n", ufoRecovery.base->name);
-		MN_ExecuteConfunc("btbasesel_enable");
-	} else {
-		ufoRecovery.selectedStorage = &ufoRecovery.replaceUFOs[num - ufoRecovery.baseHasUFOHangarCount];
-		Com_DPrintf(DEBUG_CLIENT, "CP_UFORecoveryBaseSelectPopup_f: picked replace ufo %s from base %s\n",
-				ufoRecovery.selectedStorage->ufoTemplate->id, ufoRecovery.selectedStorage->base->name);
-		ufoRecovery.base = ufoRecovery.selectedStorage->base;
-		MN_ExecuteConfunc("btbaserepl_enable");
-	}
-	Cvar_Set("mission_recoverybase", ufoRecovery.base->name);
-}
-
-/**
  * @brief Function to trigger UFO Recovered event.
  * @note This function prepares related cvars for the recovery dialog.
  * @note Command to call this: cp_uforecovery_init.
  */
 static void UR_DialogInit_f (void)
 {
-	int i;
-	ufoType_t UFOtype;
-	aircraft_t *ufocraft;
-	qboolean ufofound = qfalse;
+	char ufoID[MAX_VAR];
+	aircraft_t *ufoCraft = NULL;
 
 	if (Cmd_Argc() < 2) {
-		Com_Printf("Usage: %s <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 +194,39 @@
 static void UR_DialogInitSell_f (void)
 {
 	int i;
-	aircraft_t *ufocraft;
+	int nations = 0;
+	static char recoveryNationSelectPopup[MAX_SMALLMENUTEXTLEN];
 
 	/* Do nothing if recovery process is finished. */
 	if (ufoRecovery.recoveryDone)
 		return;
 
-	ufocraft = NULL;
-	/* Find ufo sample of given ufotype. */
-	for (i = 0; i < ccs.numAircraftTemplates; i++) {
-		ufocraft = &ccs.aircraftTemplates[i];
-		if (ufocraft->type != AIRCRAFT_UFO)
-			continue;
-		if (ufocraft->ufotype == ufoRecovery.ufoType)
-			break;
-	}
-	if (!ufocraft)
+	if (!ufoRecovery.ufoTemplate)
 		return;
 
 	memset(ufoRecovery.UFOprices, -1, sizeof(ufoRecovery.UFOprices));
 
-	UR_DialogInitSell(ufocraft);
+	recoveryNationSelectPopup[0] = '\0';
+
+	for (i = 0; i < ccs.numNations; i++) {
+		const nation_t *nation = &ccs.nations[i];
+		nations++;
+		/* Calculate price offered by nation only if this is first popup opening. */
+		if (!ufoRecovery.nation) {
+			ufoRecovery.UFOprices[i] = (int) (ufoRecovery.ufoTemplate->price * (.85f + frand() * .3f));
+			/* Nation will pay less if corrupted */
+			ufoRecovery.UFOprices[i] = (int) (ufoRecovery.UFOprices[i] * exp(-nation->stats[0].xviInfection / 20.0f));
+		}
+		Com_sprintf(recoveryNationSelectPopup + strlen(recoveryNationSelectPopup), lengthof(recoveryNationSelectPopup),
+				"%s\t\t\t%i\t\t%s\n", _(nation->name), ufoRecovery.UFOprices[i], NAT_GetHappinessString(nation));
+	}
+
+	/* Do nothing without at least one nation. */
+	if (nations == 0)
+		return;
+
+	MN_RegisterText(TEXT_UFORECOVERY_NATIONS, recoveryNationSelectPopup);
+	MN_ExecuteConfunc("btnatsel disable");
 }
 
 /**
@@ -481,7 +255,7 @@
 	Com_DPrintf(DEBUG_CLIENT, "CP_UFORecoveryNationSelectPopup_f: picked nation: %s\n", nation->name);
 
 	Cvar_Set("mission_recoverynation", _(nation->name));
-	MN_ExecuteConfunc("btnatsel_enable");
+	MN_ExecuteConfunc("btnatsel enable");
 }
 
 /**
@@ -502,13 +276,15 @@
 		Com_Printf("CP_UFOSellStart_f: Error: ufo price of -1 - nation: '%s'\n", nation->id);
 		return;
 	}
+#if 0
 	if (ufoRecovery.selectedStorage) {
 		Com_sprintf(cp_messageBuffer, sizeof(cp_messageBuffer), _("Sold previously recovered %s from %s to nation %s, gained %i credits."), UFO_TypeToName(
 				ufoRecovery.selectedStorage->ufoTemplate->ufotype), ufoRecovery.selectedStorage->base->name, _(nation->name), ufoRecovery.UFOprices[nation->idx]);
 	} else {
+#endif
 		Com_sprintf(cp_messageBuffer, sizeof(cp_messageBuffer), _("Recovered %s from the battlefield. UFO sold to nation %s, gained %i credits."), UFO_TypeToName(
-				ufoRecovery.ufoType), _(nation->name), ufoRecovery.UFOprices[nation->idx]);
-	}
+				ufoRecovery.ufoTemplate->ufotype), _(nation->name), ufoRecovery.UFOprices[nation->idx]);
+/*	}*/
 	MS_AddNewMessage(_("UFO Recovery"), cp_messageBuffer, qfalse, MSG_STANDARD, NULL);
 	CL_UpdateCredits(ccs.credits + ufoRecovery.UFOprices[nation->idx]);
 
@@ -526,16 +302,121 @@
 	UR_DialogRecoveryDone();
 }
 
+
+/* === UFO Storing === */
+
+#ifdef DEBUG
+/**
+ * @brief Debug callback to list ufostores
+ */
+static void US_ListStoredUFOs_f (void)
+{
+	int i;
+
+	Com_Printf("Number of stored UFOs: %i\n", ccs.numStoredUFOs);
+	for (i = 0; i < ccs.numStoredUFOs; i++) {
+		const storedUFO_t *ufo = US_GetStoredUFOByIDX(i);
+		const base_t *prodBase = PR_ProductionBase(ufo->disassembly);
+		dateLong_t date;
+
+		Com_Printf("IDX: %i\n", ufo->idx);
+		Com_Printf("id: %s\n", ufo->id);
+		Com_Printf("stored at %s\n", (ufo->installation) ? ufo->installation->name : "NOWHERE");
+
+		CL_DateConvertLong(&(ufo->arrive), &date);
+		Com_Printf("arrived at: %i %s %02i, %02i:%02i\n", date.year,
+		Date_GetMonthName(date.month - 1), date.day, date.hour, date.min);
+
+		if (ufo->ufoTemplate->tech->base)
+			Com_Printf("tech being researched at %s\n", ufo->ufoTemplate->tech->base->name);
+		if (prodBase)
+			Com_Printf("being disassembled at %s\n", prodBase->name);
+	}
+}
+
+/**
+ * @brief Adds an UFO to the stores
+ */
+static void US_StoreUFO_f (void)
+{
+	char ufoId[MAX_VAR];
+	int installationIDX;
+	installation_t *installation;
+	aircraft_t *ufoType = NULL;
+	int i;
+
+	if (Cmd_Argc() <= 2) {
+		Com_Printf("Usage: %s <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 +425,11 @@
 	Cmd_RemoveCommand("cp_uforecovery_sell_init");
 	Cmd_RemoveCommand("cp_uforecovery_store_init");
 	Cmd_RemoveCommand("cp_uforecovery_nationlist_click");
-	Cmd_RemoveCommand("cp_uforecovery_baselist_click");
 	Cmd_RemoveCommand("cp_uforecovery_store_start");
 	Cmd_RemoveCommand("cp_uforecovery_sell_start");
-	Cmd_RemoveCommand("cp_uforecovery_replace");
+#ifdef DEBUG
+	Cmd_RemoveCommand("debug_liststoredufos");
+	Cmd_RemoveCommand("debug_storeufo");
+	Cmd_RemoveCommand("debug_removestoredufo");
+#endif
 }
Index: src/client/campaign/cp_uforecovery_callbacks.h
===================================================================
--- src/client/campaign/cp_uforecovery_callbacks.h	(revision 25743)
+++ 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 25743)
+++ 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 25743)
+++ 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 25743)
+++ 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 25743)
+++ src/client/campaign/cp_base.c	(working copy)
@@ -523,10 +523,6 @@
 	if (B_GetBuildingStatus(base, B_GetBuildingTypeByCapacity(CAP_WORKSPACE)))
 		PR_UpdateProductionCap(base);
 
-	if (B_GetBuildingStatus(base, B_GetBuildingTypeByCapacity(CAP_UFOHANGARS_SMALL)) ||
-		B_GetBuildingStatus(base, B_GetBuildingTypeByCapacity(CAP_UFOHANGARS_LARGE)))
-		UR_UpdateUFOHangarCapForAll(base);
-
 	if (B_GetBuildingStatus(base, B_GetBuildingTypeByCapacity(CAP_ANTIMATTER)))
 		B_UpdateAntimatterCap(base);
 
@@ -2575,8 +2571,8 @@
 	case CAP_ITEMS:			/**< Update items capacity in base. */
 	case CAP_AIRCRAFT_SMALL:	/**< Update aircraft capacity in base. */
 	case CAP_AIRCRAFT_BIG:		/**< Update aircraft capacity in base. */
-	case CAP_UFOHANGARS_SMALL:	/**< Base capacities for UFO hangars. */
-	case CAP_UFOHANGARS_LARGE:	/**< Base capacities for UFO hangars. */
+	case CAP_UFOHANGARS_SMALL:      /**< Base capacities for UFO hangars. */
+	case CAP_UFOHANGARS_LARGE:      /**< Base capacities for UFO hangars. */
 	case CAP_ANTIMATTER:		/**< Update antimatter capacity in base. */
 		/* Reset given capacity in current base. */
 		base->capacities[cap].max = 0;
@@ -2789,12 +2785,52 @@
 }
 
 /**
+ * @brief Moves the UFOs stored in the base's storage structure to UFO Yards' storedUFOs structure
+ * @note this is a temporary function to keep savegame compatibility
+ * @todo remove this before release (or after a time)
+ */
+static void B_PostLoadMoveUFOsToUFOYards (void)
+{
+	int i;
+
+	/* loop throught bases */
+	for (i = 0; i < ccs.numBases; i++) {
+		int j;
+		base_t * base = B_GetFoundedBaseByIDX(i);
+
+		if (!base)
+			continue;
+
+		/* find ufos in storage */
+		for (j = 0; j < csi.numODs; j++) {
+			objDef_t *od = INVSH_GetItemByIDX(j);
+
+			if (!od)
+				continue;
+			assert(od->tech);
+			if (od->tech->type != RS_CRAFT)
+				continue;
+
+			/* add each to an ufoyard */
+			for (;0 < base->storage.num[od->idx];) {
+				installation_t *UFOYard = INS_GetFirstUFOYard(qtrue);
+
+				if (UFOYard)
+					US_StoreUFO(AIR_GetAircraft(od->id), UFOYard, ccs.date);
+				base->storage.num[od->idx]--;
+			}
+		}
+	}
+}
+
+/**
  * @brief Set the capacity stuff for all the bases after loading a savegame
  * @sa SAV_GameActionsAfterLoad
  */
 void B_PostLoadInit (void)
 {
 	B_PostLoadInitCapacity();
+	B_PostLoadMoveUFOsToUFOYards();
 }
 
 qboolean B_LoadStorageXML (mxml_node_t *parent, equipDef_t *equip)
@@ -2874,6 +2910,8 @@
 			} else
 				*building = *B_GetBuildingTemplate(buildingType);
 
+			if (!building)
+				continue;
 			building->idx = B_GetBuildingIDX(b, building);
 			if (building->idx != buildId) {
 				Com_Printf("building ID doesn't match\n");
@@ -2944,6 +2982,7 @@
 		/** @todo can't we use something like INVSH_DestroyInventory here? */
 		/* clear the mess of stray loaded pointers */
 		memset(&b->bEquipment, 0, sizeof(b->bEquipment));
+		/* reset capacities */
 	}
 
 	ccs.numBases = B_GetFoundedBaseCount();
@@ -2963,6 +3002,7 @@
 	if (!strcmp(obj->id, ANTIMATTER_TECH_ID))
 		return qfalse;
 
+	/* @todo: remove this condition - I keep it for compatibility for now. -geever */
 	/* aircraft are stored in hangars */
 	assert(obj->tech);
 	if (obj->tech->type == RS_CRAFT)
@@ -3118,74 +3158,6 @@
 }
 
 /**
- * @brief Remove ufos until everything fits in ufo hangars.
- * @param[in] base Pointer to the base
- * @param[in] buildingType type
- */
-void B_RemoveUFOsExceedingCapacity (base_t *base, const buildingType_t buildingType)
-{
-	const baseCapacities_t capacityType = B_GetCapacityFromBuildingType(buildingType);
-	int i;
-	int objIdx[MAX_OBJDEFS];	/**< Will contain idx of items that can be removed */
-	int num;
-
-	if (capacityType != CAP_UFOHANGARS_SMALL && capacityType != CAP_UFOHANGARS_LARGE)
-		return;
-
-	if (base->capacities[capacityType].cur <= base->capacities[capacityType].max)
-		return;
-
-	for (i = 0, num = 0; i < csi.numODs; i++) {
-		const objDef_t *obj = &csi.ods[i];
-		aircraft_t *ufocraft;
-
-		/* Don't count what isn't an aircraft */
-		assert(obj->tech);
-		if (obj->tech->type != RS_CRAFT)
-			continue;
-
-		/* look for corresponding aircraft in global array */
-		ufocraft = AIR_GetAircraft(obj->id);
-		if (!ufocraft) {
-			Com_DPrintf(DEBUG_CLIENT, "B_RemoveUFOsExceedingCapacity: Did not find UFO %s\n", obj->id);
-			continue;
-		}
-
-		if (ufocraft->size == AIRCRAFT_LARGE && capacityType != CAP_UFOHANGARS_LARGE)
-			continue;
-		if (ufocraft->size == AIRCRAFT_SMALL && capacityType != CAP_UFOHANGARS_SMALL)
-			continue;
-
-		/* Don't count item that we don't have in base */
-		if (!base->storage.num[i])
-			continue;
-
-		objIdx[num++] = i;
-	}
-
-	while (num && base->capacities[capacityType].cur > base->capacities[capacityType].max) {
-		/* Select the item to remove */
-		const int randNumber = rand() % num;
-		/* items are destroyed. We guess that all items of a given type are stored in the same location
-		 *	=> destroy all items of this type */
-		const int idx = objIdx[randNumber];
-
-		assert(idx >= 0);
-		assert(idx < MAX_OBJDEFS);
-		B_UpdateStorageAndCapacity(base, &csi.ods[idx], -base->storage.num[idx], qfalse, qfalse);
-
-		REMOVE_ELEM(objIdx, randNumber, num);
-		UR_UpdateUFOHangarCapForAll(base);
-
-		/* Make sure that we don't have an infinite loop */
-		if (num <= 0)
-			break;
-	}
-	Com_DPrintf(DEBUG_CLIENT, "B_RemoveUFOsExceedingCapacity: Remains %i in storage for a maximum of %i\n",
-		base->capacities[capacityType].cur, base->capacities[capacityType].max);
-}
-
-/**
  * @brief Update Storage Capacity.
  * @param[in] base Pointer to the base
  * @sa B_ResetAllStatusAndCapacities_f
Index: base/ufos/installation.ufo
===================================================================
--- base/ufos/installation.ufo	(revision 25743)
+++ 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 25743)
+++ 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 25743)
+++ base/ufos/basemanagement.ufo	(working copy)
@@ -233,6 +233,7 @@
 	ondestroy		"building_ondestroy"
 	capacity		1
 	type			smallufohangar
+	max_count	0
 }
 
 building building_large_ufo_hangar
@@ -249,6 +250,7 @@
 	pedia			rs_building_large_ufo_hangar
 	needs			building_large_ufo_hangar2
 	type			ufohangar
+	max_count	0
 }
 
 building building_large_ufo_hangar2
Index: base/ufos/ui/lostwon.ufo
===================================================================
--- base/ufos/ui/lostwon.ufo	(revision 25743)
+++ 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 25743)
+++ 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;"