| Scripts/equipstorage.js | "use strict";
this.name = "EquipmentStorage";
this.author = "phkb";
this.copyright = "2019 phkb";
this.description = "Handles the process of storing equipment items for reuse later";
this.licence = "CC BY-NC-SA 4.0";
// TODO:	Allow storage of missiles
this._noStore = ["EQ_BREAKABLE_TORUSDRIVE", "EQ_BREAKABLE_HUD_IFF_SCANNER", "EQ_IRONHIDE"];
this._allowedIgnored = ["EQ_SCANNER_SHOW_MISSILE_TARGET", "EQ_MULTI_TARGET", "EQ_ADVANCED_NAVIGATIONAL_ARRAY", "EQ_INTEGRATED_TARGETING_SYSTEM",
	"EQ_TARGET_MEMORY", "EQ_SMUGGLING_PHASE_ADJUSTMENT", "EQ_SDC_HACK_CHIP", "EQ_IG_INSURANCE", "EQ_IG_LEGAL_HACK", "EQ_LMSS_ACTUATOR",
	"EQ_ESCORTCONTRACTS", "EQ_TEA_MAKER", "EQ_TEAPOT", "EQ_COMMSLOGMFD", "EQ_COMMSLOGMFD_PASSIVE", "EQ_BROADCASTCOMMSMFD", "EQ_DAMAGE_REPORT_MFD",
	"EQ_DAMAGE_REPORT_MFD_PASSIVE", "EQ_COMBATMFD", "EQ_NAVIGATION_MFD", "EQ_APRIL_INVENTORY", "EQ_APRIL_TRANSFER", "EQ_APRIL_UNLOAD",
	"EQ_AMS_1", "EQ_AMS_2", "EQ_AMS_3", "EQ_AMS_4", "EQ_GLARE_FILTER", "EQ_AMS_5", "EQ_AMS_6", "EQ_AUTO_EJECT", "EQ_BOUNTY_INFORMER", "EQ_CARGOSPOTTER",
	"EQ_ELITE_RANK", "EQ_RESERVE_TANK_EMPTY", "EQ_AUX_TANK_EMPTY", "EQ_WEAPON_GATLING_LASER_COOLER", "EQ_HUDSELECTOR", "EQ_MARKET_INQUIRER_MFD",
	"EQ_TROPHY_MFD", "EQ_UPS_IDCLEANER", "EQ_MULTIPASS", "EQ_MULTIPASSED_ID", "EQ_HARDWIRE_ORE_PROCESSOR", "EQ_TARGETSELECTOR",
	"EQ_TARGETSELECTOR_MODECHANGER", "EQ_SHIPS_CAT_1", "EQ_SHIPS_CAT_2", "EQ_SHIPS_CAT_3", "EQ_SHIPS_CAT_4", "EQ_SHIPS_CAT_5", "EQ_SHIPS_CAT_6",
	"EQ_SHIPS_CAT_7", "EQ_SHIPS_CAT_8", "EQ_SHIPS_CAT_9", "EQ_CTE_TRADERNET", "EQ_CAMERA_DRONES", "EQ_WPH_ASC_UPGRADE", "EQ_POLICE_SCANNER_UPGRADE",
	"EQ_Q-CHARGER_MESS", "EQ_REPAIRBOTS_RECHARGE", "EQ_SAFETYCATCH", "EQ_SAFETY_MINE", "EQ_SCANNER_ALERTING_ENHANCEMENT", "EQ_HYPER_RADIO",
	"EQ_HYPER_RADIO_ADD", "EQ_HYPER_RADIO_PIRATE", "EQ_ILS", "EQ_WEAPON_LASER_COOLER", "EQ_MANIFEST_MFD", "EQ_SC_MANUAL_CONFIGURATOR_BASIC",
	"EQ_SC_MANUAL_CONFIGURATOR_STANDARD", "EQ_SC_MANUAL_CONFIGURATOR_ADVANCED", "EQ_TARGET_AUTOLOCK", "EQ_ENEMY_TARGETER_UPGRADE",
	"EQ_TELESCOPEEXT", "EQ_TORUSTOSUN", "EQ_TRACKER", "EQ_TRACKERCAM_LINK", "EQ_TWS_GALACTICSATNAV", "EQ_WELCOME_MAT", "EQ_GAL_DRIVE_MOD",
	"EQ_SALON_CONTROL", "EQ_SNIPER_SCOPE", "EQ_SYSTEM_MARKET_WATCH", "EQ_AUTOECM", "EQ_GALAXY_INFO", "EQ_LONG_RANGE_SCANNER_JUMP",
	"EQ_RETICLE_TARGET_SENSITIVE", "EQ_MANIF_SCANNERX", "EQ_MC_DESCRAMBLER", "EQ_MURPH_THARGOID_DRIVE_TOFIT", "EQ_ENEMY_TARGETER_UPGRADE",
	"EQ_SNIPERLOCK", "EQ_MFD_FAST_CONFIG", "EQ_GCM_COMMS_RELAY_SWITCH", "EQ_GCM_CARGO_STOPPER", "EQ_GCM_EJECTION_DAMPER", "EQ_GCM_UNPROCESSED_SCAN_DATA",
	"EQ_GCM_SOFTWARE", "EQ_GCM_STOLEN_SCHEMATICS", "EQ_GCM_STOLEN_CODES", "EQ_GCM_STOLEN_DOCUMENTS", "EQ_GCM_STOLEN_WEAPONDESIGNS",
	"EQ_RRS_FLIGHTFORM", "EQ_RRS_ESCORT_TARGETING_SYSTEM", "EQ_RRS_ESCORT_TARGETING_SYSTEM_WAYSTATION",
	"EQ_NS_MINING_SCANNER_UPGRADE", "EQ_NS_SCANNER_SCAVENGER_HACK", "EQ_TRAIL_DETECTOR"
];
this._storage = []; // main array holding storage information
this._sc_core = null; // link to shipconfig core functions
this._compNames = expandDescription("[storage-company-names]").replace("{sys}", "%H").split("|");
this._salesRep = ""; // holds a string containing the name of the local sales rep
this._page = 0; // controls which page of information will be displayed
this._lastChargeDate = 0; // date player was last charged for storage
this._display = 0; // controls which display will be shown on the interface screen
this._displayType = 0; // 0 = current location only, 1 = all locations
this._exitScreen = "GUI_SCREEN_INTERFACES";
this._selectedItem = ""; // currently selected item
this._lastChoice = ["", "", "", "", "", "", "", "", ""]; // players last choice on each of the displays
this._lastChoiceStore = "";
this._lastSource = -1; // used to establish the start point for destination system calculations
this._commodities = []; // stores a list of installed commodity keys
this._storedPrices = []; // stores a copy of any sample prices so the player always gets the same answer
this._establishmentFee = 100;
this._disabled = false;
this._menuColor = "orangeColor"; // color of general menu items
this._itemColor = "yellowColor"; // color of special menu items (eg exit menu items)
this._damagedColor = "redColor"; // color of damaged equipment items
this._installedColor = "greenColor"; // color of installed equipment items
this._riskColor = "0.8 0.8 0.8 1"; // color of risky transit items
this._refundPct = 0.85; // value between 0 and 1 that indicates how much refund you get on equipment sold. 0 = no refund, 1 = refund full original price
this._NCInstalled = false;
this._localChargeDate = 0.0;
this._deliveryText = "";
//-------------------------------------------------------------------------------------------------------------
this.startUpComplete = function () {
	if (worldScripts.ShipConfiguration_Core) this._sc_core = worldScripts.ShipConfiguration_Core;
	if (this._disabled) {
		delete this.playerWillSaveGame;
		delete this.shipWillDockWithStation;
		delete this.playerEnteredNewGalaxy;
		delete this.dayChanged;
		delete this.playerBoughtEquipment;
		delete this.playerBoughtNewShip;
		delete this.reportScreenEnded;
		delete this.missionScreenEnded;
		delete this.missionScreenOpportunity;
		delete this.startUpComplete;
		return;
	}
	// match the "Sell Equipment" OXP's refund percentage
	if (worldScripts.sell_equipment) this._refundPct = 0.6;
	// add a mission screen exception to Xenon UI
	if (worldScripts.XenonUI) {
		var wx = worldScripts.XenonUI;
		wx.$addMissionScreenException("oolite-storage-shortrangechart-map");
		wx.$addMissionScreenException("oolite-storage-longrangechart-map");
	}
	// add a mission screen exception to Xenon Redux UI
	if (worldScripts.XenonReduxUI) {
		var wxr = worldScripts.XenonReduxUI;
		wxr.$addMissionScreenException("oolite-storage-shortrangechart-map");
		wxr.$addMissionScreenException("oolite-storage-longrangechart-map");
	}
	if (worldScripts.DisplayCurrentCourse) {
		var dcc = worldScripts.DisplayCurrentCourse;
		dcc._screenIDList.push("oolite-storage-shortrangechart-map");
		dcc._screenIDList.push("oolite-storage-longrangechart-map");
	}
	if (worldScripts.CargoTypeExtension) this._NCInstalled = true;
	// pull in the values from ShipConfig vars, if loading for the first time after the split
	if (missionVariables.ShipConfig_EquipStorage) {
		this._storage = JSON.parse(missionVariables.ShipConfig_EquipStorage);
		this.$addExtraField();
		delete missionVariables.ShipConfig_EquipStorage;
	}
	if (missionVariables.ShipConfig_LastChargeDate) {
		this._lastChargeDate = parseFloat(missionVariables.ShipConfig_LastChargeDate);
		delete missionVariables.ShipConfig_LastChargeDate;
	}
	// otherwise, we'll get our own versions
	if (missionVariables.EquipmentStorage_Data) {
		this._storage = JSON.parse(missionVariables.EquipmentStorage_Data);
		delete missionVariables.EquipmentStorage_Data;
	}
	if (missionVariables.EquipmentStorage_LastChargeDate) {
		this._lastChargeDate = parseFloat(missionVariables.EquipmentStorage_LastChargeDate);
	}
	if (missionVariables.EquipmentStorage_LocalChargeDate) {
		this._localChargeDate = parseFloat(missionVariables.EquipmentStorage_LocalChargeDate);
	}
	this.$initInterface(player.ship.dockedStation);
	this._lastSource = system.ID;
	var keys = Object.keys(system.mainStation.market);
	for (var i = 0; i < keys.length; i++) {
		this._commodities.push(keys[i]);
	}
	this.$compileSamplePrices();
	// if shipconfig is present, point our nostore list at shipconfig's nosell list
	if (this._sc_core) this._noStore = this._noStore.concat(this._sc_core._noSell);
	// include shipversions placeholder equipment items
	if (worldScripts.shipversion) {
		for (var i = 75; i <= 100; i++) {
			this._noStore.push("EQ_SERVICE_LEVEL_" + i.toString());
		}
		for (var i = 10; i <= 100; i++) {
			this._noStore.push("EQ_SHIP_VERSION_" + i.toString());
		}
	}
	if (oolite.compareVersion("1.91") <= 0) {
		setExtraGuiScreenKeys(this.name, {
			guiScreen: "GUI_SCREEN_EQUIP_SHIP",
			registerKeys: { "eskey1": [{ key: "s", mod1: true }] },
			callback: this.$directOpen.bind(this)
		});
		if (worldScripts.ContextualHelp) {
			worldScripts.ContextualHelp.$addHelpTextToGuiScreen(this.name, "GUI_SCREEN_EQUIP_SHIP", expandDescription("[storage-help]"));
		}
	}
}
//-------------------------------------------------------------------------------------------------------------
this.playerWillSaveGame = function () {
	if (this._storage.length > 0) {
		missionVariables.EquipmentStorage_Data = JSON.stringify(this._storage);
	} else {
		delete missionVariables.EquipmentStorage_Data;
	}
	missionVariables.EquipmentStorage_LastChargeDate = this._lastChargeDate;
	missionVariables.EquipmentStorage_LocalChargeDate = this._localChargeDate;
}
//-------------------------------------------------------------------------------------------------------------
this.shipWillDockWithStation = function (station) {
	this.$initInterface(station);
	this.$checkForArrivals(true);
}
//-------------------------------------------------------------------------------------------------------------
this.shipWillLaunchFromStation = function (station) {
	this._lastSource = system.ID;
}
//-------------------------------------------------------------------------------------------------------------
this.shipExitedWitchspace = function () {
	this._localChargeDate = 0;
	// update the lastSource ID
	if (system.ID != -1) this._lastSource = system.ID;
	this.$compileSamplePrices();
}
//-------------------------------------------------------------------------------------------------------------
this.playerEnteredNewGalaxy = function (galaxyNumber) {
	// any in transit items will be transported to their destinations - a galjump freebee
	for (var i = 0; i < this._storage.length; i++) {
		if (this._storage[i].inTransit === true) {
			this._storage[i].system = this._storage[i].transitDestination;
			this._storage[i].systemName = this._storage[i].transitDestinationName;
			this._storage[i].inTransit = false;
			this._storage[i].transitSystem = -1;
			this._storage[i].transitDestination = -1;
			this._storage[i].transitETA = 0;
			this._storage[i].transitStart = 0;
		}
	}
	this._lastSource = system.ID;
}
//-------------------------------------------------------------------------------------------------------------
this.dayChanged = function () {
	// charge the player for storage
	if ((clock.seconds - this._lastChargeDate) > 86400) {
		this._lastChargeDate = clock.seconds;
		var data = this.$storageSystemList();
		var abort = false;
		var compList = "";
		var dropList = {};
		var totalRefund = 0;
		var totalCost = 0;
		var keys = Object.keys(data);
		for (var i = 0; i < keys.length; i++) {
			var gal = parseInt(keys[i].split("_")[0]);
			var sys = parseInt(keys[i].split("_")[1]);
			var cost = this.$calculateSystemCost(gal, sys);
			var count = this.$storageSystemCount(gal, sys);
			if (player.credits > cost) {
				player.credits -= cost;
				totalCost += cost;
				compList += (compList === "" ? "" : "\n") + "- " + expandDescription("[storage-cost-description]", { cost: formatCredits(cost, false, true), count: count, sys: data[keys[i]] }) +
					(galaxyNumber != gal ? " " + expandDescription("[storage-other-galaxy]", { gal: (gal + 1) }) : "");
			} else {
				// refund half the cost of the items in storage, then remove them
				abort = true;
				var refund = 0;
				var num_refunded = 0;
				for (var j = this._storage.length - 1; j >= 0; j--) {
					var item = this._storage[j];
					if (item.galaxy === gal && item.system === sys) {
						if (item.equipmentKey != "") {
							var info = EquipmentInfo.infoForKey(item.equipmentKey);
							if (info.effectiveTechLevel > 14) {
								refund += 1000;
							} else {
								refund += (info.price / 10) * 0.5;
							}
						}
						if (item.commodity != "") {
							refund += ((system.mainStation.market[item.commodity].price / 10) * item.weight) * 0.5;
						}
					}
					this._storage.splice(j, 1);
					num_refunded += 1;
					if (refund > cost) break;
				}
				refund -= cost;
				player.credits += refund;
				totalRefund += refund
				
				dropList[keys[i]] = {
					galaxy: gal,
					system: sys,
					systemName: data[keys[i]],
					refund: refund,
					count: num_refunded
				};
			}
		}
		// send a refund email, if required
		if (abort === true && totalRefund > 0) {
			//player.credits += totalRefund;
			// give the player an email, if installed
			var ga = worldScripts.GalCopAdminServices;
			if (ga) {
				var email = worldScripts.EmailSystem;
				var keys = Object.keys(dropList);
				for (var i = 0; i < keys.length; i++) {
					var item = dropList[keys[i]];
					var msg = expandDescription("[storeequip-cancelled]", {
						systemname: item.systemName,
						galaxy: (parseInt(item.galaxy) === galaxyNumber ? "" : " (G" + item.galaxy + ")"),
						count: item.count,
						refund: formatCredits(item.refund, true, true)
					});
					if (item.galaxy != galaxyNumber) {
						email.$createEmail({
							sender: expandDescription(this._compNames[item.system % 16]),
							subject: expandDescription("[storage-email-subject-cancelled]"),
							date: global.clock.seconds,
							message: msg,
							expiryDays: ga._defaultExpiryDays
						});
					} else {
						email.$createEmail({
							sender: expandDescription(this._compNames[item.system % 16]),
							subject: expandDescription("[storage-email-subject-cancelled]"),
							date: global.clock.seconds,
							sentFrom: item.system,
							message: msg,
							expiryDays: ga._defaultExpiryDays
						});
					}
				}
			}
		}
		// send a confirmation email, if required
		if (compList.length > 0 && totalCost > 0) {
			// give the player an email, if installed
			var ga = worldScripts.GalCopAdminServices;
			if (ga) {
				var email = worldScripts.EmailSystem;
				var msg = expandDescription("[storeequip-dailycost]", {
					cost: formatCredits(totalCost, true, true),
					costdetails: compList
				});
				email.$createEmail({
					sender: expandDescription("[storage-email-sender]"),
					subject: expandDescription("[storage-email-subject-payment]"),
					date: global.clock.seconds,
					message: msg,
					expiryDays: ga._defaultExpiryDays
				});
			}
		}
	}
}
//-------------------------------------------------------------------------------------------------------------
this.playerBoughtEquipment = function (equipment) {
	this.$checkForArrivals(false);
}
//-------------------------------------------------------------------------------------------------------------
this.playerBoughtNewShip = function (ship) {
	this.$checkForArrivals(false);
}
//-------------------------------------------------------------------------------------------------------------
this.reportScreenEnded = this.missionScreenEnded = function () {
	if (this._suspendedDestination) {
		player.ship.targetSystem = this._suspendedDestination;
		this._suspendedDestination = null;
	}
	this.$checkForArrivals(false);
}
//-------------------------------------------------------------------------------------------------------------
this.missionScreenOpportunity = function() {
	if (this._deliveryText != "") {
		var opts = {
			screenID: "oolite-storage-equipment-delivery",
			title: expandDescription("[storage-delivery-title]"),
			exitScreen: "GUI_SCREEN_STATUS",
			message: this._deliveryText
		};
		this._deliveryText = "";
		mission.runScreen(opts);
	}
}
//-------------------------------------------------------------------------------------------------------------
this.$addExtraField = function () {
	if (this._storage.length == 0) return;
	for (var i = 0; i < this._storage.length; i++) {
		if (!this._storage[i].hasOwnProperty("extra")) this._storage[i].extra = "";
	}
}
//-------------------------------------------------------------------------------------------------------------
// mission screen for displaying list of equipment that can be stored
this.$storeEquipment = function $storeEquipment() {
	function compare(a, b) {
		return b.space - a.space;
	}
	var p = player.ship;
	var stn = p.dockedStation;
	var used = 0;
	var maxspace = 0;
	if (this._sc_core) {
		used = this._sc_core.$calcEquipmentSpace(p);
		maxspace = Math.ceil(this._sc_core.$equipmentSpaceTotal(p) + this._sc_core.$cargoSpaceAvailable(p));
	}
	var interrupt = true;
	var spc = String.fromCharCode(31) + String.fromCharCode(31) + String.fromCharCode(31);
	var text = "";
	var curChoices = {};
	var itemcount = 0;
	var pagesize = 15;
	if (!this._sc_core) pagesize += 5;
	this._hudHidden = p.hudHidden;
	if (this.$isBigGuiActive() === false) p.hudHidden = true;
	if (this._sc_core) this._sc_core._configScreen = true;
	var enabled = true;
	if (this.$storageSystemCount(galaxyNumber, system.ID) === 0 && player.credits < this._establishmentFee) enabled = false;
	// display the list of options
	if (used > maxspace) {
		// make sure we don't lock the player in during flight
		if (p.alertCondition === 0) {
			interrupt = false; // set to false to force the player to sell equipment before continuing
		}
		if (this._sc_core) {
			curChoices["98_SWITCH"] = {
				text: "[sc-change-config]",
				color: this._itemColor
			};
			pagesize -= 1;
		}
		curChoices["99_EXIT"] = {
			text: "[storage-return]",
			color: this._disabledColor,
			unselectable: true
		};
	} else {
		curChoices["99_EXIT"] = {
			text: "[storage-return]",
			color: this._itemColor
		};
		if (this._sc_core) this._sc_core._initialSetup = false;
	}
	text += this.$pageHeader();
	if (this._sc_core) {
		text += this.$padTextRight(expandDescription("[storage-equipment-title]"), 16) +
			this.$padTextLeft((this._sc_core ? expandDescription("[storage-header-equip]") : " "), 4) +
			this.$padTextLeft(" ", 4) +
			this.$padTextLeft(expandDescription("[storage-header-storage]"), 8) + "\n";
	} else {
		var addText = " ";
		if (stn.isMainStation === true && this.$storageSystemCount(galaxyNumber, system.ID) === 0) {
			addText += expandDescription("[storage-deposit-required]", {
				fee: formatCredits(this._establishmentFee, false, true)
			});
		}
		text += this.$padTextRight(addText, 24) +
			this.$padTextLeft(expandDescription("[storage-header-storage]"), 8) + "\n";
	}
	text += this.$padTextRight(expandDescription("[storage-header-equip-item]"), 16) +
		this.$padTextLeft((this._sc_core ? expandDescription("[storage-header-space]") : " "), 4) +
		this.$padTextLeft((this._sc_core ? expandDescription("[storage-header-weight]") : " "), 4) +
		this.$padTextLeft(expandDescription("[storage-header-cost]"), 8) + "\n\n";
	var eq = p.equipment;
	var list = [];
	for (var i = 0; i < eq.length; i++) {
		var item = eq[i];
		// exclusions
		// don't include "no store" items
		if (this._noStore.indexOf(item.equipmentKey) >= 0) continue;
		if (this._sc_core) {
			// boosters can't be included either
			if (this._sc_core._boosters.indexOf(item.equipmentKey) >= 0) continue;
			// nor can armour
			if (this._sc_core._armour.indexOf(item.equipmentKey) >= 0) continue;
			// nor can extensions
			if (this._sc_core._extensions.indexOf(item.equipmentKey) >= 0) continue;
		}
		// we can't store smuggling compartments, because we can't reliably reinstall them later
		// storage facilities only exist at main stations, but smuggling compartments can only be installed at non-main stations
		if (item.equipmentKey.indexOf("EQ_SMUGGLING_COMPARTMENT") >= 0) continue;
		// don't include any items in the ignore equipment list that aren't specially allowed
		if (this._sc_core && this._sc_core._ignoreEquip.indexOf(item.equipmentKey) >= 0 && this._allowedIgnored.indexOf(item.equipmentKey) === -1) continue;
		var space = 0;
		var weight = 0;
		var zeroSpace = 0;
		var lookup = null;
		if (this._sc_core) lookup = this._sc_core._equipmentMatrix[item.equipmentKey];
		if (lookup) {
			space = parseFloat(lookup[0]);
			weight = parseFloat(lookup[1]);
			if (space === 0 && weight === 0) zeroSpace = 1;
		} else {
			if (item.requiredCargoSpace === 0) {
				if (this._allowedIgnored.indexOf(item.equipmentKey) >= 0) {
					zeroSpace = 1;
				} else {
					space = 1;
					weight = 1;
				}
			} else {
				weight = item.requiredCargoSpace;
				space = weight;
			}
		}
		var colr = this._menuColor;
		if (p.equipmentStatus(item.equipmentKey) === "EQUIPMENT_DAMAGED") colr = this._damagedColor;
		// special case for armour
		if (this._sc_core && this._sc_core._armour.indexOf(item.equipmentKey) >= 0) {
			if (p.script._armourFront < 100 || p.script._armourAft < 100) colr = this._damagedColor;
		}
		if (enabled === false) colr = this._disabledColor;
		if (item.isVisible && item.requiresEmptyPylon === false && this.$equipmentIsRequired(item.equipmentKey) === false && (!this._sc_core || (space > 0 || weight > 0 || zeroSpace === 1))) {
			// any items in the negotiate list will show as "???" in the cost column 
			// if the player chooses to sell this item he will have to negotiate a price
			var loop = 1;
			// special case for passenger berths - only 1 item is in the equipment list, but you could have multiple installed
			if (item.equipmentKey === "EQ_PASSENGER_BERTH") loop = p.passengerCapacity;
			for (var j = 1; j <= loop; j++) {
				list.push({
					eq: item.equipmentKey,
					space: space,
					weight: weight,
					cost: this.$calculateStorageCost(space, weight),
					color: colr,
					name: (item.equipmentKey === "EQ_PASSENGER_BERTH" ? expandDescription("[storage-passenger-berth]") : item.name),
					section: ""
				});
			}
		}
	}
	// laser weapons
	var lmss = worldScripts.LMSS_Core;
	var section = expandDescription("[storage-laser-mounts]").split("|");
	for (var i = 0; i < 7; i++) {
		var key = "";
		var damage = false;
		switch (i) {
			case 0:
				key = p.forwardWeapon.equipmentKey;
				if (key == "EQ_WEAPON_LBOOST_DAMAGED_LASER") {
					key = worldScripts["laserBooster_worldScript.js"]._holdDamage["FORWARD"].primary;
					damage = true;
				}
				break;
			case 1:
				key = p.aftWeapon.equipmentKey;
				if (key == "EQ_WEAPON_LBOOST_DAMAGED_LASER") {
					key = worldScripts["laserBooster_worldScript.js"]._holdDamage["AFT"].primary;
					damage = true;
				}
				break;
			case 2:
				key = p.portWeapon.equipmentKey;
				if (key == "EQ_WEAPON_LBOOST_DAMAGED_LASER") {
					key = worldScripts["laserBooster_worldScript.js"]._holdDamage["PORT"].primary;
					damage = true;
				}
				break;
			case 3:
				key = p.starboardWeapon.equipmentKey;
				if (key == "EQ_WEAPON_LBOOST_DAMAGED_LASER") {
					key = worldScripts["laserBooster_worldScript.js"]._holdDamage["STARBOARD"].primary;
					damage = true;
				}
				break;
			case 4:
				if (lmss) {
					key = lmss._forwardAltKey;
					if (key == "EQ_WEAPON_LBOOST_DAMAGED_LASER") {
						key = worldScripts["laserBooster_worldScript.js"]._holdDamage["FORWARD"].secondary;
						damage = true;
					}
				}
				break;
			case 5:
				if (lmss) {
					key = lmss._aftAltKey;
					if (key == "EQ_WEAPON_LBOOST_DAMAGED_LASER") {
						key = worldScripts["laserBooster_worldScript.js"]._holdDamage["AFT"].secondary;
						damage = true;
					}
				}
				break;
			case 6:
				if (lmss) {
					key = lmss._portAltKey;
					if (key == "EQ_WEAPON_LBOOST_DAMAGED_LASER") {
						key = worldScripts["laserBooster_worldScript.js"]._holdDamage["PORT"].secondary;
						damage = true;
					}
				}
				break;
			case 7:
				if (lmss) {
					key = lmss._starboardAltKey;
					if (key == "EQ_WEAPON_LBOOST_DAMAGED_LASER") {
						key = worldScripts["laserBooster_worldScript.js"]._holdDamage["STARBOARD"].secondary;
						damage = true;
					}
				}
				break;
		}
		if (key !== "" && key !== "EQ_WEAPON_NONE") {
			var item = EquipmentInfo.infoForKey(key);
			var space = 1; // for storage purposes, it's always 1t
			var weight = 0;
			var zeroSpace = 0;
			if (this._sc_core) {
				var lookup = this._sc_core._equipmentMatrix[item.equipmentKey];
				if (lookup) {
					space = parseFloat(lookup[0]);
					if (space === 0) space = 1;
					weight = parseFloat(lookup[1]);
				} else {
					weight = 1;
				}
			} else {
				weight = 1;
			}
			colr = this._menuColor;
			if (damage) colr = this._damagedColor;
			list.push({
				eq: item.equipmentKey,
				space: space,
				weight: weight,
				cost: this.$calculateStorageCost(space, weight),
				color: colr,
				name: item.name + " (" + section[i] + ")",
				section: i
			});
		}
	}
	if (list.length > 0) {
		list.sort(compare);
		var maxpage = Math.ceil(list.length / pagesize);
		if (maxpage < (this._page + 1)) this._page -= 1;
		var end = ((this._page * pagesize) + pagesize);
		if (end > list.length) end = list.length;
		// note: then station equipmentPriceFactor has been removed to prevent exploitation of equipment sales at different stations (galcop => rock hermit)
		for (var i = (this._page * pagesize); i < end; i++) {
			curChoices["01_EQUIP-" + (i < 10 ? "0" : "") + i + "~" + list[i].eq + (list[i].section !== "" ? "|" + list[i].section : "")] = {
				text: this.$padTextRight(list[i].name, 16) +
					this.$padTextLeft((this._sc_core ? list[i].space.toFixed(1) + spc + "t" : " "), 4) +
					this.$padTextLeft((this._sc_core ? list[i].weight.toFixed(1) + spc + "t" : " "), 4) +
					(list[i].cost === "???" ? this.$padTextLeft("???", 8) :
						this.$padTextLeft(formatCredits((list[i].cost), true, true) + "/d", 8)), //* stn.equipmentPriceFactor
				alignment: "LEFT",
				unselectable: !enabled,
				color: list[i].color
			};
			itemcount += 1;
		}
		for (var i = 0; i < ((pagesize + 1) - itemcount); i++) {
			curChoices["90_SPACER_" + i] = "";
		}
	} else {
		text += expandDescription("[storage-none-to-store]");
	}
	if (maxpage > 1 && this._page < (maxpage - 1)) {
		curChoices["95_NEXT"] = {
			text: "[storage-nextpage]",
			color: this._itemColor
		};
	} else {
		curChoices["95_NEXT"] = {
			text: "[storage-nextpage]",
			color: this._disabledColor,
			unselectable: true
		};
	}
	if (this._page > 0) {
		curChoices["96_PREV"] = {
			text: "[storage-prevpage]",
			color: this._itemColor
		};
	} else {
		curChoices["96_PREV"] = {
			text: "[storage-prevpage]",
			color: this._disabledColor,
			unselectable: true
		};
	}
	var def = "99_EXIT";
	if (this._lastChoiceStore != "") def = this._lastChoiceStore;
	var opts = {
		screenID: "oolite-storage-equipment-map",
		title: expandDescription("[storage-store-equipment]"),
		allowInterrupt: interrupt,
		exitScreen: (this._sc_core ? this._sc_core._sellEquipExitScreen : this._exitScreen),
		choices: curChoices,
		initialChoicesKey: def,
		message: text
	};
	if (this._sc_core) this._sc_core.$addShipModelToScreen(opts);
	mission.runScreen(opts, this.$storeEquipmentScreenHandler.bind(this), this);
	if (this._sc_core) this._sc_core.$finaliseShipModel();
}
//-------------------------------------------------------------------------------------------------------------
// handles menu selection on store equipment screen
this.$storeEquipmentScreenHandler = function $storeEquipmentScreenHandler(choice) {
	if (!choice) return;
	var p = player.ship;
	var stn = p.dockedStation;
	this._lastChoiceStore = choice;
	if (choice.indexOf("01_EQUIP") >= 0) {
		var item = choice.substring(choice.indexOf("~") + 1);
		var section = ""
		// check for a laser position
		if (item.indexOf("|") >= 0) {
			section = item.split("|")[1];
			item = item.split("|")[0];
		}
		var info = EquipmentInfo.infoForKey(item);
		var space = 0;
		var weight = 0;
		if (section === "") {
			var dam = p.equipmentStatus(item);
			var lookup = null;
			if (this._sc_core) lookup = this._sc_core._equipmentMatrix[info.equipmentKey];
			if (lookup) {
				space = parseFloat(lookup[0]);
				weight = parseFloat(lookup[1]);
				if (space === 0 && info.requiredCargoSpace > 0) space = info.requiredCargoSpace;
			} else {
				if (info.requiredCargoSpace === 0) {
					if (this._allowedIgnored.indexOf(info.equipmentKey) === -1) {
						space = 1;
						weight = 1;
					}
				} else {
					weight = info.requiredCargoSpace;
					space = weight;
				}
			}
			// special case for passenger berths
			if (item === "EQ_PASSENGER_BERTH") {
				var creds = player.credits;
				p.awardEquipment("EQ_PASSENGER_BERTH_REMOVAL");
				p.credits = creds;
			} else {
				p.removeEquipment(item);
			}
			if (item.indexOf("EQ_LMSS") >= 0) {
				// if we've removed all the positions, then remove the activator as well
				if (p.equipmentStatus("EQ_LMSS_FRONT") != "EQUIPMENT_OK" && p.equipmentStatus("EQ_LMSS_AFT") != "EQUIPMENT_OK" &&
					p.equipmentStatus("EQ_LMSS_PORT") != "EQUIPMENT_OK" && p.equipmentStatus("EQ_LMSS_STARBOARD") != "EQUIPMENT_OK") {
					p.removeEquipment("EQ_LMSS_ACTIVATOR");
				}
				worldScripts.LMSS_Core.$resetInterface();
			}
			if (this._sc_core) this._sc_core._current = {};
			var idx = this.$countItemAtStation(item);
			// for some reason passenger berths report as unavailable
			if (item === "EQ_PASSENGER_BERTH" && dam === "EQUIPMENT_UNAVAILABLE") dam = "EQUIPMENT_OK";
		} else {
			// weapons are removed from specific locations
			var lookup = null;
			if (this._sc_core) lookup = this._sc_core._equipmentMatrix[item];
			if (lookup) {
				space = parseFloat(lookup[0]);
				if (space === 0) space = 1;
				weight = parseFloat(lookup[1]);
			} else {
				space = 1;
				weight = 1;
			}
			var dam = "EQUIPMENT_OK"; // lasers can't be damaged
			var lmss = worldScripts.LMSS_Core;
			var lb = worldScripts["laserBooster_worldScript.js"];
			switch (section) {
				case "0":
					if (p.forwardWeapon.equipmentKey == "EQ_WEAPON_LBOOST_DAMAGED_LASER") {
						lb._holdDamage["FORWARD"].primary = "";
						dam = "EQUIPMENT_DAMAGED";
					}
					p.forwardWeapon = "EQ_WEAPON_NONE";
					break;
				case "1":
					if (p.aftWeapon.equipmentKey == "EQ_WEAPON_LBOOST_DAMAGED_LASER") {
						lb._holdDamage["AFT"].primary = "";
						dam = "EQUIPMENT_DAMAGED";
					}
					p.aftWeapon = "EQ_WEAPON_NONE";
					break;
				case "2":
					if (p.portWeapon.equipmentKey == "EQ_WEAPON_LBOOST_DAMAGED_LASER") {
						lb._holdDamage["PORT"].primary = "";
						dam = "EQUIPMENT_DAMAGED";
					}
					p.portWeapon = "EQ_WEAPON_NONE";
					break;
				case "3":
					if (p.starboardWeapon.equipmentKey == "EQ_WEAPON_LBOOST_DAMAGED_LASER") {
						lb._holdDamage["STARBOARD"].primary = "";
						dam = "EQUIPMENT_DAMAGED";
					}
					p.starboardWeapon = "EQ_WEAPON_NONE"
					break;
				case "4":
					if (lmss._forwardAltKey == "EQ_WEAPON_LBOOST_DAMAGED_LASER") {
						lb._holdDamage["FORWARD"].secondary = "";
						dam = "EQUIPMENT_DAMAGED";
					}
					lmss._forwardAltKey = "EQ_WEAPON_NONE";
					p.removeEquipment("EQ_LMSS_FRONT_WEAPON");
					break;
				case "5":
					if (lmss._aftAltKey == "EQ_WEAPON_LBOOST_DAMAGED_LASER") {
						lb._holdDamage["AFT"].secondary = "";
						dam = "EQUIPMENT_DAMAGED";
					}
					lmss._aftAltKey = "EQ_WEAPON_NONE";
					p.removeEquipment("EQ_LMSS_AFT_WEAPON");
					break;
				case "6":
					if (lmss._portAltKey == "EQ_WEAPON_LBOOST_DAMAGED_LASER") {
						lb._holdDamage["PORT"].primary = "";
						dam = "EQUIPMENT_DAMAGED";
					}
					lmss._portAltKey = "EQ_WEAPON_NONE";
					p.removeEquipment("EQ_LMSS_PORT_WEAPON");
					break;
				case "7":
					if (lmss._starboardAltKey == "EQ_WEAPON_LBOOST_DAMAGED_LASER") {
						lb._holdDamage["STARBOARD"].primary = "";
						dam = "EQUIPMENT_DAMAGED";
					}
					lmss._starboardAltKey = "EQ_WEAPON_NONE";
					p.removeEquipment("EQ_LMSS_STARBOARD_WEAPON");
					break;
			}
			if (lmss) lmss.$updateManifest();
			if (lb) lb.$updateManifest();
			var idx = this.$countItemAtStation(item);
			// once stored, they will become just standard lasers, not allocated to any specific position on the ship
			// positions will be confirmed when lasers are re-installed 
		}
		// charge player deposit
		if (this.$storageSystemCount(galaxyNumber, system.ID) === 0 && (clock.adjustedSeconds - this._localChargeDate) > 86400) {
			player.credits -= this._establishmentFee;
			this._localChargeDate = clock.adjustedSeconds;
			player.consoleMessage(expandDescription("[storage-opening-account]", { cost: formatCredits(this._establishmentFee, false, true) }), 5);
		}
		this._storage.push({
			galaxy: galaxyNumber,
			system: system.ID,
			systemName: system.name,
			equipmentKey: item,
			commodity: "",
			space: space,
			weight: weight,
			status: dam,
			index: idx,
			inTransit: false,
			transitETA: 0,
			transitDestination: -1,
			transitSystem: -1,
			transitStart: 0
		});
		// give the player an email, if installed
		var ga = worldScripts.GalCopAdminServices;
		if (ga) {
			var email = worldScripts.EmailSystem;
			var storeName = expandDescription(this._compNames[system.ID % 16]);
			if (this.$storageSystemCount(galaxyNumber, system.ID) === 0) {
				// email T&C's
			}
			// were we charged something, or did we get a refund?
			var cost = expandDescription("[storage-ongoing-cost]", { cost: formatCredits(this.$calculateStorageCost(space, weight), true, true) });
			var msg = expandDescription("[storeequip-email]", {
				storagename: storeName,
				equipname: info.name,
				equipcost: cost,
				sender: this.$salesRep()
			});
			email.$createEmail({
				sender: storeName,
				subject: expandDescription("[storage-email-subject-storing]"),
				date: global.clock.adjustedSeconds,
				message: msg,
				expiryDays: ga._defaultExpiryDays
			});
		}
		if (this._sc_core) this._sc_core.$addEquipmentSpaceItem(p);
	}
	if (choice === "95_NEXT") this._page += 1;
	if (choice === "96_PREV") this._page -= 1;
	if (choice === "98_SWITCH") {
		this._sc_core._source = "";
		this._sc_core.$shipConfig();
		return;
	}
	if (this._sc_core && this._sc_core._source === "REMOVEITEM") return;
	if (choice != "99_EXIT") {
		this.$storeEquipment();
		return;
	}
	if (choice === "99_EXIT") {
		if (this._sc_core) {
			if (this._sc_core._source === "CONFIGSCREEN") {
				this._sc_core._source = "";
				this._sc_core.$shipConfig();
			}
			return;
		}
		this.$viewStoredItems();
	}
}
//-------------------------------------------------------------------------------------------------------------
// initialise the equipment specification list
this.$initInterface = function $initInterface(station) {
	// no access in interstellar space
	if (system.ID === -1) {
		station.setInterface(this.name, null);
		return;
	}
	if (this.$stationHasStorageFacilities(station) === true) {
		station.setInterface(this.name, {
			title: expandDescription("[storage-interface-title-mgr]"),
			category: expandDescription("[storage-interface-category]"),
			summary: expandDescription("[storage-interface-summary-mgr]"),
			callback: this.$openStorageListing.bind(this)
		});
	} else {
		station.setInterface(this.name, {
			title: expandDescription("[storage-interface-title-info]"),
			category: expandDescription("[storage-interface-category]"),
			summary: expandDescription("[storage-interface-summary-info]"),
			callback: this.$openStorageListing.bind(this)
		});
	}
}
//-------------------------------------------------------------------------------------------------------------
this.$openStorageListing = function $openStorageListing() {
	this._page = 0;
	this._display = 0;
	this._displayType = 0;
	this._exitScreen = "GUI_SCREEN_INTERFACES";
	this.$viewStoredItems();
}
//-------------------------------------------------------------------------------------------------------------
this.$directOpen = function $directOpen() {
	this._page = 0;
	this._display = 0;
	this._displayType = 0;
	this._exitScreen = "GUI_SCREEN_EQUIP_SHIP";
	this.$viewStoredItems();
}
//-------------------------------------------------------------------------------------------------------------
// views mission screen showing currently stored equipment
this.$viewStoredItems = function $viewStoredItems() {
	function compare(a, b) {
		return (b.sort > a.sort ? -1 : 1);
	}
	var p = player.ship;
	var stn = p.dockedStation;
	var text = "";
	var curChoices = {};
	var itemcount = 0;
	var list = [];
	var pagesize = 20;
	this._hudHidden = p.hudHidden;
	if (this.$isBigGuiActive() === false) p.hudHidden = true;
	if (this._sc_core) this._sc_core._configScreen = true;
	// main list
	if (this._display === 0) {
		pagesize = 19;
		text += expandDescription("[storage-view-header]", { name: this._compNames[system.ID % 16] }) + (p.dockedStation.isMainStation === true ? "" : expandDescription("[storage-view-main])")) + "\n\n";
		text += this.$padTextRight(expandDescription("[storage-header-item]"), 16) +
			this.$padTextRight(expandDescription("[storage-header-location]"), 10) +
			this.$padTextLeft(expandDescription("[storage-header-cost]"), 6) + "\n";
		for (var i = 0; i < this._storage.length; i++) {
			if (this._displayType === 1 || (this._storage[i].galaxy === galaxyNumber && this._storage[i].system === system.ID) || this._storage[i].inTransit === true) {
				if (this._storage[i].equipmentKey != "") {
					var equip = EquipmentInfo.infoForKey(this._storage[i].equipmentKey);
					if (equip) { // only display equipment items that are currently installed (in case an OXP is removed)
						var colr = this._menuColor;
						if (this._storage[i].galaxy === galaxyNumber && this._storage[i].system === system.ID) colr = this._installedColor;
						if (this._storage[i].status === "EQUIPMENT_DAMAGED") colr = this._damagedColor;
						if (this._storage[i].galaxy != galaxyNumber) colr = "blueColor";
						if (this._storage[i].inTransit === true) colr = this._riskColor;
						var info = null;
						var dist = 0;
						if (this._storage[i].galaxy === galaxyNumber && this._storage[i].system != system.ID && this._storage[i].system >= 0) {
							info = System.infoForSystem(galaxyNumber, this._storage[i].system);
							var route = system.info.routeToSystem(info);
							if (route && route.route.length > 0) dist = route.distance;
						}
						list.push({
							equipmentName: expandDescription("[storage-header-equipment]") + ": " + (equip.equipmentKey === "EQ_PASSENGER_BERTH" ? expandDescription("[storage-passenger-berth]") : equip.name),
							location: (this._storage[i].inTransit === false ? this._storage[i].systemName + " " + (info != null ? expandDescription("[storage-distance]", { dist: dist.toFixed(1) }) : (this._storage[i].galaxy != galaxyNumber ? expandDescription("[storage-other-galaxy]", { gal: (this._storage[i].galaxy + 1) }) : "")) : expandDescription("[storage-in-transit]")),
							cost: expandDescription("[storage-cost-per-day]", { cost: formatCredits(this.$calculateStorageCost(this._storage[i].space, this._storage[i].weight), true, true) }),
							equipmentKey: this._storage[i].equipmentKey,
							commodity: "",
							galaxy: this._storage[i].galaxy,
							system: this._storage[i].system,
							color: colr,
							sort: this._storage[i].galaxy + "_" + this._storage[i].systemName + "_" + equip.name,
							id: this._storage[i].galaxy + "|" + this._storage[i].system + "|" + this._storage[i].equipmentKey + "|" + this._storage[i].index
						});
					}
				}
				if (this._storage[i].commodity != "" && this._commodities.indexOf(this._storage[i].commodity) >= 0) {
					var colr = this._menuColor;
					if (this._storage[i].galaxy === galaxyNumber && this._storage[i].system === system.ID) colr = this._installedColor;
					if (this._storage[i].galaxy != galaxyNumber) colr = "blueColor";
					if (this._storage[i].inTransit === true) colr = this._riskColor;
					var info = null;
					var dist = 0;
					if (this._storage[i].galaxy === galaxyNumber && this._storage[i].system != system.ID && this._storage[i].system >= 0) {
						info = System.infoForSystem(galaxyNumber, this._storage[i].system);
						var route = system.info.routeToSystem(info);
						if (route && route.route.length > 0) dist = route.distance;
					}
					list.push({
						equipmentName: expandDescription("[storage-header-cargo]") + ": " + this._storage[i].weight + this.$getCommodityUnit(this._storage[i].commodity) + " × " + displayNameForCommodity(this._storage[i].commodity) + (this._storage[i].extra != "" ? "*" : ""),
						location: (this._storage[i].inTransit === false ? this._storage[i].systemName + " " + (info != null ? expandDescription("[storage-distance]", { dist: dist.toFixed(1) }) : (this._storage[i].galaxy != galaxyNumber ? expandDescription("[storage-other-galaxy]", { gal: (this._storage[i].galaxy + 1) }) : "")) : expandDescription("[storage-in-transit]")),
						cost: expandDescription("[storage-cost-per-day]", { cost: formatCredits(this.$calculateStorageCost(this._storage[i].space, this._storage[i].weight, this.$getCommodityUnit(this._storage[i].commodity)), true, true) }),
						equipmentKey: "",
						commodity: this._storage[i].commodity,
						galaxy: this._storage[i].galaxy,
						system: this._storage[i].system,
						color: colr,
						sort: this._storage[i].galaxy + "_" + this._storage[i].systemName + "_zz" + displayNameForCommodity(this._storage[i].commodity), // ensure cargo items come after equipment
						id: this._storage[i].galaxy + "|" + this._storage[i].system + "|" + this._storage[i].commodity + "|" + this._storage[i].index
					});
				}
			}
		}
		if (list.length > 0) {
			list.sort(compare);
			var maxpage = Math.ceil(list.length / pagesize);
			if (maxpage < (this._page + 1)) this._page -= 1;
			var end = ((this._page * pagesize) + pagesize);
			if (end > list.length) end = list.length;
			// note: the station equipmentPriceFactor has been removed to prevent exploitation of equipment sales at different stations (galcop => rock hermit)
			for (var i = (this._page * pagesize); i < end; i++) {
				curChoices["01_EQUIP-" + (i < 10 ? "0" : "") + i + "~" + list[i].id] = {
					text: this.$padTextRight(list[i].equipmentName, 16) +
						this.$padTextRight(list[i].location, 10) +
						this.$padTextLeft(list[i].cost, 6),
					alignment: "LEFT",
					color: list[i].color
				};
				itemcount += 1;
			}
			for (var i = 0; i < (pagesize - itemcount) + (p.dockedStation.isMainStation === false ? 1 : 0); i++) {
				curChoices["90_SPACER_" + i] = "";
			}
		} else {
			text += expandDescription("[storage-equip-none]");
		}
		if (p.dockedStation.isMainStation === true) {
			if (this.$storageSystemCount(galaxyNumber, system.ID) === 0 && player.credits < this._establishmentFee) {
				if (!this._sc_core) {
					curChoices["98_STOREEQUIP"] = {
						text: expandDescription("[storage-store-equipment-menu]") + expandDescription("[storage-no-credits]"),
						color: this._disabledColor,
						unselectable: true
					};
				}
				curChoices["98_MOVECARGO"] = {
					text: expandDescription("[storage-move-cargo]") + expandDescription("[storage-no-credits]"),
					color: this._disabledColor,
					unselectable: true
				};
			} else {
				if (!this._sc_core) {
					curChoices["98_STOREEQUIP"] = {
						text: expandDescription("[storage-store-equipment-menu]"),
						color: this._itemColor,
					};
				}
				curChoices["98_MOVECARGO"] = {
					text: "[storage-move-cargo]",
					color: this._itemColor
				};
			}
		}
		if (maxpage > 1 && this._page < (maxpage - 1)) {
			curChoices["95_NEXT"] = {
				text: "[storage-nextpage]",
				color: this._itemColor
			};
		} else {
			curChoices["95_NEXT"] = {
				text: "[storage-nextpage]",
				color: this._disabledColor,
				unselectable: true
			};
		}
		if (this._page > 0) {
			curChoices["96_PREV"] = {
				text: "[storage-prevpage]",
				color: this._itemColor
			};
		} else {
			curChoices["96_PREV"] = {
				text: "[storage-prevpage]",
				color: this._disabledColor,
				unselectable: true
			};
		}
		if (this._displayType === 1) {
			curChoices["97_SHOWLOCAL"] = {
				text: "[storage-showlocal]",
				color: this._itemColor
			};
		} else {
			curChoices["97_SHOWALL"] = {
				text: "[storage-showall]",
				color: this._itemColor
			};
		}
		curChoices["99_EXIT"] = {
			text: expandDescription("[storage-exit]"),
			color: this._itemColor
		};
		var def = "99_EXIT";
		if (this._lastChoice[this._display] != "") def = this._lastChoice[this._display];
		var opts = {
			screenID: "oolite-storage-main-map",
			title: expandDescription("[storage-screen-title]", { type: expandDescription((this._displayType === 0 ? "[storage-screen-local-only]" : "[storage-screen-all-systems]")) }),
			allowInterrupt: true,
			overlay: {
				name: "equipstorage.png",
				height: 546
			},
			exitScreen: this._exitScreen,
			choices: curChoices,
			initialChoicesKey: def,
			message: text
		};
	}
	// equipment details
	if (this._display === 1) {
		var itemIndex = this.$getStorageIndex(this._selectedItem);
		var item = this._storage[itemIndex];
		if (item.equipmentKey != "") {
			var equip = EquipmentInfo.infoForKey(item.equipmentKey);
		}
		var info = null;
		var dist = 0;
		var jumps = 0;
		var danger = 0;
		var illegal = 0;
		var time = 0;
		if (item.galaxy === galaxyNumber && item.system != system.ID && item.system >= 0) {
			this.$revealMap();
			info = System.infoForSystem(galaxyNumber, item.system);
			var route = system.info.routeToSystem(info);
			jumps = route.route.length;
			dist = route.distance;
			danger = this.$checkRouteDangerLevel(route);
			if (item.commodity != "") illegal = this.$checkRouteIllegalLevel(route, item.commodity);
			time = route.time;
			this.$restoreMap();
		}
		if (item.equipmentKey != "") {
			text = this.$padTextRight(expandDescription("[storage-header-equip-item]") + ":", 10) + (item.equipmentKey === "EQ_PASSENGER_BERTH" ? expandDescription("[storage-passenger-berth]") : equip.name) + "\n";
			text += this.$padTextRight(expandDescription("[storage-header-equip-status]") + ":", 10) + expandDescription((item.status === "EQUIPMENT_OK" ? "[storage-equip-ok]" : "[storage-equip-damaged]")) + "\n";
			if (this._sc_core) {
				if (parseFloat(item.space) === 0 && parseFloat(item.weight) === 0) {
					text += this.$padTextRight(expandDescription("[storage-header-space-weight]") + ":", 10) + expandDescription("[storage-negligible]") + "\n";
				} else {
					text += this.$padTextRight(expandDescription("[storage-header-space-weight]") + ":", 10) + (parseFloat(item.space) === 0 ? item.weight : item.space) + "t/" + item.weight + "t\n";
				}
			}
			text += this.$padTextRight(expandDescription("[storage-header-storage-cost]") + ":", 10) + expandDescription("[storage-cost-per-day-long]", {cost:formatCredits(this.$calculateStorageCost(item.space, item.weight), true, true)}) + "\n";
		}
		if (item.commodity != "") {
			text = this.$padTextRight(expandDescription("[storage-header-cargo-type]") + ":", 10) + displayNameForCommodity(item.commodity) + "\n";
			if (this._sc_core) {
				var spcwt = this.$cargoSpaceRequired(this.$getCommodityUnit(item.commodity), item.weight);
				text += this.$padTextRight(expandDescription("[storage-header-space-weight]") + ":", 10) + spcwt + "t/" + item.weight + this.$getCommodityUnit(item.commodity) + "\n";
			}
			text += this.$padTextRight(expandDescription("[storage-header-storage-cost]") + ":", 10) + expandDescription("[storage-cost-per-day-long]", {cost: formatCredits(this.$calculateStorageCost(item.space, item.weight, this.$getCommodityUnit(item.commodity)), true, true)}) + "\n";
			if (item.extra != "") {
				text += this.$padTextRight(expandDescription("[storage-store-note]"), 10) + ":" + expandDescription("[storage-store-extra]", {item: item.extra.split(";").length.toString() + this.$getCommodityUnit(item.commodity)}) + "\n";
			}
		}
		if (item.system != -1) {
			text += this.$padTextRight(expandDescription("[storage-header-location]") + ":", 10) +
				item.systemName + " " + (info != null ? expandDescription("[storage-distance]", { dist: dist.toFixed(1) }) : (item.galaxy != galaxyNumber ? expandDescription("[storage-other-galaxy]", { gal: (item.galaxy + 1) }) : "")) + "\n";
		} else {
			text += this.$padTextRight(expandDescription("[storage-header-location]") + ":", 10) + expandDescription("[storage-in-transit]") + "\n";
			var eta = this.$getTimeRemaining(item.transitETA);
			text += this.$padTextRight(expandDescription("[storage-header-eta]") + ":", 10) + System.systemNameForID(item.transitDestination) + (eta === expandDescription("[storage-overdue]") ? expandDescription("[storage-overdue-long]") : expandDescription("[storage-in-time]", { eta: eta })) + "\n";
		}
		var tfrcost = 0;
		if (dist > 0) {
			tfrcost = this.$calculateTransferCost(dist, jumps, danger, illegal);
			text += this.$padTextRight(expandDescription("[storage-header-tfr-cost]") + ":", 10) + formatCredits(tfrcost, true, true) + "\n";
			text += this.$padTextRight(expandDescription("[storage-header-tfr-time]") + ":", 10) + time.toFixed(1) + " " + expandDescription("[storage-hours]") + "\n";
			text += this.$padTextRight(expandDescription("[storage-header-tfr-risk]") + ":", 10) + expandDescription("[transfer-danger-" + danger + "]") + "\n";
		}
		if (dist > 7.5) {
			curChoices["25_SHOWMAP_LONG"] = {
				text: "[storage-showmap]",
				color: this._itemColor
			};
		} else if (dist > 0 && dist <= 7.5) {
			curChoices["25_SHOWMAP_SHORT"] = {
				text: "[storage-showmap]",
				color: this._itemColor
			};
		}
		if (dist > 0 && player.credits >= tfrcost) {
			if (item.equipmentKey != "") {
				curChoices["26_TRANSFER"] = {
					text: "[storage-transfer]",
					color: this._itemColor
				};
			} else if (item.commodity != "") {
				curChoices["26_TRANSFER"] = {
					text: "[storage-transfer-cargo]",
					color: this._itemColor
				};
			}
		}
		if (item.inTransit === true) {
			curChoices["35_WAIT"] = {
				text: "[storage-wait]",
				color: this._itemColor
			};
		}
		// can only install items if we're in the same system and at the main station
		if (item.galaxy === galaxyNumber && item.system === system.ID && p.dockedStation.isMainStation === true) {
			if (item.equipmentKey != "") {
				// can we install this item?
				var inst = true;
				var reason = "";
				var curr = p.equipmentStatus(item.equipmentKey);
				if (p.canAwardEquipment(item.equipmentKey) === false) {
					inst = false;
					reason = expandDescription("[storage-install-invalid]");
				}
				// special case for shield boosters
				//if (item.equipmentKey === "EQ_NAVAL_SHIELD_BOOSTER" && p.equipmentStatus("EQ_SHIELD_BOOSTER") != "EQUIPMENT_OK") {inst = false; reason = " (required equipment item not installed)";}
				// if we can't carry multiple versions of it, and we already have one, then no
				if (curr === "EQUIPMENT_OK" && equip.canCarryMultiple === false) {
					inst = false;
					reason = expandDescription("[storage-install-already-in]");
				}
				// reset for laser items
				if (item.equipmentKey.indexOf("EQ_WEAPON") >= 0) {
					inst = true;
					reason = "";
				}
				// if the current item of the same time is damaged, and so's the one in storage, then nodeName
				// (otherwise it can be used to fix the broken one)
				if (curr === "EQUIPMENT_DAMAGED" && item.status === "EQUIPMENT_DAMAGED") {
					inst = false;
					reason = expandDescription("[storage-install-damaged]");
				}
				// do we have room for it, or will it put the ship into overload?
				var eq = EquipmentInfo.infoForKey(item.equipmentKey);
				if (eq.requiredCargoSpace > 0 && p.cargoSpaceAvailable < eq.requiredCargoSpace) {
					inst = false;
					reason = expandDescription("[storage-install-no-cargo-space]");
				}
				var lookup = null;
				if (this._sc_core) lookup = this._sc_core._equipmentMatrix[item.equipmentKey];
				if (lookup) {
					var equipSpace = this._sc_core.$availableEquipmentSpace(p);
					// subtract the space the existing item is taking up
					if (curr === "EQUIPMENT_OK" || curr === "EQUIPMENT_DAMAGED") equipSpace += lookup[0];
					if (lookup[0] > equipSpace) {
						inst = false;
						reason = expandDescription("[storage-install-no-equip-space]");
					}
				}
				if (inst === true) {
					curChoices["30_INSTALL"] = {
						text: "[storage-install]",
						color: this._itemColor
					};
				} else {
					curChoices["30_INSTALL"] = {
						text: expandDescription("[storage-install]" + reason),
						color: this._disabledColor,
						unselectable: true
					};
				}
			}
			if (item.commodity != "") {
				// can we transfer any of this cargo into our hold
				var inst = true;
				var reason = "";
				if (p.cargoSpaceAvailable === 0) {
					inst = false;
					reason = expandDescription("[storage-install-no-cargo-space]");
				}
				if (inst === true) {
					curChoices["31_LOAD"] = {
						text: "[storage-load-cargo]",
						color: this._itemColor
					};
				} else {
					curChoices["31_LOAD"] = {
						text: expandDescription("[storage-load-cargo]" + reason),
						color: this._disabledColor,
						unselectable: true
					};
				}
			}
		}
		// work out item cost
		if (item.equipmentKey != "") {
			if (item.inTransit === false && equip.techLevel <= 14 && item.galaxy === galaxyNumber) {
				var sellcost = equip.price / 10 * (item.status === "EQUIPMENT_DAMAGED" ? 0.5 : (item.equipmentKey.indexOf("EQ_WEAPON") >= 0 ? 1 : this._refundPct)); // * factor;
				curChoices["70_SELL"] = {
					text: expandDescription("[storage-sell]", {
						cost: formatCredits(sellcost, true, true)
					}),
					color: this._itemColor
				};
			}
		}
		if (item.commodity != "") {
			// can only sell stored cargo when you're in the same system
			if (item.inTransit === false && item.galaxy === galaxyNumber) {
				var pcsfee = 1;
				var maxqty = item.weight;
				var sellcost = 0;
				var price = 0;
				if (item.system === system.ID) {
					if ((item.weight + system.mainStation.market[item.commodity].quantity) > 127) {
						maxqty = 127 - system.mainStation.market[item.commodity].quantity;
					}
					// 10% price penalty for selling remotely
					if (p.dockedStation.isMainStation === false) pcsfee = 0.9;
					price = (system.mainStation.market[item.commodity].price / 10) * pcsfee;
				} else {
					if (item.weight + Math.floor(system.scrambledPseudoRandomNumber(77) * 127) > 127) {
						maxqty = 127 - Math.floor(system.scrambledPseudoRandomNumber(77) * 127);
					}
					// price will be 20% less than the sample price
					pcsfee = 0.8;
					price = this.$getSamplePrice(item.system, item.commodity) * pcsfee;
				}
				// only show the sell option if we can sell something
				if (maxqty > 0) {
					sellcost = price * maxqty;
					curChoices["71_SELL"] = {
						text: expandDescription("[storage-sell]", {
							cost: maxqty + this.$getCommodityUnit(item.commodity) + (item.extra != "" ? "*" : "") + " @ " + price.toFixed(1) +
								", total " + formatCredits(sellcost, true, true) +
								(pcsfee < 1 ? expandDescription("[storage-process-fee]", { fee: ((1 - pcsfee) * 100).toFixed(0) }) : "")
						}),
						color: this._itemColor
					};
				} else {
					curChoices["71_SELL"] = {
						text: "[storage-nosell]",
						color: this._disabledColor,
						unselectable: true
					};
				}
			}
		}
		// give player the option of dumping stuff in other galaxies (you're unable to do business transactions from different sectors)
		if (item.galaxy != galaxyNumber) {
			curChoices["80_DUMP"] = {
				text: "[storage-dump]",
				color: this._itemColor
			};
		}
		curChoices["98_EXIT"] = {
			text: "[storage-return]",
			color: this._itemColor
		};
		var def = "98_EXIT";
		if (this._lastChoice[this._display] != "") def = this._lastChoice[this._display];
		var opts = {
			screenID: "oolite-storage-item-map",
			title: expandDescription("[storage-screen-title]", { type: (item.inTransit === true ? expandDescription("[storage-in-transit]") : item.systemName) }),
			allowInterrupt: true,
			overlay: {
				name: "equipstorage.png",
				height: 546
			},
			exitScreen: this._exitScreen,
			choices: curChoices,
			initialChoicesKey: def,
			message: text
		};
	}
	// show short range map to location
	if (this._display === 2) {
		var itemIndex = this.$getStorageIndex(this._selectedItem);
		var item = this._storage[itemIndex];
		var playerTargetSystem = this.$playerTargetSystem();
		// hold the player's destination
		this._suspendedDestination = p.targetSystem;
		// override it for the display
		p.targetSystem = item.system;
		var bg = "SHORT_RANGE_CHART";
		curChoices["20_RETURN"] = {
			text: "[storage-return-item]",
			color: this._itemColor
		};
		if (item.system != system.ID && item.system >= 0 && playerTargetSystem != item.system && p.hasEquipmentProviding("EQ_ADVANCED_NAVIGATIONAL_ARRAY")) {
			curChoices["96_SETCOURSE~" + item.system] = {
				text: expandDescription("[storage-set-course]", { sys: item.systemName }),
				color: this._itemColor
			};
		}
		curChoices["98_EXIT"] = {
			text: "[storage-return]",
			color: this._itemColor
		};
		def = "20_RETURN";
		if (this._lastChoice[this._display] != "") def = this._lastChoice[this._display];
		var opts = {
			screenID: "oolite-storage-shortrangechart-map",
			title: expandDescription("[storage-screen-title]", { type: expandDescription("[storage-screen-chart]") }),
			backgroundSpecial: bg,
			allowInterrupt: true,
			exitScreen: this._exitScreen,
			choices: curChoices,
			initialChoicesKey: def,
			message: text
		};
	}
	// show long range map to location
	if (this._display === 3) {
		var itemIndex = this.$getStorageIndex(this._selectedItem);
		var item = this._storage[itemIndex];
		var playerTargetSystem = this.$playerTargetSystem();
		// hold the player's destination
		this._suspendedDestination = p.targetSystem;
		// override it for the display
		p.targetSystem = item.system;
		var bg = "LONG_RANGE_CHART";
		if (p.hasEquipmentProviding("EQ_ADVANCED_NAVIGATIONAL_ARRAY")) bg = "LONG_RANGE_CHART_SHORTEST";
		curChoices["20_RETURN"] = {
			text: "[storage-return-item]",
			color: this._itemColor
		};
		if (item.system != system.ID && item.system >= 0 && playerTargetSystem != item.system && p.hasEquipmentProviding("EQ_ADVANCED_NAVIGATIONAL_ARRAY")) {
			curChoices["96_SETCOURSE~" + item.system] = {
				text: expandDescription("[storage-set-course]", { sys: item.systemName }),
				color: this._itemColor
			};
		}
		curChoices["98_EXIT"] = {
			text: "[storage-return]",
			color: this._itemColor
		};
		def = "20_RETURN";
		if (this._lastChoice[this._display] != "") def = this._lastChoice[this._display];
		var opts = {
			screenID: "oolite-storage-longrangechart-map",
			title: expandDescription("[storage-screen-title]", { type: expandDescription("[storage-screen-chart]") }),
			backgroundSpecial: bg,
			allowInterrupt: true,
			exitScreen: this._exitScreen,
			choices: curChoices,
			initialChoicesKey: def,
			message: text
		};
	}
	// request transfer
	if (this._display === 4) {
		var itemIndex = this.$getStorageIndex(this._selectedItem);
		var item = this._storage[itemIndex];
		if (item.equipmentKey != "") {
			var equip = EquipmentInfo.infoForKey(item.equipmentKey);
		}
		var info = null;
		var dist = 0;
		var jumps = 0;
		var danger = 0;
		var illegal = 0;
		var time = 0;
		if (item.galaxy === galaxyNumber && item.system != system.ID && item.system >= 0) {
			this.$revealMap();
			info = System.infoForSystem(galaxyNumber, item.system);
			var route = system.info.routeToSystem(info);
			jumps = route.route.length;
			dist = route.distance;
			danger = this.$checkRouteDangerLevel(route);
			if (item.commodity != "") illegal = this.$checkRouteIllegalLevel(route, item.commodity);
			time = route.time;
			this.$restoreMap();
		}
		if (item.equipmentKey != "") {
			text = this.$padTextRight(expandDescription("[storage-header-equip-item]") + ":", 10) + (item.equipmentKey === "EQ_PASSENGER_BERTH" ? expandDescription("[storage-passenger-berth]") : equip.name) + "\n";
			text += this.$padTextRight(expandDescription("[storage-header-equip-status]") + ":", 10) + expandDescription((item.status === "EQUIPMENT_OK" ? "[storage-equip-ok]" : "[storage-equip-damaged]")) + "\n";
			text += this.$padTextRight(expandDescription("[storage-header-space-weight]") + ":", 10) + (item.space === 0 ? item.weight : item.space) + "t/" + item.weight + "t\n";
			text += this.$padTextRight(expandDescription("[storage-header-storage-cost]") + ":", 10) + expandDescription("[storage-cost-per-day]", { cost: formatCredits(this.$calculateStorageCost(item.space, item.weight), true, true) }) + "\n";
		}
		if (item.commodity != "") {
			text = this.$padTextRight(expandDescription("[storage-header-cargo-type]") + ":", 10) + displayNameForCommodity(item.commodity) + "\n";
			var spcwt = this.$cargoSpaceRequired(this.$getCommodityUnit(item.commodity), item.weight);
			text += this.$padTextRight(expandDescription("[storage-header-space-weight]") + ":", 10) + spcwt + "t/" + item.weight + this.$getCommodityUnit(item.commodity) + "\n";
			text += this.$padTextRight(expandDescription("[storage-header-storage-cost]") + ":", 10) + expandDescription("[storage-cost-per-day]", { cost: formatCredits(this.$calculateStorageCost(item.space, item.weight, this.$getCommodityUnit(item.commodity)), true, true) }) + "\n";
		}
		if (item.system != -1) {
			text += this.$padTextRight(expandDescription("[storage-header-location]") + ":", 10) +
				item.systemName + " " + (info != null ? expandDescription("[storage-distance]", { dist: dist.toFixed(1) }) : (item.galaxy != galaxyNumber ? expandDescription("[storage-other-galaxy]", { gal: (item.galaxy + 1) }) : "")) + "\n";
		} else {
			text += this.$padTextRight(expandDescription("[storage-header-location]") + ":", 10) + expandDescription("[storage-in-transit]") + "\n";
			var eta = this.$getTimeRemaining(item.transitETA);
			text += this.$padTextRight(expandDescription("[storage-header-eta]") + ":", 10) + System.systemNameForID(item.transitDestination) + (eta === expandDescription("[storage-overdue]") ? expandDescription("[storage-overdue-long]") : expandDescription("[storage-in-time]", { eta: eta })) + "\n";
		}
		if (dist > 0) {
			var tfrcost = this.$calculateTransferCost(dist, jumps, danger, illegal);
			text += this.$padTextRight(expandDescription("[storage-header-tfr-cost]") + ":", 10) + formatCredits(tfrcost, true, true) + "\n";
			text += this.$padTextRight(expandDescription("[storage-header-tfr-time]") + ":", 10) + time.toFixed(1) + " " + expandDescription("[storage-hours]") + "\n";
			text += this.$padTextRight(expandDescription("[storage-header-tfr-risk]") + ":", 10) + expandDescription("[transfer-danger-" + danger + "]") + "\n";
		}
		if (item.commodity != "" && illegal > 0) {
			text += "\n\n" + expandDescription("[storage-cost-bribes]");
		}
		text += "\n\n" + expandDescription("[storage-transfer-wait]");
		curChoices["40_YES"] = {
			text: expandDescription("[storage-yes]"),
			color: this._itemColor
		};
		curChoices["41_NO"] = {
			text: expandDescription("[storage-no]"),
			color: this._itemColor
		};
		curChoices["42_CANCEL"] = {
			text: expandDescription("[storage-cancel-tfr]"),
			color: this._itemColor
		};
		def = "42_CANCEL";
		var opts = {
			screenID: "oolite-storage-item-map",
			title: expandDescription("[storage-screen-title]", { type: item.systemName }),
			allowInterrupt: true,
			overlay: {
				name: "equipstorage.png",
				height: 546
			},
			exitScreen: this._exitScreen,
			choices: curChoices,
			initialChoicesKey: def,
			message: text
		};
	}
	// transfer cargo to storage - select commodity
	if (this._display === 5) {
		var cargo = p.manifest.list;
		var itemcount = 1;
		var sdm = worldScripts.Smugglers_DockMaster;
		var gcm = worldScripts.GalCopBB_Missions;
		var notAllowed = ["slaves", "firearms", "narcotics"];
		text = expandDescription("[storage-select-cargo]") + ":\n";
		if (this.$storageSystemCount(galaxyNumber, system.ID) === 0) {
			text += expandDescription("[storage-note-fee]", { fee: formatCredits(this._establishmentFee, false, true) }) + "\n";
			itemcount += 1;
		}
		for (var i = 0; i < cargo.length; i++) {
			// make sure we don't store relabelled cargo
			var q = cargo[i].quantity;
			if (sdm && sdm._cargoLabelled.length > 0) {
				for (var j = 0; j < sdm._cargoLabelled.length; j++) {
					if (sdm._cargoLabelled[j].newCommodity === cargo[i].commodity) q -= sdm._cargoLabelled[j].quantity;
				}
			}
			// make sure we're not storing something required for a mission that would stop that mission from working.
			if (gcm) {
				var restr = gcm.$cargoRestricted(cargo[i].commodity);
				q -= restr;
			}
			if (q > 0) {
				var unselect = false;
				if (notAllowed.indexOf(cargo[i].commodity) >= 0) unselect = true
				curChoices["50_" + (i < 10 ? "0" : "") + i + "_CARGO~" + cargo[i].commodity] = {
					text: cargo[i].displayName + " (" + q + cargo[i].unit + ")" + (this.$calcCTEcargo(cargo[i].commodity) > 0 ? "*" : "") +
						(unselect === true ? expandDescription("[storage-not-permitted]") : ""),
					color: (unselect === false ? this._menuColor : this._disabledColor),
					unselectable: unselect
				};
				itemcount += 1;
			}
		}
		for (var i = 0; i < (25 - itemcount); i++) {
			curChoices["97_SPACER_" + i] = "";
		}
		curChoices["98_EXIT"] = {
			text: "[storage-return]",
			color: this._itemColor
		};
		var def = "98_EXIT";
		if (this._lastChoice[this._display] != "") def = this._lastChoice[this._display];
		var opts = {
			screenID: "oolite-storage-cargo-map",
			title: expandDescription("[storage-screen-title]", { type: expandDescription("[storage-header-cargo]") }),
			allowInterrupt: true,
			overlay: {
				name: "equipstorage.png",
				height: 546
			},
			exitScreen: this._exitScreen,
			choices: curChoices,
			initialChoicesKey: def,
			message: text
		};
	}
	// how much of the cargo should be transferred
	if (this._display === 6) {
		// either from storage (tfrType = 1) or ships hold (tfrType = 0)
		this.$getTransferAmount();
		return;
	}
	// confirm dumping
	if (this._display === 7) {
		var itemIndex = this.$getStorageIndex(this._selectedItem);
		var item = this._storage[itemIndex];
		var itmText = "";
		if (item.equipmentKey != "") {
			var equip = EquipmentInfo.infoForKey(item.equipmentKey);
			itmText = " - " + (item.equipmentKey === "EQ_PASSENGER_BERTH" ? expandDescription("[storage-passenger-berth]") : equip.name);
		}
		if (item.commodity != "") {
			itmText = " - " + item.weight + this.$getCommodityUnit(item.commodity) + " × " + displayNameForCommodity(item.commodity);
		}
		text = expandDescription("[storage-dump-confirm]", {
			item: itmText
		});
		curChoices["81_YES"] = {
			text: expandDescription("[storage-yes]"),
			color: this._itemColor
		};
		curChoices["82_NO"] = {
			text: expandDescription("[storage-no]"),
			color: this._itemColor
		};
		var def = "82_NO";
		if (this._lastChoice[this._display] != "") def = this._lastChoice[this._display];
		var opts = {
			screenID: "oolite-storage-cargo-map",
			title: expandDescription("[storage-screen-title]", { type: expandDescription("[storage-dump]") }),
			allowInterrupt: true,
			overlay: {
				name: "equipstorage.png",
				height: 546
			},
			exitScreen: this._exitScreen,
			choices: curChoices,
			initialChoicesKey: def,
			message: text
		};
	}
	// install laser
	if (this._display === 8) {
		var itemcount = 3;
		var itemIndex = this.$getStorageIndex(this._selectedItem);
		var item = this._storage[itemIndex];
		if (item.equipmentKey != "") {
			var equip = EquipmentInfo.infoForKey(item.equipmentKey);
		}
		text = expandDescription("[storage-install-item]", { item: equip.name }) + "\n\n" + expandDescription("[storage-install-weapon]") + ":\n";
		var lmss = worldScripts.LMSS_Core;
		var section = expandDescription("[storage-laser-mounts]").split("|");
		for (var i = 0; i < 7; i++) {
			var curr = "X";
			switch (i) {
				case 0:
					if (" 1 3 5 7 9 11 13 15 ".indexOf(" " + p.weaponFacings.toString() + " ") >= 0)
						curr = p.forwardWeapon.equipmentKey;
					break;
				case 1:
					if (" 2 3 6 7 10 11 14 15 ".indexOf(" " + p.weaponFacings.toString() + " ") >= 0)
						curr = p.aftWeapon.equipmentKey;
					break;
				case 2:
					if (" 4 5 6 7 12 13 14 15 ".indexOf(" " + p.weaponFacings.toString() + " ") >= 0)
						curr = p.portWeapon.equipmentKey;
					break;
				case 3:
					if (" 8 9 10 11 12 13 14 15 ".indexOf(" " + p.weaponFacings.toString() + " ") >= 0)
						curr = p.starboardWeapon.equipmentKey;
					break;
				case 4:
					if (" 1 3 5 7 9 11 13 15 ".indexOf(" " + p.weaponFacings.toString() + " ") >= 0)
						if (lmss && p.equipmentStatus("EQ_LMSS_FRONT") === "EQUIPMENT_OK") curr = lmss._forwardAltKey;
					break;
				case 5:
					if (" 2 3 6 7 10 11 14 15 ".indexOf(" " + p.weaponFacings.toString() + " ") >= 0)
						if (lmss && p.equipmentStatus("EQ_LMSS_AFT") === "EQUIPMENT_OK") curr = lmss._aftAltKey;
					break;
				case 6:
					if (" 4 5 6 7 12 13 14 15 ".indexOf(" " + p.weaponFacings.toString() + " ") >= 0)
						if (lmss && p.equipmentStatus("EQ_LMSS_PORT") === "EQUIPMENT_OK") curr = lmss._portAltKey;
					break;
				case 7:
					if (" 8 9 10 11 12 13 14 15 ".indexOf(" " + p.weaponFacings.toString() + " ") >= 0)
						if (lmss && p.equipmentStatus("EQ_LMSS_STARBOARD") === "EQUIPMENT_OK") curr = lmss._starboardAltKey;
					break;
			}
			if (curr !== "X") {
				itemcount += 1;
				if (curr === "" || curr === "EQ_WEAPON_NONE") {
					curChoices[(60 + i) + "_WEAPON~" + item.equipmentKey] = {
						text: this.$padTextRight(section[i], 31),
						color: this._menuColor,
						unselectable: false
					};
				} else {
					curChoices[(60 + i) + "_WEAPON~" + item.equipmentKey] = {
						text: this.$padTextRight(section[i], 10) + this.$padTextLeft(expandDescription("[storage-current-weapon]", { item: EquipmentInfo.infoForKey(curr).name }), 21),
						color: this._disabledColor,
						unselectable: true
					};
				}
			}
		}
		for (var i = 0; i < (25 - itemcount); i++) {
			curChoices["97_SPACER_" + i] = "";
		}
		curChoices["98_EXIT"] = {
			text: "[storage-return]",
			color: this._itemColor
		};
		var def = "98_EXIT";
		if (this._lastChoice[this._display] != "") def = this._lastChoice[this._display];
		var opts = {
			screenID: "oolite-storage-item-map",
			title: expandDescription("[storage-screen-title]", { type: expandDescription("[storage-screen-weapon]") }),
			allowInterrupt: true,
			overlay: {
				name: "equipstorage.png",
				height: 546
			},
			exitScreen: this._exitScreen,
			choices: curChoices,
			initialChoicesKey: def,
			message: text
		};
	}
	mission.runScreen(opts, this.$itemStorageScreenHandler.bind(this), this);
}
//-------------------------------------------------------------------------------------------------------------
this.$itemStorageScreenHandler = function $itemStorageScreenHandler(choice) {
	if (this._suspendedDestination) player.ship.targetSystem = this._suspendedDestination;
	this._suspendedDestination = null;
	if (choice == null) return;
	var p = player.ship;
	var stn = p.dockedStation;
	this._lastChoice[this._display] = choice;
	if (choice.indexOf("01_EQUIP") >= 0) {
		this._selectedItem = choice.substring(choice.indexOf("~") + 1);
		this._display = 1;
	}
	if (choice === "20_RETURN") this._display = 1;
	if (choice === "25_SHOWMAP_SHORT") this._display = 2;
	if (choice === "25_SHOWMAP_LONG") this._display = 3;
	if (choice === "26_TRANSFER") this._display = 4;
	if (choice === "35_WAIT") {
		// switch over to the wait option
		choice = "40_YES";
	}
	if (choice === "30_INSTALL") {
		var itemIndex = this.$getStorageIndex(this._selectedItem);
		var item = this._storage[itemIndex];
		// for laser weapons, we need to check what position it will be loaded into
		if (item.equipmentKey.indexOf("EQ_WEAPON") >= 0) {
			this._display = 8;
		} else {
			var equip = EquipmentInfo.infoForKey(item.equipmentKey);
			this._display = 0;
			// we should only get here if it's possible to install the equipment, so we shouldn't need to do any extra checks
			if (p.equipmentStatus(item.equipmentKey) === "EQUIPMENT_DAMAGED") {
				if (equip.techLevel <= 14) {
					// ask the player if they want to keep the damaged item?
					var curChoices = {};
					var sellcost = equip.price / 10 * 0.5 * this._refundPct;
					curChoices["01_SELL"] = expandDescription("[storage-sell]", { cost: formatCredits(sellcost, true, true) });
					curChoices["02_STORE"] = expandDescription("[storage-store]");
					var opts = {
						screenID: "oolite-storage-swapout-map",
						title: expandDescription("[storage-screen-title]", { type: item.systemName }),
						allowInterrupt: false,
						overlay: {
							name: "equipstorage.png",
							height: 546
						},
						exitScreen: this._exitScreen,
						choices: curChoices,
						message: expandDescription("[storage-sellorstore]"),
					};
					mission.runScreen(opts, this.$sellOrStoreDamaged.bind(this), this);
					return;
				} else {
					// no choice but to store the item, so swap it out
					p.setEquipmentStatus(item.equipmentKey, "EQUIPMENT_OK");
					item.status = "EQUIPMENT_DAMAGED";
					player.consoleMessage(expandDescription("[storage-damaged-replaced]"));
				}
			} else {
				if (p.awardEquipment(item.equipmentKey) === true) {
					if (item.status === "EQUIPMENT_DAMAGED") p.setEquipmentStatus(item.equipmentKey, "EQUIPMENT_DAMAGED");
					player.consoleMessage(expandDescription("[storage-item-installed]"));
					if (item.equipmentKey.indexOf("EQ_LMSS") >= 0) {
						worldScripts.LMSS_Core.playerBoughtEquipment(item.equipmentKey);
					}
					this._storage.splice(itemIndex, 1);
				} else {
					player.consoleMessage(expandDescription("[storage-cant-install]"));
					this._display = 1;
				}
			}
		}
	}
	if (choice === "31_LOAD") {
		var itemIndex = this.$getStorageIndex(this._selectedItem);
		var item = this._storage[itemIndex];
		this._tfrCargo = item.commodity;
		this._maxCargo = item.weight;
		this._tfrExtra = item.extra.split(";").length.toString();
		// how much of this can the player ship handle?
		var orig = p.manifest[this._tfrCargo];
		p.manifest[this._tfrCargo] += this._maxCargo;
		// check that the hold actually took all the amount
		if (p.manifest[this._tfrCargo] != (orig + this._maxCargo)) {
			// if not, adjust the amount of transfer
			this._maxCargo = p.manifest[this._tfrCargo] - orig;
		}
		p.manifest[this._tfrCargo] = orig;
		this._tfrType = 1; // transfer to cargo hold
		this._display = 6; // ask player how much cargo to transfer
	}
	if (choice === "42_CANCEL") this._display = 1;
	if (choice === "40_YES") {
		// we're waiting, so more the equipment directly into local storage and add the required time
		var itemIndex = this.$getStorageIndex(this._selectedItem);
		var item = this._storage[itemIndex];
		var equip = EquipmentInfo.infoForKey(item.equipmentKey);
		this.$revealMap();
		var danger = 0;
		var time = 0;
		var info = System.infoForSystem(galaxyNumber, (item.inTransit === true ? item.transitSystem : item.system));
		var route = system.info.routeToSystem(info);
		var jumps = route.route.length;
		var dist = route.distance;
		var danger = this.$checkRouteDangerLevel(route);
		var illegal = (item.commodity != "" ? this.$checkRouteIllegalLevel(route, item.commodity) : 0);
		var time = route.time * 3600 + (Math.floor(Math.random() * 4) * 3600);
		var tfrcost = this.$calculateTransferCost(dist, jumps, danger, illegal);
		player.credits -= parseInt(tfrcost * 10) / 10;
		player.consoleMessage(expandDescription("[storage-tfr-started]", { cost: formatCredits(tfrcost, true, true) }));
		var destroyed = false;
		// work out if the item arrives safely
		for (var i = 0; i < route.route.length; i++) {
			var sys = System.infoForSystem(galaxyNumber, route.route[i]);
			if (Math.random() > ((sys.government + 1) / 9) && Math.random() > ((sys.government + 1) / 9) && Math.random() > ((sys.government + 1) / 9)) {
				destroyed = true;
				break;
			}
		}
		if (destroyed === false && item.commodity != "") {
			// chance of item being confiscated
			var chance = 0;
			for (var i = 0; i < illegal; i++) {
				chance += Math.random();
			}
			var caught = (chance < (illegal * 0.2) ? false : true);
		}
		this.$restoreMap();
		// add the transit time to the current time
		clock.addSeconds(time);
		this._display = 0;
		var info = null;
		var route = null;
		if (destroyed === true) {
			// bad luck, pal
			var sellcost = 0;
			var extra = "";
			if (equip) {
				if (equip.techLevel <= 14) {
					sellcost = (equip.price / 10 * (item.status === "EQUIPMENT_DAMAGED" ? 0.5 : 1) * 0.1);
					extra = expandDescription("[storage-insurance-payout-equip]");
				}
			} else if (item.commodity != "") {
				// 10% of cargo value
				sellcost = Math.floor(((system.mainStation.manifest[item.commodity].price / 10) * item.weight) * 0.1);
				extra = expandDescription("[storage-insurance-payout-cargo]");
			}
			var storeName = expandDescription(this._compNames[item.system % 16]);
			mission.runScreen({
				screenID: "oolite-storage-destroyed-map",
				title: expandDescription("[storage-screen-title]", { type: expandDescription("[storage-screen-transfer]") }),
				overlay: {
					name: "equipstorage.png",
					height: 546
				},
				allowInterrupt: false,
				message: (equip ? expandDescription("[storage-transfer-destroyed]", {
					extra: extra,
					storagename: storeName
				}) : expandDescription("[storage-transfer-destroyed-cargo]", {
					extra: extra,
					storagename: storeName
				})),
				exitScreen: this._exitScreen
			}, this.$transferFailed.bind(this), this);
			// give player a consolation refund of 10% of item cost
			if (sellcost > 0) {
				player.credits += sellcost;
				player.consoleMessage(expandDescription("[storage-refund]", { refund: formatCredits(sellcost, true, true) }));
			}
			// remove the item from the storage array
			this._storage.splice(itemIndex, 1);
			return;
		} else if (caught === true) {
			var storeName = expandDescription(this._compNames[item.system % 16]);
			mission.runScreen({
				screenID: "oolite-storage-caught-map",
				title: expandDescription("[storage-screen-title]", { type: expandDescription("[storage-screen-transfer]") }),
				overlay: {
					name: "equipstorage.png",
					height: 546
				},
				allowInterrupt: false,
				message: expandDescription("[storage-transfer-caught]", {
					storagename: storeName
				}),
				exitScreen: this._exitScreen
			}, this.$transferFailed.bind(this), this);
			this._storage.splice(itemIndex, 1);
			return;
		} else {
			// otherwise just move the item to be local
			item.system = system.ID;
			item.systemName = system.name;
		}
	}
	if (choice === "41_NO") {
		var itemIndex = this.$getStorageIndex(this._selectedItem);
		var item = this._storage[itemIndex];
		var info = System.infoForSystem(galaxyNumber, item.system);
		var route = system.info.routeToSystem(info);
		var jumps = route.route.length;
		var dist = route.distance;
		var danger = this.$checkRouteDangerLevel(route);
		var illegal = (item.commodity != "" ? this.$checkRouteIllegalLevel(route, item.commodity) : 0);
		var tfrcost = this.$calculateTransferCost(dist, jumps, danger, illegal);
		player.credits -= parseInt(tfrcost * 10) / 10;
		player.consoleMessage(expandDescription("[storage-tfr-started]", { cost: formatCredits(tfrcost, true, true) }) + ".");
		item.inTransit = true;
		item.transitDestination = system.ID;
		item.transitDestinationName = system.name;
		item.transitSystem = item.system;
		item.transitStart = clock.adjustedSeconds;
		item.system = -1;
		item.transitETA = clock.adjustedSeconds + (route.time * 3600) + 7200;
		this._display = 0;
	}
	if (choice.indexOf("50_") >= 0) {
		this._tfrCargo = choice.substring(choice.indexOf("~") + 1);
		this._maxCargo = p.manifest[this._tfrCargo];
		this._tfrType = 0; // transfer to storage
		this._tfrExtra = this.$calcCTEcargo(this._tfrCargo).toString();
		this._display = 6;
	}
	if (choice.match(/6\d\_WEAPON/g)) {
		var itemIndex = this.$getStorageIndex(this._selectedItem);
		var item = this._storage[itemIndex];
		var equip = choice.substring(choice.indexOf("~") + 1);
		var section = choice.substring(1, 2);
		// note: because of the way I interfaced LMSS with redspear's "NewLasers" OXP, you will need to have the LMSS OXP in order to install most New Lasers
		var lmss = worldScripts.LMSS_Core;
		if (lmss) lmss._switching = true;
		var lb = worldScripts["laserBooster_worldScript.js"];
		// we will only allow installation into an empty position, so there is no need to do a remove/re-store
		switch (section) {
			case "0":
				if (item.status == "EQUIPMENT_DAMAGED") {
					p.forwardWeapon = "EQ_WEAPON_LBOOST_DAMAGED_LASER";
					lb._holdDamage["FORWARD"].primary = equip;
					lb.$updateManifest();
				} else {
					p.forwardWeapon = equip;
				}
				break;
			case "1":
				if (item.status == "EQUIPMENT_DAMAGED") {
					p.forwardWeapon = "EQ_WEAPON_LBOOST_DAMAGED_LASER";
					lb._holdDamage["AFT"].primary = equip;
					lb.$updateManifest();
				} else {
					p.aftWeapon = equip;
				}
				break;
			case "2":
				if (item.status == "EQUIPMENT_DAMAGED") {
					p.forwardWeapon = "EQ_WEAPON_LBOOST_DAMAGED_LASER";
					lb._holdDamage["PORT"].primary = equip;
					lb.$updateManifest();
				} else {
					p.portWeapon = equip;
				}
				break;
			case "3":
				if (item.status == "EQUIPMENT_DAMAGED") {
					p.forwardWeapon = "EQ_WEAPON_LBOOST_DAMAGED_LASER";
					lb._holdDamage["STARBOARD"].primary = equip;
					lb.$updateManifest();
				} else {
					p.starboardWeapon = equip;
				}
				break;
			case "4":
				if (lmss) {
					if (item.status == "EQUIPMENT_DAMAGED") {
						lmss._forwardAltKey = "EQ_WEAPON_LBOOST_DAMAGED_LASER";
						lb._holdDamage["FORWARD"].secondary = equip;
						lb.$updateManifest();
					} else {
						lmss._forwardAltKey = equip;
					}
					p.awardEquipment("EQ_LMSS_FRONT_WEAPON");
				}
				break;
			case "5":
				if (lmss) {
					if (item.status == "EQUIPMENT_DAMAGED") {
						lmss._aftAltKey = "EQ_WEAPON_LBOOST_DAMAGED_LASER";
						lb._holdDamage["AFT"].secondary = equip;
						lb.$updateManifest();
					} else {
						lmss._aftAltKey = equip;
					}
					p.awardEquipment("EQ_LMSS_AFT_WEAPON");
				}
				break;
			case "6":
				if (lmss) {
					if (item.status == "EQUIPMENT_DAMAGED") {
						lmss._portAltKey = "EQ_WEAPON_LBOOST_DAMAGED_LASER";
						lb._holdDamage["PORT"].secondary = equip;
						lb.$updateManifest();
					} else {
						lmss._portAltKey = equip;
					}
					p.awardEquipment("EQ_LMSS_PORT_WEAPON");
				}
				break;
			case "7":
				if (lmss) {
					if (item.status == "EQUIPMENT_DAMAGED") {
						lmss._starboardAltKey = "EQ_WEAPON_LBOOST_DAMAGED_LASER";
						lb._holdDamage["STARBOARD"].secondary = equip;
						lb.$updateManifest();
					} else {
						lmss._starboardAltKey = equip;
					}
					p.awardEquipment("EQ_LMSS_STARBOARD_WEAPON");
				}
				break;
		}
		if (lmss) {
			lmss._switching = false;
			lmss.$updateManifest();
		}
		this._storage.splice(itemIndex, 1);
		this._display = 0;
	}
	if (choice === "70_SELL") {
		var itemIndex = this.$getStorageIndex(this._selectedItem);
		this.$sellEquipment(itemIndex);
		this._display = 0;
	}
	if (choice === "71_SELL") {
		var itemIndex = this.$getStorageIndex(this._selectedItem);
		// ask how much to sell
		this._tfrCargo = this._storage[itemIndex].commodity;
		this._maxCargo = this._storage[itemIndex].weight;
		this._tfrExtra = this._storage[itemIndex].extra.split(";").length.toString();
		if (this._storage[itemIndex].system != system.ID) {
			if (this._maxCargo + (system.scrambledPseudoRandomNumber(77) * 127) > 127) {
				this._maxCargo = 127 - (system.scrambledPseudoRandomNumber(77) * 127);
			}
		} else {
			if ((this._maxCargo + system.mainStation.market[this._tfrCargo].quantity) > 127) {
				this._maxCargo = 127 - system.mainStation.market[this._tfrCargo].quantity;
			}
		}
		this._tfrType = 2;
		this._display = 6;
	}
	if (choice === "80_DUMP") this._display = 7;
	if (choice === "81_YES") {
		var itemIndex = this.$getStorageIndex(this._selectedItem);
		this._storage.splice(itemIndex, 1);
		player.consoleMessage(expandDescription("[storage-item-dump-ok]"));
		this._display = 0;
	}
	if (choice === "82_NO") this._display = 1;
	if (choice.indexOf("96_SETCOURSE") >= 0) {
		var dest = parseInt(choice.substring(choice.indexOf("~") + 1));
		if (dest >= 0 && dest <= 255) {
			player.ship.targetSystem = dest;
			player.ship.infoSystem = player.ship.targetSystem;
			player.consoleMessage(expandDescription("[storage-course-set]", { sys: System.systemNameForID(dest) }));
		}
	}
	if (choice === "98_STOREEQUIP") {
		this.$storeEquipment();
		return;
	}
	if (choice === "95_NEXT") this._page += 1;
	if (choice === "96_PREV") this._page -= 1;
	if (choice === "97_SHOWLOCAL") {
		this._displayType = 0;
		this._lastChoice[this._display] = "97_SHOWALL";
	}
	if (choice === "97_SHOWALL") {
		this._displayType = 1;
		this._lastChoice[this._display] = "97_SHOWLOCAL";
	}
	if (choice === "98_MOVECARGO") this._display = 5;
	if (choice === "98_EXIT") this._display = 0;
	if (choice != "99_EXIT") {
		this.$viewStoredItems();
		return;
	}
}
//-------------------------------------------------------------------------------------------------------------
// prompts the user for the transfer amount
this.$getTransferAmount = function $getTransferAmount(type) {
	var text = "";
	// don't show a screen when only 1 unit is possible
	if (this._maxCargo === 1) {
		switch (this._tfrType) {
			case 0:
				this.$getCargoTransferIn(1);
				return;
			case 1:
				this.$getCargoTransferOut(1);
				return;
			case 2:
				this.$getCargoToSell(1);
				return;
		}
	}
	switch (this._tfrType) {
		case 0:
			text = expandDescription("[enter-quantity-storage]", {
				commodity: displayNameForCommodity(this._tfrCargo),
				quantity: this._maxCargo,
				cte_note: (this._tfrExtra != "" ? "\n\n" + expandDescription("[storage-tfr-extra]", { amount: this._tfrExtra }) : "")
			});
			break;
		case 1:
			text = expandDescription("[enter-quantity-hold]", {
				commodity: displayNameForCommodity(this._tfrCargo),
				quantity: this._maxCargo,
				cte_note: (this._tfrExtra != "" ? "\n\n" + expandDescription("[storage-tfr-extra]", { amount: this._tfrExtra }) : "")
			});
			break;
		case 2:
			text = expandDescription("[enter-quantity-sell]", {
				commodity: displayNameForCommodity(this._tfrCargo),
				quantity: this._maxCargo,
				cte_note: (this._tfrExtra != "" ? "\n\n" + expandDescription("[storage-tfr-extra]", { amount: this._tfrExtra }) : "")
			});
			break;
	}
	var opts = {
		screenID: "oolite-storage-transfer-map",
		title: expandDescription("[storage-screen-title]", { type: displayNameForCommodity(this._tfrCargo) }),
		allowInterrupt: false,
		overlay: {
			name: "equipstorage.png",
			height: 546
		},
		exitScreen: this._exitScreen,
		message: text,
		textEntry: true
	};
	switch (this._tfrType) {
		case 0:
			mission.runScreen(opts, this.$getCargoTransferIn.bind(this), true);
			break;
		case 1:
			mission.runScreen(opts, this.$getCargoTransferOut.bind(this), true);
			break;
		case 2:
			mission.runScreen(opts, this.$getCargoToSell.bind(this), true);
			break;
	}
}
//-------------------------------------------------------------------------------------------------------------
// parses the input from the getTransferAmount screen - for transferring cargo to storage
this.$getCargoTransferIn = function $getCargoTransferIn(param) {
	if (!param) {
		this._display = 0;
		this.$viewStoredItems();
		return;
	}
	if (parseInt(param) >= 1 && parseInt(param) <= this._maxCargo) {
		var amount = parseInt(param);
		var cte = worldScripts.CargoTypeExtension;
		var extra = "";
		if (cte) {
			extra = this.$extractCargoFromCTE(this._tfrCargo, amount, this._maxCargo);
		}
		var idx = this.$countItemAtStation(this._tfrCargo);
		if (idx === 0) {
			// charge establishment fee (if required)
			if (this.$storageSystemCount(galaxyNumber, system.ID) === 0 && (clock.adjustedSeconds - this._localChargeDate) > 86400) {
				player.credits -= this._establishmentFee;
				this._localChargeDate = clock.adjustedSeconds;
				player.consoleMessage(expandDescription("[storage-opening-account]", { cost: formatCredits(this._establishmentFee, false, true) }), 5);
			}
			var spcwt = this.$cargoSpaceRequired(this.$getCommodityUnit(this._tfrCargo), amount);
			this._storage.push({
				galaxy: galaxyNumber,
				system: system.ID,
				systemName: system.name,
				equipmentKey: "",
				commodity: this._tfrCargo,
				space: spcwt,
				weight: amount,
				status: "",
				index: 0,
				inTransit: false,
				transitETA: 0,
				transitDestination: -1,
				transitSystem: -1,
				transitStart: 0,
				extra: extra
			});
		} else {
			idx = this.$getStorageIndex(galaxyNumber + "|" + system.ID + "|" + this._tfrCargo + "|0");
			this._storage[idx].weight += amount;
			var spcwt = this.$cargoSpaceRequired(this.$getCommodityUnit(this._tfrCargo), this._storage[idx].weight);
			this._storage[idx].space = spcwt;
			this._storage[idx].extra += (this._storage[idx].extra == "" ? "" : ";") + extra;
		}
		// take the cargo out of the hold
		var p = player.ship;
		p.manifest[this._tfrCargo] -= amount;
	}
	this._display = 0;
	this.$viewStoredItems();
}
//-------------------------------------------------------------------------------------------------------------
this.$calcCTEcargo = function (generic) {
	var cte = worldScripts.CargoTypeExtension;
	if (!cte) return 0;
	// if there isn't enough generic cargo of a particular type
	// throw out special cargo entries of that type until there is
	var totalspecial = 0;
	if (cte.specialCargoCarried[generic]) {
		for (var i = 0; i < cte.specialCargoCarried[generic].length; i++) {
			if (cte.specialCargoRegister[cte.specialCargoCarried[generic][i].type]) {
				totalspecial += cte.specialCargoCarried[generic][i].quantity;
			}
		}
	}
	return totalspecial;
}
//-------------------------------------------------------------------------------------------------------------
// parses the input from the getTransferAmount screen - for transferring cargo to hold
this.$getCargoTransferOut = function $getCargoTransferOut(param) {
	if (!param) {
		this._display = 1;
		this.$viewStoredItems();
		return;
	}
	if (parseInt(param) >= 1 && parseInt(param) <= this._maxCargo) {
		var itemIndex = this.$getStorageIndex(this._selectedItem);
		var p = player.ship;
		var amount = parseInt(param);
		var starting = p.manifest[this._tfrCargo];
		// put the cargo into the main hold
		p.manifest[this._tfrCargo] += amount;
		// check that the hold actually took all the amount
		if (p.manifest[this._tfrCargo] != (starting + amount)) {
			// if not, adjust the amount of transfer
			amount = p.manifest[this._tfrCargo] - starting;
		}
		// interface with hermitage cargo
		//if (worldScripts.Hermitage_Cargo) {
		//	worldScripts.Hermitage_Cargo.$addCargoToData(this._tfrCargo, amount, p.dockedStation.equivalentTechLevel, system.info.economy);
		//}
		var count = 0;
		var ncmanifest = this.$checkForAndRemoveCTECargo(this._tfrCargo, amount);
		if (ncmanifest && this._NCInstalled) {
			var cte = worldScripts.CargoTypeExtension;
			cte.mergePlayerManifest(ncmanifest);
		}
		this._storage[itemIndex].weight -= amount;
		if (this._storage[itemIndex].weight === 0) {
			this._storage.splice(itemIndex, 1);
			this._display = 0;
			this.$viewStoredItems();
			return;
		}
		this._storage[itemIndex].space = this.$cargoSpaceRequired(this.$getCommodityUnit(this._tfrCargo), this._storage[itemIndex].weight);
	}
	this._display = 1;
	this.$viewStoredItems();
}
//-------------------------------------------------------------------------------------------------------------
// parses the input from the getTransferAmount screen - for selling cargo 
this.$getCargoToSell = function $getCargoToSell(param) {
	if (!param) {
		this._display = 1;
		this.$viewStoredItems();
		return;
	}
	if (parseInt(param) >= 1 && parseInt(param) <= this._maxCargo) {
		var itemIndex = this.$getStorageIndex(this._selectedItem);
		var item = this._storage[itemIndex];
		var amount = parseInt(param);
		var price = 0;
		// give player credits
		if (item.system === system.ID) {
			// 10% price penalty for selling remotely in the same system
			price = (system.mainStation.market[item.commodity].price / 10) * (player.ship.dockedStation.isMainStation ? 1 : 0.9);
			// add cargo to market
			var current = system.mainStation.market[item.commodity].quantity;
			// make sure we don't go over capacity
			var diff = 0;
			if (current + amount > system.mainStation.market[item.commodity].capacity) {
				diff = (current + amount) - system.mainStation.market[item.commodity].capacity;
				amount -= diff;
			}
			system.mainStation.setMarketQuantity(item.commodity, current + amount);
		} else {
			// use the sample price as the means to calc the sale price at a distant system
			// includes a 20% price penalty for selling remotely
			price = this.$getSamplePrice(item.system, item.commodity) * 0.8;
		}
		player.credits += price * amount;
		player.consoleMessage(expandDescription("[storage-credit]", { amount: formatCredits(price * amount, true, true) }));
		this.$playSound("sell");
		// remove qty from storage
		this._storage[itemIndex].weight -= amount;
		// if we have nothing left, remove the item from the array
		if (this._storage[itemIndex].weight === 0) {
			this._storage.splice(itemIndex, 1);
			this._display = 0;
			this.$viewStoredItems();
			return;
		}
		this._storage[itemIndex].space = this.$cargoSpaceRequired(this.$getCommodityUnit(this._tfrCargo), this._storage[itemIndex].weight);
	}
	this._display = 1;
	this.$viewStoredItems();
}
//-------------------------------------------------------------------------------------------------------------
// extracts an certain amount of a particular commodity from new cargoes
this.$extractCargoFromCTE = function (commodity, amount, max) {
	var cte = worldScripts.CargoTypeExtension;
	var special = this.$calcCTEcargo(commodity);
	var normal = max - special;
	var extra = "";
	if (amount - normal > 0) {
		// we're transferring some special cargo
		var storage = [];
		var ncmanifest = cte.suspendPlayerManifest(commodity);
		var nc = ncmanifest.split("|");
		var lst = nc[1].split("/");
		var counter = 0;
		for (var i = 0; i < lst.length; i++) {
			if (lst[i] != "") {
				var itms = lst[i].split(";");
				for (var j = 0; j < itms.length; j++) {
					if (itms[j] != "") {
						// reduce the original
						var sub = itms[j].split("=");
						var removed = 0;
						while (sub[1] > 0) {
							sub[1]--;
							counter++;
							removed++;
							if ((amount - normal) - counter == 0) break;
						}
						itms[j] = sub.join("=");
						//storage.push(sub[0] + "=" + removed + "=" + sub[2]);
						// make a copy of what we're taking out
						// we'll store them a 1 unit items to make the return process easier
						for (var k = 0; k < removed; k++) {
							var st = "";
							for (var m = 0; m < sub.length; m++) {
								st += (st == "" ? "" : "=") + (m == 1 ? 1 : sub[m]);
							}
							storage.push(st);
						}
					}
				}
				lst[i] = itms.join(";");
			}
			if ((amount - normal) - counter == 0) break;
		}
		nc[1] = lst.join("/");
		ncmanifest = nc.join("|");
		// tell NC about what we've done
		cte.mergePlayerManifest(ncmanifest);
		// pull our storage data together
		extra = storage.join(";");
	}
	return extra;
}
//-------------------------------------------------------------------------------------------------------------
this.$checkForAndRemoveCTECargo = function (commodity, quantity) {
	var extra = "";
	for (var i = 0; i < this._storage.length; i++) {
		if (this._storage[i].commodity == commodity) {
			if (this._storage[i].extra != "") {
				var dta = [];
				dta = this._storage[i].extra.split(";");
				var normal = this._storage[i].weight - dta.length;
				if (quantity - normal > 0) {
					var diff = quantity - normal;
					// compile a list of the cargo items to re-merge with nc
					while (diff > 0) {
						extra += (extra == "" ? "" : ";") + dta.shift();
						diff--;
					}
					this._storage[i].extra = dta.join(";");
				}
			}
		}
	}
	// because we store everything as 1 unit items, we need to compile it back into totals so NC has good data
	var hold = this.$compileCTEValues(extra);
	var ncmanifest = "2|" + this.$buildMergeManifest(commodity, hold);
	return ncmanifest;
}
//-------------------------------------------------------------------------------------------------------------
// compile multiple 1 unit items down to one entry with a 1+ unit count
this.$compileCTEValues = function (extra) {
	var items = extra.split(";");
	var list = {};
	for (var i = 0; i < items.length; i++) {
		if (!list[items[i]]) {
			list[items[i]] = 1;
		} else {
			list[items[i]] = list[items[i]] + 1;
		}
	}
	var keys = Object.keys(list);
	var final = "";
	for (var i = 0; i < keys.length; i++) {
		var subs = keys[i].split("=");
		final = (final == "" ? "" : ";") + subs[0] + "=" + list[keys[i]] + "=" + subs[2];
	}
	return final;
}
//-------------------------------------------------------------------------------------------------------------
// put the manifest string together
// blank entries in all but the item we are processing the commodity for
this.$buildMergeManifest = function (commodity, extra) {
	var cte = worldScripts.CargoTypeExtension;
	var goodsList = cte.cargoTypes;
	var serial = "";
	for (var i = 0; i < goodsList.length; i++) {
		if (goodsList[i] == commodity) {
			serial += extra;
		}
		if (i + 1 < goodsList.length) {
			serial += "/";
		}
	}
	return serial;
}
//-------------------------------------------------------------------------------------------------------------
// used to sell a particular item out of the storage array
this.$sellEquipment = function $sellEquipment(itemIndex) {
	var item = this._storage[itemIndex];
	var info = EquipmentInfo.infoForKey(item.equipmentKey);
	var stn = player.ship.dockedStation;
	var sellcost = info.price / 10 * (item.status === "EQUIPMENT_DAMAGED" ? 0.5 : 1) * (item.equipmentKey.indexOf("EQ_WEAPON") >= 0 ? 1 : this._refundPct);
	player.credits += sellcost;
	// give the player an email, if installed
	var ga = worldScripts.GalCopAdminServices;
	if (ga) {
		var email = worldScripts.EmailSystem;
		var cost = expandDescription("[storage-total-refund]", { amount: formatCredits(sellcost, true, true) });
		var msg = expandDescription("[storage-sell-email]", {
			storename: expandDescription(this._compNames[item.system % 16]),
			systemname: item.systemName,
			equipname: (item.equipmentKey === "EQ_PASSENGER_BERTH" ? expandDescription("[storage-passenger-berth]") : info.name),
			equipcost: cost
		});
		email.$createEmail({
			sender: expandDescription(this._compNames[item.system % 16]),
			subject: expandDescription("[storage-email-subject-removing]"),
			date: global.clock.adjustedSeconds,
			sentFrom: item.system,
			message: msg,
			expiryDays: ga._defaultExpiryDays
		});
	}
	player.consoleMessage(expandDescription("[storage-refund]", { refund: formatCredits(sellcost, true, true) }));
	this._storage.splice(itemIndex, 1);
}
//-------------------------------------------------------------------------------------------------------------
this.$transferFailed = function $transferFailed(choice) {
	this.$viewStoredItems();
}
//-------------------------------------------------------------------------------------------------------------
this.$sellOrStoreDamaged = function $sellOrStoreDamaged(choice) {
	if (choice == null) return;
	var p = player.ship;
	var itemIndex = this.$getStorageIndex(this._selectedItem);
	var item = this._storage[itemIndex];
	if (choice === "01_SELL") {
		// this is a swap out replacement to a damaged item, and sell the damaged one
		p.setEquipmentStatus(item.equipmentKey, "EQUIPMENT_OK");
		item.status = "EQUIPMENT_DAMAGED";
		player.consoleMessage(expandDescription("[storage-damaged-replaced-sold]"));
		this.$sellEquipment(itemIndex);
	}
	if (choice === "02_STORE") {
		// just change the status of each equipment item
		p.setEquipmentStatus(item.equipmentKey, "EQUIPMENT_OK");
		item.status = "EQUIPMENT_DAMAGED";
		player.consoleMessage(expandDescription("[storage-damaged-replaced]"));
	}
	this.$viewStoredItems();
	return;
}
//-------------------------------------------------------------------------------------------------------------
// calculates the storage cost for a single item of equipment, based on its size/weight profile
this.$calculateStorageCost = function $calculateStorageCost(space, weight, commodityType) {
	var calc = 0;
	if (commodityType != null) {
		space = this.$cargoSpaceRequired(commodityType, weight);
		switch (commodityType) {
			case "kg":
				weight /= 1000;
				break;
			case "g":
				weight /= 1000000;
				break;
		}
	}
	calc = space + (weight > space ? weight - space : 0);
	if (calc <= 0) calc = 0.1;
	return calc;
}
//-------------------------------------------------------------------------------------------------------------
this.$salesRep = function $salesRep() {
	if (this._salesRep === "") {
		this._salesRep = randomName() + " " + randomName();
	}
	return this._salesRep;
}
//-------------------------------------------------------------------------------------------------------------
// calculates the total daily cost of all equipment currently stored throughout the galaxy
this.$calculateTotalDailyCost = function $calculateTotalDailyCost() {
	var cost = 0;
	for (var i = 0; i < this._storage.length; i++) {
		var item = this._storage[i];
		var add = true;
		if (item.equipmentKey != "") {
			var equip = EquipmentInfo.infoForKey(item.equipmentKey);
			if (!equip) add = false;
		}
		if (add === true) cost += this.$calculateStorageCost(item.space, item.weight);
	}
	return cost;
}
//-------------------------------------------------------------------------------------------------------------
// returns the current daily cost for the gal/sys ID combination
this.$calculateSystemCost = function $calculateSystemCost(galaxyNum, systemID) {
	var cost = 0;
	for (var i = 0; i < this._storage.length; i++) {
		var item = this._storage[i];
		if (item.system === systemID && item.galaxy === galaxyNum) {
			var add = true;
			if (item.equipmentKey != "") {
				var equip = EquipmentInfo.infoForKey(item.equipmentKey);
				if (!equip) add = false;
			}
			if (add === true) cost += this.$calculateStorageCost(item.space, item.weight);
		}
	}
	return cost;
}
//-------------------------------------------------------------------------------------------------------------
// returns a dictionary of gal/sys id's with the corresponding name of the system where the player currently has equipment stored
this.$storageSystemList = function $storageSystemList() {
	var data = {};
	for (var i = 0; i < this._storage.length; i++) {
		var item = this._storage[i];
		var add = true;
		if (item.equipmentKey != "") {
			var equip = EquipmentInfo.infoForKey(item.equipmentKey);
			if (!equip) add = false;
		}
		if (add === true) data[item.galaxy + "_" + item.system] = item.systemName;
	}
	return data;
}
//-------------------------------------------------------------------------------------------------------------
// counts the number of equipment items stored at the selected gal/sys 
this.$storageSystemCount = function $storageSystemCount(galaxyNum, systemID) {
	var count = 0;
	for (var i = 0; i < this._storage.length; i++) {
		var item = this._storage[i];
		if (item.system === systemID && item.galaxy === galaxyNum) {
			if (item.equipmentKey != "") {
				var equip = EquipmentInfo.infoForKey(item.equipmentKey);
				if (equip) count += 1;
			}
			if (item.commodity != "") {
				count += item.weight;
			}
		}
	}
	return count;
}
//-------------------------------------------------------------------------------------------------------------
// returns true if the station has equipment storage facilities, otherwise false
this.$stationHasStorageFacilities = function $stationHasStorageFacilities(station) {
	if (station.isMainStation) return true;
	return false;
}
//-------------------------------------------------------------------------------------------------------------
// returns the count of the specific equipment key stored at a station (in case the player decides to store multiples of the same item)
this.$countItemAtStation = function $countItemAtStation(itemKey) {
	var count = 0;
	for (var i = 0; i < this._storage.length; i++) {
		if (this._storage[i].galaxy === galaxyNumber && this._storage[i].system === system.ID) {
			if (this._storage[i].equipmentKey === itemKey) {
				var equip = EquipmentInfo.infoForKey(this._storage[i].equipmentKey);
				if (equip) count += 1;
			}
			if (this._storage[i].commodity === itemKey) {
				count += this._storage[i].weight;
			}
		}
	}
	return count;
}
//-------------------------------------------------------------------------------------------------------------
this.$getStorageIndex = function $getStorageIndex(datakey) {
	var data = datakey.split("|");
	var gal = parseInt(data[0]);
	var sys = parseInt(data[1]);
	var eq = data[2];
	var idx = parseInt(data[3]);
	for (var i = 0; i < this._storage.length; i++) {
		if (this._storage[i].galaxy === gal && this._storage[i].system === sys && (this._storage[i].equipmentKey === eq || this._storage[i].commodity === eq) && this._storage[i].index === idx) return i;
	}
	return -1;
}
//-------------------------------------------------------------------------------------------------------------
// returns a weighted average danger level for a particular route
this.$checkRouteDangerLevel = function $checkRouteDangerLevel(route) {
	var danger = 0;
	var count = 0;
	for (var i = 0; i < route.route.length; i++) {
		var sys = System.infoForSystem(galaxyNumber, route.route[i]);
		danger += (7 - sys.government);
		count += 1;
		if (sys.government === 0) {
			danger += (7 - sys.government);
			count += 1;
		}
		if (sys.government <= 1) {
			danger += (7 - sys.government);
			count += 1;
		}
		if (sys.government <= 2) {
			danger += (7 - sys.government);
			count += 1;
		}
		if (sys.government <= 3) {
			danger += (7 - sys.government);
			count += 1;
		}
		if (sys.government <= 4) {
			danger += (7 - sys.government);
			count += 1;
		}
		if (sys.government <= 5) {
			danger += (7 - sys.government);
			count += 1;
		}
	}
	danger = parseInt(danger / count);
	return danger;
}
//-------------------------------------------------------------------------------------------------------------
// returns a count of the number of systems where this good is illegal
this.$checkRouteIllegalLevel = function $checkRouteIllegalLevel(rt, commodity) {
	var si = worldScripts.Smugglers_Illegal;
	if (!si) return 0;
	var illegal = 0;
	for (var i = 0; i < rt.route.length; i++) {
		if (si.$isGoodIllegal(rt.route[i], commodity) === true) illegal += 1;
	}
	return illegal;
}
//-------------------------------------------------------------------------------------------------------------
// returns the player's target system (1.80) or the next jump to their target system (1.82)
this.$playerTargetSystem = function $playerTargetSystem() {
	if (player.ship.hasOwnProperty("nextSystem")) return player.ship.nextSystem;
	var target = player.ship.targetSystem;
	if (oolite.compareVersion("1.81") < 0 && player.ship.hasEquipmentProviding("EQ_ADVANCED_NAVIGATIONAL_ARRAY") === true) {
		// in 1.81 or greater, the target system could be more than 7 ly away. It becomes, essentially, the final destination.
		// there could be multiple interim stop points between the current system and the target system.
		// the only way to get this info is to recreate a route using the same logic as entered on the ANA, and pick item 1
		// from the list. That should be the next destination in the list.
		if (system.ID === -1) {
			var myRoute = System.infoForSystem(galaxyNumber, this._lastSource).routeToSystem(System.infoForSystem(galaxyNumber, target), player.ship.routeMode);
		} else {
			var myRoute = System.infoForSystem(galaxyNumber, global.system.ID).routeToSystem(System.infoForSystem(galaxyNumber, target), player.ship.routeMode);
		}
		if (myRoute && myRoute.route.length >= 1) {
			target = myRoute.route[1];
		}
	}
	return target;
}
//-------------------------------------------------------------------------------------------------------------
// returns a string containing the hours, minutes (and possibly seconds) remaining until the expiry time is reached
this.$getTimeRemaining = function $getTimeRemaining(expiry, hoursOnly) {
	var diff = expiry - clock.adjustedSeconds;
	var result = "";
	if (diff > 0) {
		var hours = Math.floor(diff / 3600);
		var mins = Math.floor((diff - (hours * 3600)) / 60);
		var secs = Math.floor(diff - (hours * 3600) - (mins * 60));
		// special case - reduce 1 hour down to mins
		if (hours === 1 && mins < 40) {
			hours = 0;
			mins += 60;
		}
		// special case - reduce 1 min down to secs
		if (hours === 0 && mins === 1 && secs < 40) {
			mins = 0;
			secs += 60;
		}
		if (hoursOnly && hoursOnly === true && mins > 30 && hours > 1) hours += 1;
		if (hours > 0) result += hours + " " + expandDescription("[storage-hours]");
		if ((hoursOnly == null || hoursOnly === false) || (hours === 0 && mins > 0)) {
			if (mins > 0) result += (result === "" ? "" : " ") + mins + " " + expandDescription("[storage-minutes]");
		}
		if ((hoursOnly == null || hoursOnly === false) || (hours === 0 && mins === 0 && secs > 0)) {
			if (hours === 0 && secs > 0) result += (result === "" ? "" : " ") + secs + " " + expandDescription("[storage-seconds]");
		}
	} else {
		result = expandDescription("[storage-overdue]");
	}
	return result;
}
//-------------------------------------------------------------------------------------------------------------
// check for any in transit arrivals
this.$checkForArrivals = function $checkForArrivals(docking) {
	var si = worldScripts.Smugglers_Illegal;
	var mapOn = false;
	var finalText = "";
	// any in transit arrivals?
	for (var i = this._storage.length - 1; i >= 0; i--) {
		if (this._storage[i].inTransit === true) {
			var item = this._storage[i];
			var equip = null;
			var cargo = "";
			if (item.equipmentKey != "") equip = EquipmentInfo.infoForKey(item.equipmentKey);
			if (item.commodity != "" && this._commodities.indexOf(item.commodity) >= 0) cargo = item.commodity;
			// only move valid equipment items
			if (equip || cargo != "") {
				if (mapOn === false) {
					this.$revealMap();
					mapOn = true;
				}
				// our starting system
				var info1 = System.infoForSystem(galaxyNumber, item.transitSystem);
				// our ultimate destination
				var info2 = System.infoForSystem(galaxyNumber, item.transitDestination);
				// route between the two
				var route1 = info1.routeToSystem(info2);
				// now work out the first step of this complete route
				var info3 = System.infoForSystem(galaxyNumber, route1.route[1]);
				// get the route between info1 (start) and this new point
				var route2 = info1.routeToSystem(info3);
				if (item.transitStart + (route2.time * 3600) < clock.adjustedSeconds) {
					if (Math.random() > ((info3.government + 1) / 9) && Math.random() > ((info3.government + 1) / 9) && Math.random() > ((info3.government + 1) / 9)) {
						var sellcost = 0;
						var extra = "";
						var text = "";
						extra = expandDescription("[storage-insurance-pays]", { amount: formatCredits(sellcost, true, true) });
						if (equip && equip.techLevel <= 14) {
							sellcost = (equip.price / 10 * (item.status === "EQUIPMENT_DAMAGED" ? 0.5 : 1) * 0.1);
							text = expandDescription("[storage-equip-destroyed]", { item: (item.equipmentKey === "EQ_PASSENGER_BERTH" ? expandDescription("[storage-passenger-berth-lower]") : equip.name.toLowerCase()), sys: info2.name, extra: extra });
						}
						if (cargo != "") {
							sellcost = ((system.mainStation.market[cargo].price / 10) * 0.1) * item.weight;
							text = expandDescription("[storage-equip-destroyed]", { item: item.weight + this.$getCommodityUnit(item.commodity) + " × " + displayNameForCommodity(item.commodity), sys: info2.name, extra: extra });
						}
						// tell the player via an arrival report messages
						if (docking) {
							player.addMessageToArrivalReport(text);
						} else {
							finalText += text + "\n";
						}
						this._storage.splice(i, 1);
					} else {
						// move the ship along the plot
						item.transitSystem = info3.systemID;
						// add some time
						item.transitStart += (route2.time * 3600) + 3600;
						// check for illegal goods
						if (si && cargo != "") {
							// player may have paid a premium to allow for the shipping commander to bribe official's, but that doesn't mean it's going to work!
							if (si.$isGoodIllegal(item.transitSystem, item.commodity) === true) {
								// attempt a bribe on player's behalf - change player amount. if they can't afford it, remove cargo
								// 2 tries
								var chance = worldScripts.Smugglers_DockMaster._bribeChance[system.ID];
								var result = false;
								for (var tries = 1; tries <= 2; tries++) {
									var bribe_attempt = Math.random();
									if (bribe_attempt >= chance) {
										result = true;
										break;
									}
								}
								if (result === false) {
									// tell the player via an arrival report messages
									var text = expandDescription("[storage-cargo-caught]", { item: item.weight + this.$getCommodityUnit(item.commodity) + " × " + displayNameForCommodity(item.commodity), sys: info3.name, extra: extra });
									if (docking) {
										player.addMessageToArrivalReport(text);
									} else {
										finalText += text + "\n";
									}
									this._storage.splice(i, 1);
									continue;
								}
							}
						}
						if (item.transitSystem === item.transitDestination) {
							// arrived! reset transit data
							item.system = item.transitDestination;
							item.systemName = info2.name;
							item.inTransit = false;
							item.transitSystem = -1;
							item.transitDestination = -1;
							item.transitETA = 0;
							item.transitStart = 0;
						}
					}
				}
			}
		}
	}
	if (mapOn === true) {
		this.$restoreMap();
	}
	if (finalText != "") {
		this._deliveryText = finalText;
	}
}
//-------------------------------------------------------------------------------------------------------------
// returns the cost of transferring an item to local storage, based on the total distance, number of jumps, the danger level and the number of illegal systems
this.$calculateTransferCost = function $calculateTransferCost(dist, jumps, danger, illegal) {
	// cargo only: we add a premium for bribes if there are any systems that consider the cargo illegal
	// for equipment, illegal should always be zero
	return (((dist / jumps) * 3) * (danger + 1)) * 10 + (illegal * (system.scrambledPseudoRandomNumber(illegal) * 500 + 500));
}
//-------------------------------------------------------------------------------------------------------------
// gets the commodity type for a particular commodity (t, kg or g)
this.$getCommodityUnit = function $getCommodityUnit(generic) {
	if (system.isInterstellarSpace) {
		var m = player.ship.manifest;
		if (m[generic] > 0) {
			for (var i = 0; i < m.list.length; i++) {
				if (m.list[i].commodity == generic) return m.list[i].unit;
			}
		} else {
			return "t";
		}
	}
	var types = ["t", "kg", "g"];
	return types[system.mainStation.market[generic].quantity_unit];
}
//-------------------------------------------------------------------------------------------------------------
// returns the minimum amount of space required for a particular cargo type (kg, g or t) and weight
this.$cargoSpaceRequired = function $cargoSpaceRequired(cargoType, weight) {
	var spcwt = 0;
	switch (cargoType) {
		case "kg":
			spcwt = parseInt(weight / 1000);
			break;
		case "g":
			spcwt = parseInt(weight / 1000000);
			break;
		default:
			spcwt = weight;
			break;
	}
	if (spcwt === 0) spcwt = 1;
	return spcwt;
}
//-------------------------------------------------------------------------------------------------------------
// compiles a list of sample prices for all the cargo/system combinations we have in storage
this.$compileSamplePrices = function $compileSamplePrices() {
	this._storedPrices.length = 0;
	var sc = worldScripts.Smugglers_Contracts;
	if (system.ID === -1) return;
	for (var i = 0; i < this._storage.length; i++) {
		if (this._storage[i].commodity != "" && this._storage[i].galaxy === galaxyNumber && this._storage[i].system != system.ID) {
			var sys = System.infoForSystem(galaxyNumber, this._storage[i].system);
			var prc = 0;
			if (sc) {
				prc = sc.$priceForCommodity(this._storage[i].commodity, sys);
			} else {
				prc = (sys.samplePrice(this._storage[i].commodity) / 10);
			}
			this._storedPrices.push({
				system: this._storage[i].system,
				commodity: this._storage[i].commodity,
				price: prc
			});
		}
	}
	sc = null
}
//-------------------------------------------------------------------------------------------------------------
// returns the price for a particular system/commoodity combo
this.$getSamplePrice = function $getSamplePrice(sysID, commodity) {
	for (var i = 0; i < this._storedPrices.length; i++) {
		if (this._storedPrices[i].system === sysID && this._storedPrices[i].commodity === commodity) return this._storedPrices[i].price;
	}
	// if we get here, the specific system/commodity pair wan't matched, so add a new one to the array and return the new price
	var sc = worldScripts.Smugglers_Contracts;
	var sys = System.infoForSystem(galaxyNumber, sysID);
	var prc = 0;
	if (sc) {
		prc = sc.$priceForCommodity(commodity, sys);
	} else {
		prc = (sys.samplePrice(commodity) / 10);
	}
	// add it to the array so we don't have to keep looking it up
	this._storedPrices.push({
		system: sysID,
		commodity: commodity,
		price: prc
	});
	return prc;
}
//-------------------------------------------------------------------------------------------------------------
// returns true if a HUD with allowBigGUI is enabled, otherwise false
this.$isBigGuiActive = function $isBigGuiActive() {
	if (oolite.compareVersion("1.83") <= 0) {
		return player.ship.hudAllowsBigGui;
	} else {
		var bigGuiHUD = ["XenonHUD.plist", "coluber_hud_ch01-dock.plist"]; // until there is a property we can check, I'll be listing HUD's that have the allow_big_gui property set here
		if (bigGuiHUD.indexOf(player.ship.hud) >= 0) {
			return true;
		} else {
			return false;
		}
	}
}
//-------------------------------------------------------------------------------------------------------------
// appends space to currentText to the specified length in 'em'
this.$padTextRight = function $padTextRight(currentText, desiredLength, leftSwitch) {
	if (currentText == null) currentText = "";
	var hairSpace = String.fromCharCode(31);
	var ellip = "…";
	var currentLength = defaultFont.measureString(currentText.replace(/%%/g, "%"));
	var hairSpaceLength = defaultFont.measureString(hairSpace);
	// calculate number needed to fill remaining length
	var padsNeeded = Math.floor((desiredLength - currentLength) / hairSpaceLength);
	if (padsNeeded < 1) {
		// text is too long for column, so start pulling characters off
		var tmp = currentText;
		do {
			tmp = tmp.substring(0, tmp.length - 2) + ellip;
			if (tmp === ellip) break;
		} while (defaultFont.measureString(tmp.replace(/%%/g, "%")) > desiredLength);
		currentLength = defaultFont.measureString(tmp.replace(/%%/g, "%"));
		padsNeeded = Math.floor((desiredLength - currentLength) / hairSpaceLength);
		currentText = tmp;
	}
	// quick way of generating a repeated string of that number
	if (!leftSwitch || leftSwitch === false) {
		return currentText + new Array(padsNeeded).join(hairSpace);
	} else {
		return new Array(padsNeeded).join(hairSpace) + currentText;
	}
}
//-------------------------------------------------------------------------------------------------------------
// appends space to currentText to the specified length in 'em'
this.$padTextLeft = function $padTextLeft(currentText, desiredLength) {
	return this.$padTextRight(currentText, desiredLength, true);
}
//-------------------------------------------------------------------------------------------------------------
this.$pageHeader = function $pageHeader() {
	if (this._sc_core) {
		return this._sc_core.$pageHeader("store");
	} else {
		return "";
	}
}
//-------------------------------------------------------------------------------------------------------------
// player the buy/sell sound effects
this.$playSound = function $playSound(soundtype) {
	var mySound = new SoundSource;
	switch (soundtype) {
		case "buy":
			mySound.sound = "[buy-commodity]";
			break;
		case "sell":
			mySound.sound = "[sell-commodity]";
			break;
	}
	mySound.loop = false;
	mySound.play();
}
//-------------------------------------------------------------------------------------------------------------
// routine to completely reveal the system map (if parts have been concealed) to ensure route calculations work
this.$revealMap = function $revealMap() {
	this._holdConcealment = [];
	this._unconcealmentActive = true;
	//if (this._debug) log(this.name, "system map uncovered");
	for (var i = 0; i < 256; i++) {
		var sys = System.infoForSystem(galaxyNumber, i);
		var c = 0;
		if (sys.concealment) c = sys.concealment;
		this._holdConcealment.push(c);
		if (c != 0) sys.setProperty(2, "concealment", 0);
	}
}
//-------------------------------------------------------------------------------------------------------------
// routine to restore system map (if parts had been concealed) after route calculations have completed
this.$restoreMap = function $restoreMap() {
	if (this._holdConcealment.length === 0) return;
	for (var i = 0; i < this._holdConcealment.length; i++) {
		var sys = System.infoForSystem(galaxyNumber, i);
		if (this._holdConcealment[i] != 0) sys.setProperty(2, "concealment", this._holdConcealment[i]);
	}
	//if (this._debug) log(this.name, "system map restored");
	this._unconcealmentActive = false;
}
//-------------------------------------------------------------------------------------------------------------
// returns true if the passed equipment key is required by another piece of installed equipment, otherwise false
this.$equipmentIsRequired = function $equipmentIsRequired(equipmentKey) {
	// todo: look at requiresAnyEquipment as well
	var p = player.ship;
	var eq = p.equipment;
	var result = false;
	// special case for passengers: make sure we always have enough berths for the number of passengers currently on board.
	if (equipmentKey === "EQ_PASSENGER_BERTH") {
		if (p.passengerCount > 0 && p.passengerCapacity <= p.passengerCount) {
			result = true;
		}
	} else {
		for (var i = 0; i < eq.length; i++) {
			// special case that isn't listed in equipment config
			//if (equipmentKey === "EQ_SHIELD_BOOSTER" && eq[i].equipmentKey === "EQ_NAVAL_SHIELD_BOOSTER") {
			//	result = true;
			//	break;
			// }
			var info = EquipmentInfo.infoForKey(eq[i].equipmentKey);
			if (info.requiresEquipment) {
				if (info.requiresEquipment.indexOf(equipmentKey) >= 0 ||
					// some special cases for ShipConfig items
					(this._sc_core &&
						((equipmentKey.indexOf("EQ_FUEL_SCOOPS") >= 0 && info.requiresEquipment.indexOf("EQ_FUEL_SCOOPS") >= 0) ||
							(equipmentKey.indexOf("EQ_FUEL_INJECTION") >= 0 && info.requiresEquipment.indexOf("EQ_FUEL_INJECTION") >= 0) ||
							(equipmentKey.indexOf("EQ_HEAT_SHIELD") >= 0 && info.requiresEquipment.indexOf("EQ_HEAT_SHIELD") >= 0))
					)) {
					result = true;
					break;
				}
			}
		}
	}
	return result;
} |