| Scripts/shipcomparison.js | "use strict";
this.name = "ShipComparison";
this.author = "phkb";
this.copyright = "2017 phkb";
this.description = "Routines for selecting ships and displaying details of them side-by-side.";
this.license = "CC BY-NC-SA 4.0";
this._lastChoice = "";
this._shipList = [];
this._selectedList = [];
this._direction = 1;
this._itemColor = "yellowColor";
this._menuColor = "orangeColor";
this._exitColor = "yellowColor";
this._disabledColor = "darkGrayColor";
this._hideListBasic = expandDescription("[shipcomp_hidden_ships]").split("|");
this._hideListAll = ["Constrictor"];
this._shipConfig = false;
this._resetData = false;
this._clearShips = false;
this._trueValues = ["yes", "1", 1, "true", true];
this._exitScreen = "GUI_SCREEN_INTERFACES";
this._limitList = false;
this._switch = false;
this._shipMassExceptions = {
	"Cobra Mark III": 214737.6875,
};
// configuration settings for use in Lib_Config
this._compConfig = {
	Name: this.name,
	Alias: expandDescription("[shipcomp_config_alias]"),
	Display: expandDescription("[shipcomp_config_display]"),
	Alive: "_compConfig",
	Notify: "$onChange",
	Bool: {
		B0: { Name: "_resetData", Def: false, Desc: expandDescription("[shipcomp_reset_data]") },
		B1: { Name: "_clearShips", Def: false, Desc: expandDescription("[shipcomp_clear_selections]") },
		Info: expandDescription("[shipcomp_config_bool_info]")
	}
};
// set with this._override[data key] = "New ship name";
this._override = {};
// this is a holding array for equipment space data for ships, if ShipConfig is installed
this._equipSpace = [];
this._mass = {};
// element 1: extra cargo
// element 2: weapon facings
this._extraData = {
};
//-------------------------------------------------------------------------------------------------------------
this.$onChange = function () {
	if (this._resetData === true) {
		this._equipSpace = [];
		this._resetData = false;
		player.consoleMessage(expandDescription("[shipcomp_reset]"));
	}
	if (this._clearShips === true) {
		this._selectedList = [];
		this._clearShips = false;
		player.consoleMessage(expandDescription("[shipcomp_clear]"));
	}
}
//-------------------------------------------------------------------------------------------------------------
this.startUpComplete = function () {
	// register our settings, if Lib_Config is present
	if (worldScripts.Lib_Config) worldScripts.Lib_Config._registerSet(this._compConfig);
	if (worldScripts.ShipConfiguration_Core && worldScripts.ShipConfiguration_Core.$getEquipmentSpaceFromShipKey) this._shipConfig = true
	// set up the interface screen, if required
	this.$initInterface(player.ship.dockedStation);
	// load any equipment space calculations that have been done previously
	if (missionVariables.ShipComparison_EquipSpace) {
		this._equipSpace = JSON.parse(missionVariables.ShipComparison_EquipSpace);
		this.$cleanUpEquipSpace();
	}
	if (missionVariables.ShipComparison_Reset) delete missionVariables.ShipComparison_Reset;
	if (oolite.compareVersion("1.91") <= 0) {
		setExtraGuiScreenKeys(this.name, {
			guiScreen: "GUI_SCREEN_SHIPYARD",
			registerKeys: { "sckey1": [{ key: "c", mod1: true }] },
			callback: this.$directOpen.bind(this)
		});
		if (worldScripts.ContextualHelp) {
			worldScripts.ContextualHelp.$addHelpTextToGuiScreen(this.name, "GUI_SCREEN_SHIPYARD", expandDescription("[shipcomp_help_keys]"));
		}
	}
}
//-------------------------------------------------------------------------------------------------------------
this.playerWillSaveGame = function () {
	if (this._equipSpace.length > 0) {
		// if we've gone to the trouble of calculating equipment space, we'll retrieve the results so that we keep the speed of lookup values
		missionVariables.ShipComparison_EquipSpace = JSON.stringify(this._equipSpace);
	} else {
		delete missionVariables.ShipComparison_EquipSpace;
	}
}
//-------------------------------------------------------------------------------------------------------------
this.shipDockedWithStation = function (station) {
	// set up interface screen
	this.$initInterface(station);
}
//-------------------------------------------------------------------------------------------------------------
this.missionScreenEnded = function () {
	if (player.ship.hudHidden == true) player.ship.hudHidden = false;
}
//-------------------------------------------------------------------------------------------------------------
// initialise the interface screen in the station
this.$initInterface = function (station) {
	station.setInterface(this.name, {
		title: expandDescription("[shipcomp_interface_title]"),
		category: expandDescription("[interfaces-category-ship-systems]"),
		summary: expandDescription("[shipcomp_interface_summary]"),
		callback: this.$openComparison.bind(this)
	});
}
//-------------------------------------------------------------------------------------------------------------
this.$openComparison = function () {
	function compare(a, b) {
		return a.name > b.name;
	}
	this._exitScreen = "GUI_SCREEN_INTERFACES";
	this._lastChoice = "";
	this.$buildShipList();
	this._shipList.sort(compare);
	if (this._switch == true) {
		this._switch = false;
		this.$showComparisonEquipment();
		return;
	}
	this.$showComparison();
}
//-------------------------------------------------------------------------------------------------------------
this.$directOpen = function () {
	function compare(a, b) {
		return a.name > b.name;
	}
	this._exitScreen = "GUI_SCREEN_SHIPYARD";
	this._lastChoice = "";
	this.$buildShipList();
	this._shipList.sort(compare);
	if (this._switch == true) {
		this._switch = false;
		this.$showComparisonEquipment();
		return;
	}
	this.$showComparison();
}
//-------------------------------------------------------------------------------------------------------------
this.$showComparison = function () {
	var text = "";
	var colShip = 10;
	var colDesc = 10;
	var curChoices = {};
	var sc = null;
	var keyList = [];
	var shipArray = [];
	if (this.$isBigGuiActive() === false) player.ship.hudHidden = true;
	for (var i = 0; i < this._selectedList.length; i++) {
		if (this._selectedList[i] != "") {
			var shipDef = Ship.shipDataForKey(this._selectedList[i]);
			shipArray.push(shipDef);
			keyList.push(this._selectedList[i]);
		}
	}
	colShip = Math.floor(22 / shipArray.length);
	var textList = expandDescription("[shipcomp_properties]").split("|");
	var outList = [];
	for (var i = 0; i < textList.length; i++) outList.push("");
	// select ships to be in comparison (up to 4)
	text = expandDescription("[shipcomp_select]") + ":\n";
	if (this._shipConfig) {
		textList.push(expandDescription("[shipcomp_item_equipspace]"));
		outList.push("");
		sc = worldScripts.ShipConfiguration_Core;
	} else {
		text += "\n";
	}
	var na = expandDescription("[shipcomp_na]")
	for (var ol = 0; ol < textList.length; ol++) {
		outList[ol] = this.$padTextRight(textList[ol] + (textList[ol] != "" ? ":" : ""), colDesc);
		for (var i = 0; i < shipArray.length; i++) {
			var shipDef = shipArray[i];
			var shipName = shipDef.name;
			if (this._override[this._selectedList[i]]) shipName = this._override[this._selectedList[i]];
			switch (textList[ol]) {
				case expandDescription("[shipcomp_item_shiptype]"):
					outList[ol] += this.$padTextRight(shipName, colShip);
					break;
				case expandDescription("[shipcomp_item_price]"):
					if (shipDef._oo_shipyard) {
						outList[ol] += this.$padTextRight(formatCredits(parseInt(shipDef._oo_shipyard.price), false, true), colShip);
					} else {
						log(this.name, expandDescription("[shipcomp_shipyard_error]", { name: shipName, key: keyList[i] }));
					}
					break;
				case expandDescription("[shipcomp_item_speed]"):
					if (this._hideListAll.indexOf(shipName) >= 0) {
						outList[ol] += this.$padTextRight(na, colShip);
					} else {
						outList[ol] += this.$padTextRight((parseFloat(shipDef["max_flight_speed"]) / 1000).toFixed(3) + " " + expandDescription("[shipcomp_ls]"), colShip);
					}
					break;
				case expandDescription("[shipcomp_item_thrust]"):
					if (this._hideListAll.indexOf(shipName) >= 0) {
						outList[ol] += this.$padTextRight(na, colShip);
					} else {
						outList[ol] += this.$padTextRight((parseFloat(shipDef["thrust"]) / 1000).toFixed(3) + " " + expandDescription("[shipcomp_ls]"), colShip);
					}
					break;
				case expandDescription("[shipcomp_item_injectorspeed]"):
					if (this._hideListAll.indexOf(shipName) >= 0) {
						outList[ol] += this.$padTextRight(na, colShip);
					} else {
						var inj = 7;
						if (0 >= oolite.compareVersion("1.81") && shipDef["injector_speed_factor"] != undefined) inj = parseFloat(shipDef["injector_speed_factor"]);
						outList[ol] += this.$padTextRight(((parseFloat(shipDef["max_flight_speed"]) * inj) / 1000).toFixed(3) + " " + expandDescription("[shipcomp_ls]"), colShip);
					}
					break;
				case expandDescription("[shipcomp_item_pitchrollyaw]"):
					if (this._hideListAll.indexOf(shipName) >= 0 || this._hideListBasic.indexOf(shipName) >= 0) {
						outList[ol] += this.$padTextRight(na, colShip);
					} else {
						var yaw = parseFloat(shipDef["max_flight_pitch"]);
						if (shipDef["max_flight_yaw"] != undefined) yaw = parseFloat(shipDef["max_flight_yaw"]);
						outList[ol] += this.$padTextRight(parseFloat(shipDef["max_flight_pitch"]).toFixed(1)
							+ "/" + parseFloat(shipDef["max_flight_roll"]).toFixed(1)
							+ "/" + yaw.toFixed(1), colShip);
					}
					break;
				case expandDescription("[shipcomp_item_weaponfacings]"):
					if (this._hideListAll.indexOf(shipName) >= 0) {
						outList[ol] += this.$padTextRight(na, colShip);
					} else {
						var facings = shipDef["weapon_facings"];
						if (shipDef._oo_shipyard && shipDef._oo_shipyard.weapon_facings != undefined) facings = shipDef._oo_shipyard.weapon_facings;
						if (this._extraData[shipName] != null) facings = this._extraData[shipName][1].toString();
						var wpn = expandDescription("[shipcomp_forward]");
						if (facings === "15") {
							wpn = expandDescription("[shipcomp_all]");
						} else {
							if (" 3 7 11 ".indexOf(" " + facings + " ") >= 0) wpn += ", " + expandDescription("[shipcomp_aft]");
							if (" 5 7 13 ".indexOf(" " + facings + " ") >= 0) wpn += ", " + expandDescription("[shipcomp_port]");
							if (" 9 11 13 ".indexOf(" " + facings + " ") >= 0) wpn += ", " + expandDescription("[shipcomp_starboard]");
						}
						if (facings === "0") wpn = expandDescription("[shipcomp_none]");
						if (facings === na) wpn = na;
						outList[ol] += this.$padTextRight(wpn, colShip);
					}
					break;
				case expandDescription("[shipcomp_item_turrets]"):
					if (this._hideListAll.indexOf(shipName) >= 0) {
						outList[ol] += this.$padTextRight(na, colShip);
					} else {
						var turrets = 0;
						var sub = shipDef["subentities"];
						if (sub && sub.length > 0) {
							for (var j = 0; j < sub.length; j++) {
								if (sub[j].type == "ball_turret") turrets += 1;
							}
						}
						if (turrets > 0) {
							outList[ol] += this.$padTextRight(turrets, colShip);
						} else {
							outList[ol] += this.$padTextRight("", colShip);
						}
					}
					break;
				case expandDescription("[shipcomp_item_maxmissiles]"):
					if (this._hideListAll.indexOf(shipName) >= 0) {
						outList[ol] += this.$padTextRight(na, colShip);
					} else {
						var miss = 0;
						if (shipDef["max_missiles"] != undefined) {
							miss = parseInt(shipDef["max_missiles"]);
						} else {
							miss = parseInt(shipDef["missiles"]);
						}
						outList[ol] += this.$padTextRight(miss, colShip);
					}
					break;
				case expandDescription("[shipcomp_item_maxcargo]"):
					if (this._hideListAll.indexOf(shipName) >= 0) {
						outList[ol] += this.$padTextRight(na, colShip);
					} else {
						var cargo = 0;
						if (shipDef["max_cargo"] != undefined) cargo = parseInt(shipDef["max_cargo"]);
						var exp = "";
						if (shipDef["extra_cargo"] != undefined) {
							exp = shipDef["extra_cargo"];
						}
						if (exp === "") {
							if (shipDef._oo_shipyard) {
								if ((shipDef._oo_shipyard.standard_equipment.extras && shipDef._oo_shipyard.standard_equipment.extras.indexOf("EQ_CARGO_BAY") >= 0) ||
									(shipDef._oo_shipyard.optional_equipment && shipDef._oo_shipyard.optional_equipment.indexOf("EQ_CARGO_BAY") >= 0)) exp = 15;
							}
						}
						if (this._extraData[shipName] != null) exp = this._extraData[shipName][0].toString();
						if (exp === undefined || exp === "0") exp = "";
						outList[ol] += this.$padTextRight(cargo.toString() + (exp != "" ? " (+" + exp + ")" : ""), colShip);
					}
					break;
				case expandDescription("[shipcomp_item_maxenergy]"):
					if (this._hideListAll.indexOf(shipName) >= 0 || this._hideListBasic.indexOf(shipName) >= 0) {
						outList[ol] += this.$padTextRight(na, colShip);
					} else {
						var en = 200;
						if (shipDef["max_energy"] != undefined) en = parseInt(shipDef["max_energy"]);
						if (Math.floor(en / 64) === 0) en = 64;
						outList[ol] += this.$padTextRight(Math.floor(en / 64), colShip);
					}
					break;
				case expandDescription("[shipcomp_item_rechargerate]"):
					if (this._hideListAll.indexOf(shipName) >= 0) {
						outList[ol] += this.$padTextRight(na, colShip);
					} else {
						var rech = 1;
						if (shipDef["energy_recharge_rate"] != undefined) rech = parseFloat(shipDef["energy_recharge_rate"]);
						outList[ol] += this.$padTextRight(rech.toFixed(1), colShip);
					}
					break;
				case expandDescription("[shipcomp_item_hyperspace]"):
					var hyper = expandDescription("[shipcomp_yes]");
					if (shipDef["hyperspace_motor"] != undefined && (shipDef["hyperspace_motor"] === "no" || shipDef["hyperspace_motor"] === "false" || shipDef["hyperspace_motor"] === "0")) hyper = expandDescription("[shipcomp_no]");
					outList[ol] += this.$padTextRight(hyper, colShip);
					break;
				case expandDescription("[shipcomp_item_equipspace]"):
					if (this._hideListAll.indexOf(shipName) >= 0) {
						outList[ol] += this.$padTextRight(na, colShip);
					} else {
						var found = false;
						// check to see if we have already analysed this ship type
						for (var j = 0; j < this._equipSpace.length; j++) {
							if (this._equipSpace[j].shipType == shipName) {
								outList[ol] += this.$padTextRight(this._equipSpace[j].equipSpace + (this._equipSpace[j].cargoSpace != na && this._equipSpace[j].cargoSpace != 0 ? " (+" + this._equipSpace[j].cargoSpace + ")" : ""), colShip);
								found = true;
								break;
							}
						}
						// if we haven't we will need to execute a query against ShipConfig
						if (found === false) {
							var es = sc.$getEquipmentSpaceFromShipKey(this._selectedList[i]);
							outList[ol] += this.$padTextRight(es.equipSpace + (es.cargoSpace != na && es.cargoSpace != 0 ? " (+" + es.cargoSpace + ")" : ""), colShip);
							// store the result so we don't have to run this process again (but only if there was a definite value, not N/A)
							if (es.equipSpace != "N/A") this._equipSpace.push({ shipType: shipName, equipSpace: es.equipSpace, cargoSpace: es.cargoSpace, shipMass: (es.hasOwnProperty("mass") ? es.mass : na) });
						}
					}
					break;
				case expandDescription("[shipcomp_item_mass]"):
					if (this._hideListAll.indexOf(shipName) >= 0) {
						outList[ol] += this.$padTextRight(na, colShip);
					} else {
						// check to see if we have already analysed this ship type
						if (!sc) {
							// if ship config is not installed
							var mass = this._mass[shipName];
							if (!mass) {
								mass = this.$getMassFromShipKey(this._selectedList[i]);
								// store the result so we don't have to do this again
								if (mass && mass > 0) this._mass[shipName] = mass;
							}
							if (!mass || mass == 0) {
								outList[ol] += this.$padTextRight(na, colShip);
							} else {
								outList[ol] += this.$padTextRight(mass.toFixed(0), colShip);
							}
						} else {
							var idx = -1
							var found = false;
							// if ship config is installed
							for (var j = 0; j < this._equipSpace.length; j++) {
								if (this._equipSpace[j].shipType === shipName) {
									idx = j;
									if (this._equipSpace[j].hasOwnProperty("shipMass") && this._equipSpace[j].shipMass != "N/A") {
										outList[ol] += this.$padTextRight(this._equipSpace[j].shipMass.toFixed(0), colShip);
										found = true;
									}
									break;
								}
							}
							// if we haven't we will need to execute a query against ShipConfig
							if (found === false) {
								var es = sc.$getEquipmentSpaceFromShipKey(this._selectedList[i]);
								if (es.hasOwnProperty("mass") && es.mass != "N/A") {
									outList[ol] += this.$padTextRight(es.mass.toFixed(0), colShip);
								} else {
									outList[ol] += this.$padTextRight(na, colShip);
								}
								// store the result so we don't have to run this process again (but only if there was a definite value, not N/A)
								if (es.mass != "N/A" || idx === -1) {
									this._equipSpace.push({ shipType: shipName, equipSpace: es.equipSpace, cargoSpace: es.cargoSpace, shipMass: (es.hasOwnProperty("mass") ? es.mass : na) });
								} else {
									this._equipSpace[idx].shipMass = es.mass;
								}
							}
						}
					}
					break;
			}
		}
	}
	
	for (var i = 0; i < outList.length; i++) {
		text += outList[i] + "\n";
	}
	if (this._selectedList.length === 0) {
		if (this._direction === 1) {
			curChoices["01_CHANGE"] = { text: expandDescription("[shipcomp_change_ship]", { number: this.$numberWord(1), ship: this._shipList[0].name }), color: this._menuColor };
			curChoices["02_CHANGE"] = { text: expandDescription("[shipcomp_change_ship]", { number: this.$numberWord(2), ship: this._shipList[0].name }), unselectable: true, color: this._disabledColor };
			curChoices["03_CHANGE"] = { text: expandDescription("[shipcomp_change_ship]", { number: this.$numberWord(3), ship: this._shipList[0].name }), unselectable: true, color: this._disabledColor };
		} else {
			curChoices["01_CHANGE"] = { text: expandDescription("[shipcomp_change_ship]", { number: this.$numberWord(1), ship: this._shipList[this._shipList.length - 1].name }), color: this._menuColor };
			curChoices["02_CHANGE"] = { text: expandDescription("[shipcomp_change_ship]", { number: this.$numberWord(2), ship: this._shipList[this._shipList.length - 1].name }), unselectable: true, color: this._disabledColor };
			curChoices["03_CHANGE"] = { text: expandDescription("[shipcomp_change_ship]", { number: this.$numberWord(3), ship: this._shipList[this._shipList.length - 1].name }), unselectable: true, color: this._disabledColor };
		}
	} else {
		for (var i = 0; i < this._selectedList.length; i++) {
			for (var j = 0; j < this._shipList.length; j++) {
				if (this._shipList[j].key === this._selectedList[i]) {
					if (this._direction === 1) {
						if (j < this._shipList.length - 1) {
							curChoices["0" + (i + 1) + "_CHANGE"] = { text: expandDescription("[shipcomp_change_ship]", { number: this.$numberWord(i + 1), ship: this._shipList[j + 1].name }), color: this._menuColor };
						} else {
							curChoices["0" + (i + 1) + "_CHANGE"] = { text: expandDescription("[shipcomp_change_ship]", { number: this.$numberWord(i + 1), ship: this._shipList[0].name }), color: this._menuColor };
						}
					} else {
						if (j > 0) {
							curChoices["0" + (i + 1) + "_CHANGE"] = { text: expandDescription("[shipcomp_change_ship]", { number: this.$numberWord(i + 1), ship: this._shipList[j - 1].name }), color: this._menuColor };
						} else {
							curChoices["0" + (i + 1) + "_CHANGE"] = { text: expandDescription("[shipcomp_change_ship]", { number: this.$numberWord(i + 1), ship: this._shipList[this._shipList.length - 1].name }), color: this._menuColor };
						}
					}
				}
			}
		}
		if (this._selectedList.length === 1) {
			if (this._direction === 1) {
				curChoices["02_CHANGE"] = { text: expandDescription("[shipcomp_change_ship]", { number: this.$numberWord(2), ship: this._shipList[0].name }), color: this._menuColor };
				curChoices["03_CHANGE"] = { text: expandDescription("[shipcomp_change_ship]", { number: this.$numberWord(3), ship: this._shipList[0].name }), unselectable: true, color: this._disabledColor };
			} else {
				curChoices["02_CHANGE"] = { text: expandDescription("[shipcomp_change_ship]", { number: this.$numberWord(2), ship: this._shipList[this._shipList.length - 1].name }), color: this._menuColor };
				curChoices["03_CHANGE"] = { text: expandDescription("[shipcomp_change_ship]", { number: this.$numberWord(3), ship: this._shipList[this._shipList.length - 1].name }), unselectable: true, color: this._disabledColor };
			}
		}
		if (this._selectedList.length === 2) {
			if (this._direction === 1) {
				curChoices["03_CHANGE"] = { text: expandDescription("[shipcomp_change_ship]", { number: this.$numberWord(3), ship: this._shipList[0].name }), color: this._menuColor };
			} else {
				curChoices["03_CHANGE"] = { text: expandDescription("[shipcomp_change_ship]", { number: this.$numberWord(3), ship: this._shipList[this._shipList.length - 1].name }), color: this._menuColor };
			}
		}
	}
	curChoices["04_CURRENT"] = { text: "[shipcomp_first_current]", color: this._menuColor };
	if (this._limitList == false) {
		curChoices["04A_LIMIT"] = { text: "[shipcomp_shipyard_only]", color: this._menuColor };
	} else {
		curChoices["04B_LIMIT"] = { text: "[shipcomp_all_ships]", color: this._menuColor };
	}
	curChoices["05_DIRECTION"] = { text: (this._direction === 1 ? "[shipcomp_direction_ascending]" : "[shipcomp_direction_descending]"), color: this._menuColor };
	curChoices["06_EQUIPMENT"] = { text: "[shipcomp_view_equipment]", color: this._itemColor };
	curChoices["99_EXIT"] = { text: "[shipcomp_return]", color: this._itemColor };
	var def = "99_EXIT";
	var opts = {
		screenID: "oolite-ship-compare-map",
		title: expandDescription("[shipcomp_config_alias]"),
		allowInterrupt: true,
		exitScreen: this._exitScreen,
		overlay: { name: "compare-balance.png", height: 546 },
		choices: curChoices,
		initialChoicesKey: (this._lastChoice ? this._lastChoice : def),
		message: text
	};
	mission.runScreen(opts, this.$screenHandler, this);
}
//-------------------------------------------------------------------------------------------------------------
this.$showComparisonEquipment = function () {
	var text = "";
	var colShip = 10;
	var colDesc = 10;
	var curChoices = {};
	var sc = null;
	var keyList = [];
	var shipArray = [];
	if (this.$isBigGuiActive() === false) player.ship.hudHidden = true;
	for (var i = 0; i < this._selectedList.length; i++) {
		if (this._selectedList[i] != "") {
			var shipDef = Ship.shipDataForKey(this._selectedList[i]);
			shipArray.push(shipDef);
			keyList.push(this._selectedList[i]);
		}
	}
	colShip = Math.floor(22 / shipArray.length);
	var textList = expandDescription("[shipcomp_equipment]").split("|");
	var outList = [];
	for (var i = 0; i < textList.length; i++) outList.push("");
	// select ships to be in comparison (up to 4)
	text = expandDescription("[shipcomp_select]") + ":\n";
	for (var ol = 0; ol < textList.length; ol++) {
		outList[ol] = this.$padTextRight(textList[ol] + (textList[ol] != "" ? ":" : ""), colDesc);
		for (var i = 0; i < shipArray.length; i++) {
			var shipDef = shipArray[i];
			var shipName = shipDef.name;
			if (this._override[this._selectedList[i]]) shipName = this._override[this._selectedList[i]];
			switch (textList[ol]) {
				case expandDescription("[shipcomp_item_shiptype]"):
					outList[ol] += this.$padTextRight(shipName, colShip);
					break;
				case expandDescription("[shipcomp_item_tl]"):
					if (this._hideListAll.indexOf(shipName) >= 0) {
						outList[ol] += this.$padTextRight("?", colShip);
					} else {
						if (shipDef._oo_shipyard) {
							outList[ol] += this.$padTextRight(shipDef._oo_shipyard.techlevel, colShip);
						} else {
							log(this.name, expandDescription("[shipcomp_shipyard_error", { name: shipName, key: keyList[i] }));
						}
					}
					break;
				case expandDescription("[shipcomp_item_forwardweapon]"):
					if (this._hideListAll.indexOf(shipName) >= 0) {
						outList[ol] += this.$padTextRight("?", colShip);
					} else {
						outList[ol] += this.$padTextRight(this.$forwardWeaponType(shipDef), colShip);
					}
					break;
				case expandDescription("[shipcomp_item_aftweapon]"):
					if (this._hideListAll.indexOf(shipName) >= 0) {
						outList[ol] += this.$padTextRight("?", colShip);
					} else {
						outList[ol] += this.$padTextRight(this.$aftWeaponType(shipDef), colShip);
					}
					break;
				case expandDescription("[shipcomp_item_ecm]"):
					if (this._hideListAll.indexOf(shipName) >= 0) {
						outList[ol] += this.$padTextRight("?", colShip);
					} else {
						outList[ol] += this.$padTextRight(this.$shipDefHasOption(shipDef, "EQ_ECM"), colShip);
					}
					break;
				case expandDescription("[shipcomp_item_fuelscoops]"):
					if (this._hideListAll.indexOf(shipName) >= 0) {
						outList[ol] += this.$padTextRight("?", colShip);
					} else {
						outList[ol] += this.$padTextRight(this.$shipDefHasOption(shipDef, "EQ_FUEL_SCOOPS"), colShip);
					}
					break;
				case expandDescription("[shipcomp_item_passengers]"):
					if (this._hideListAll.indexOf(shipName) >= 0) {
						outList[ol] += this.$padTextRight("?", colShip);
					} else {
						outList[ol] += this.$padTextRight(this.$shipDefHasOption(shipDef, "EQ_PASSENGER_BERTH"), colShip);
					}
					break;
				case expandDescription("[shipcomp_item_energyunit]"):
					if (this._hideListAll.indexOf(shipName) >= 0) {
						outList[ol] += this.$padTextRight("?", colShip);
					} else {
						outList[ol] += this.$padTextRight(this.$shipDefHasOption(shipDef, "EQ_ENERGY_UNIT"), colShip);
					}
					break;
				case expandDescription("[shipcomp_item_navalunit]"):
					if (this._hideListAll.indexOf(shipName) >= 0) {
						outList[ol] += this.$padTextRight("?", colShip);
					} else {
						outList[ol] += this.$padTextRight(this.$shipDefHasOption(shipDef, "EQ_NAVAL_ENERGY_UNIT"), colShip);
					}
					break;
				case expandDescription("[shipcomp_item_dockingcomp]"):
					if (this._hideListAll.indexOf(shipName) >= 0) {
						outList[ol] += this.$padTextRight("?", colShip);
					} else {
						outList[ol] += this.$padTextRight(this.$shipDefHasOption(shipDef, "EQ_DOCK_COMP"), colShip);
					}
					break;
				case expandDescription("[shipcomp_item_booster]"):
					if (this._hideListAll.indexOf(shipName) >= 0) {
						outList[ol] += this.$padTextRight("?", colShip);
					} else {
						outList[ol] += this.$padTextRight(this.$shipDefHasOption(shipDef, "EQ_SHIELD_BOOSTER"), colShip);
					}
					break;
				case expandDescription("[shipcomp_item_milbooster]"):
					if (this._hideListAll.indexOf(shipName) >= 0) {
						outList[ol] += this.$padTextRight("?", colShip);
					} else {
						outList[ol] += this.$padTextRight(this.$shipDefHasOption(shipDef, "EQ_NAVAL_SHIELD_BOOSTER"), colShip);
					}
					break;
				case expandDescription("[shipcomp_item_galdrive]"):
					if (this._hideListAll.indexOf(shipName) >= 0) {
						outList[ol] += this.$padTextRight("?", colShip);
					} else {
						outList[ol] += this.$padTextRight(this.$shipDefHasOption(shipDef, "EQ_GAL_DRIVE"), colShip);
					}
					break;
				case expandDescription("[shipcomp_item_targetenh]"):
					if (this._hideListAll.indexOf(shipName) >= 0) {
						outList[ol] += this.$padTextRight("?", colShip);
					} else {
						outList[ol] += this.$padTextRight(this.$shipDefHasOption(shipDef, "EQ_SCANNER_SHOW_MISSILE_TARGET"), colShip);
					}
					break;
				case expandDescription("[shipcomp_item_multitarget]"):
					if (this._hideListAll.indexOf(shipName) >= 0) {
						outList[ol] += this.$padTextRight("?", colShip);
					} else {
						outList[ol] += this.$padTextRight(this.$shipDefHasOption(shipDef, "EQ_MULTI_TARGET"), colShip);
					}
					break;
			}
		}
	}
	for (var i = 0; i < outList.length; i++) {
		text += outList[i] + "\n";
	}
	if (this._selectedList.length === 0) {
		if (this._direction === 1) {
			curChoices["01_CHANGE"] = { text: expandDescription("[shipcomp_change_ship]", {number:this.$numberWord(1), ship:this._shipList[0].name}), color: this._menuColor };
			curChoices["02_CHANGE"] = { text: expandDescription("[shipcomp_change_ship]", {number:this.$numberWord(2), ship:this._shipList[0].name}), unselectable: true, color: this._disabledColor };
			curChoices["03_CHANGE"] = { text: expandDescription("[shipcomp_change_ship]", {number:this.$numberWord(3), ship:this._shipList[0].name}), unselectable: true, color: this._disabledColor };
		} else {
			curChoices["01_CHANGE"] = { text: expandDescription("[shipcomp_change_ship]", {number:this.$numberWord(1), ship:this._shipList[this._shipList.length - 1].name}), color: this._menuColor };
			curChoices["02_CHANGE"] = { text: expandDescription("[shipcomp_change_ship]", {number:this.$numberWord(2), ship:this._shipList[this._shipList.length - 1].name}), unselectable: true, color: this._disabledColor };
			curChoices["03_CHANGE"] = { text: expandDescription("[shipcomp_change_ship]", {number:this.$numberWord(3), ship:this._shipList[this._shipList.length - 1].name}), unselectable: true, color: this._disabledColor };
		}
	} else {
		for (var i = 0; i < this._selectedList.length; i++) {
			for (var j = 0; j < this._shipList.length; j++) {
				if (this._shipList[j].key === this._selectedList[i]) {
					if (this._direction === 1) {
						if (j < this._shipList.length - 1) {
							curChoices["0" + (i + 1) + "_CHANGE"] = { text: expandDescription("[shipcomp_change_ship]", {number:this.$numberWord(i + 1), ship:this._shipList[j + 1].name}), color: this._menuColor };
						} else {
							curChoices["0" + (i + 1) + "_CHANGE"] = { text: expandDescription("[shipcomp_change_ship]", {number:this.$numberWord(i + 1), ship: this._shipList[0].name}), color: this._menuColor };
						}
					} else {
						if (j > 0) {
							curChoices["0" + (i + 1) + "_CHANGE"] = { text: expandDescription("[shipcomp_change_ship]", {number:this.$numberWord(i + 1), ship:this._shipList[j - 1].name}), color: this._menuColor };
						} else {
							curChoices["0" + (i + 1) + "_CHANGE"] = { text: expandDescription("[shipcomp_change_ship]", {number:this.$numberWord(i + 1), ship:this._shipList[this._shipList.length - 1].name }), color: this._menuColor };
						}
					}
				}
			}
		}
		if (this._selectedList.length === 1) {
			if (this._direction === 1) {
				curChoices["02_CHANGE"] = { text: expandDescription("[shipcomp_change_ship]", {number:this.$numberWord(2), ship:this._shipList[0].name }), color: this._menuColor };
				curChoices["03_CHANGE"] = { text: expandDescription("[shipcomp_change_ship]", {number:this.$numberWord(3), ship:this._shipList[0].name }), unselectable: true, color: this._disabledColor };
			} else {
				curChoices["02_CHANGE"] = { text: expandDescription("[shipcomp_change_ship]", {number:this.$numberWord(2), ship:this._shipList[this._shipList.length - 1].name }), color: this._menuColor };
				curChoices["03_CHANGE"] = { text: expandDescription("[shipcomp_change_ship]", {number:this.$numberWord(3), ship:this._shipList[this._shipList.length - 1].name }), unselectable: true, color: this._disabledColor };
			}
		}
		if (this._selectedList.length === 2) {
			if (this._direction === 1) {
				curChoices["03_CHANGE"] = { text: expandDescription("[shipcomp_change_ship]", {number:this.$numberWord(3), ship:this._shipList[0].name }), color: this._menuColor };
			} else {
				curChoices["03_CHANGE"] = { text: expandDescription("[shipcomp_change_ship]", {number:this.$numberWord(3), ship:this._shipList[this._shipList.length - 1].name }), color: this._menuColor };
			}
		}
	}
	curChoices["04_CURRENT"] = { text: "[shipcomp_first_current]", color: this._menuColor };
	if (this._limitList == false) {
		curChoices["04A_LIMIT"] = { text: "[shipcomp_shipyard_only]", color: this._menuColor };
	} else {
		curChoices["04B_LIMIT"] = { text: "[shipcomp_all_ships]", color: this._menuColor };
	}
	curChoices["05_DIRECTION"] = { text: (this._direction === 1 ? "[shipcomp_direction_ascending]" : "[shipcomp_direction_descending]"), color: this._menuColor };
	curChoices["06_SHIPSTATS"] = { text: "[shipcomp_general_stats]", color: this._itemColor };
	curChoices["99_EXIT"] = { text: "[shipcomp_return]", color: this._itemColor };
	var def = "99_EXIT";
	var opts = {
		screenID: "oolite-ship-compare-map",
		title: expandDescription("[shipcomp_config_alias]"),
		allowInterrupt: true,
		exitScreen: this._exitScreen,
		overlay: { name: "compare-balance.png", height: 546 },
		choices: curChoices,
		initialChoicesKey: (this._lastChoice ? this._lastChoice : def),
		message: text
	};
	mission.runScreen(opts, this.$screenHandlerEq, this);
}
//-------------------------------------------------------------------------------------------------------------
this.$screenHandler = function (choice) {
	if (!choice) return;
	this._lastChoice = choice;
	switch (choice) {
		case "01_CHANGE":
			this.$getNewItem(0);
			break;
		case "02_CHANGE":
			this.$getNewItem(1);
			break;
		case "03_CHANGE":
			this.$getNewItem(2);
			break;
		case "04_CURRENT":
			var idx = this.$findCurrentShip();
			if (idx >= 0) {
				this._selectedList[0] = this._shipList[idx].key;
			}
			break;
		case "04A_LIMIT":
			this._limitList = true;
			this._selectedList.length = 0;
			if (this._exitScreen == "GUI_SCREEN_INTERFACES") {
				this.$openComparison();
			} else {
				this.$directOpen();
			}
			return;
		case "04B_LIMIT":
			this._limitList = false;
			this._selectedList.length = 0;
			if (this._exitScreen == "GUI_SCREEN_INTERFACES") {
				this.$openComparison();
			} else {
				this.$directOpen();
			}
			return;
		case "06_EQUIPMENT":
			this._lastChoice = "06_SHIPSTATS";
			this.$showComparisonEquipment();
			return;
		case "05_DIRECTION":
			if (this._direction === 1) {
				this._direction = -1;
			} else {
				this._direction = 1;
			}
			break;
	}
	if (choice != "99_EXIT") this.$showComparison();
}
//-------------------------------------------------------------------------------------------------------------
this.$screenHandlerEq = function (choice) {
	if (!choice) return;
	this._lastChoice = choice;
	switch (choice) {
		case "01_CHANGE":
			this.$getNewItem(0);
			break;
		case "02_CHANGE":
			this.$getNewItem(1);
			break;
		case "03_CHANGE":
			this.$getNewItem(2);
			break;
		case "04_CURRENT":
			var idx = this.$findCurrentShip();
			if (idx >= 0) {
				this._selectedList[0] = this._shipList[idx].key;
			}
			break;
		case "04A_LIMIT":
			this._limitList = true;
			this._selectedList.length = 0;
			this._switch = true;
			if (this._exitScreen == "GUI_SCREEN_INTERFACES") {
				this.$openComparison();
			} else {
				this.$directOpen();
			}
			return;
		case "04B_LIMIT":
			this._limitList = false;
			this._selectedList.length = 0;
			this._switch = true;
			if (this._exitScreen == "GUI_SCREEN_INTERFACES") {
				this.$openComparison();
			} else {
				this.$directOpen();
			}
			return;
		case "06_SHIPSTATS":
			this._lastChoice = "06_EQUIPMENT";
			this.$showComparison();
			return;
		case "05_DIRECTION":
			if (this._direction === 1) {
				this._direction = -1;
			} else {
				this._direction = 1;
			}
			break;
	}
	if (choice != "99_EXIT") this.$showComparisonEquipment();
}
//-------------------------------------------------------------------------------------------------------------
this.$getNewItem = function (idx) {
	var cur = "";
	if (this._selectedList.length >= (idx + 1)) {
		cur = this._selectedList[idx];
	}
	if (cur === "") {
		this._selectedList.push(this._shipList[0].key);
	} else {
		for (var i = 0; i < this._shipList.length; i++) {
			if (this._shipList[i].key === cur) {
				if (this._direction === 1) {
					if (i === this._shipList.length - 1) {
						this._selectedList[idx] = this._shipList[0].key;
					} else {
						this._selectedList[idx] = this._shipList[i + 1].key;
					}
				} else {
					if (i === 0) {
						this._selectedList[idx] = this._shipList[this._shipList.length - 1].key;
					} else {
						this._selectedList[idx] = this._shipList[i - 1].key;
					}
				}
				break;
			}
		}
	}
}
//-------------------------------------------------------------------------------------------------------------
this.$findCurrentShip = function () {
	var p = player.ship;
	for (var i = 0; i < this._shipList.length; i++) {
		if (this._shipList[i].name == p.shipClassName) return i;
	}
	return -1;
}
//-------------------------------------------------------------------------------------------------------------
this.$buildShipList = function () {
	var truevalues = this._trueValues;
	var keys = Ship.keysForRole("player");
	this._shipList.length = 0;
	if (this._limitList) {
		var stn = player.ship.dockedStation;
		// get all ships in the shipyard
		var numShips = stn.shipyard.length;
		for (var i = 0; i < numShips; i++) {
			var shp = stn.shipyard[i];
			this.$addItemToArray(shp.ship.name, shp.shipdata_key);
		}
		// make sure the player ship is added, though
		this.$addItemToArray(player.ship.shipClassName, player.ship.dataKey);
	} else {
		for (var i = 0; i < keys.length; i++) {
			var shp = Ship.shipDataForKey(keys[i]);
			// only add records that have a corresponding shipyard entry
			if (shp._oo_shipyard) {
				if (!shp.script_info || !shp.script_info["sc_ignore"] || truevalues.indexOf(shp.script_info["sc_ignore"]) === -1)
					this.$addItemToArray(shp.name, keys[i]);
			}
		}
	}
}
//-------------------------------------------------------------------------------------------------------------
this.$addItemToArray = function (shipName, shipKey) {
	var ovr = this._override[shipKey];
	if (ovr) shipName = ovr;
	for (var i = 0; i < this._shipList.length; i++) {
		if (this._shipList[i].name === shipName) return;
	}
	this._shipList.push({ name: shipName, key: shipKey });
}
//-------------------------------------------------------------------------------------------------------------
this.$numberWord = function (nbr) {
	switch (nbr) {
		case 1: return expandDescription("[shipcomp_first]");
		case 2: return expandDescription("[shipcomp_second]");
		case 3: return expandDescription("[shipcomp_third]");
		case 4: return expandDescription("[shipcomp_fourth]"); // in case we ever add a fourth column!
	}
}
//-------------------------------------------------------------------------------------------------------------
// appends space to currentText to the specified length in 'em'
this.$padTextRight = function $padTextRight(currentText, desiredLength, leftSwitch) {
	if (currentText == null) currentText = "";
	currentText = currentText.toString();
	var space = " ";
	var hairSpace = String.fromCharCode(31);
	var ellip = "…";
	var currentLength = defaultFont.measureString(currentText.replace(/%%/g, "%"));
	var hairSpaceLength = defaultFont.measureString(hairSpace);
	var spaceLength = defaultFont.measureString(space);
	// calculate number needed to fill remaining length
	var spacesNeeded = parseInt(Math.floor((desiredLength - currentLength) / spaceLength));
	if (spacesNeeded < 0) {
		spacesNeeded = 0;
	} else {
		if (!leftSwitch || leftSwitch === false) {
			currentText += new Array(spacesNeeded).join(space);
		} else {
			currentText = new Array(spacesNeeded).join(space) + currentText;
		}
		currentLength = defaultFont.measureString(currentText.replace(/%%/g, "%"));
	}
	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 (currentText, desiredLength) {
	return this.$padTextRight(currentText, desiredLength, true);
}
//-------------------------------------------------------------------------------------------------------------
this.$shipDefHasOption = function (def, eqKey) {
	var found = "";
	if (!def._oo_shipyard) return expandDescription("[shipcomp_data_error]");
	if (def._oo_shipyard.standard_equipment.extras && def._oo_shipyard.standard_equipment.extras.indexOf(eqKey) >= 0) found = expandDescription("[shipcomp_standard]");
	if (found === "" && def._oo_shipyard.optional_equipment && def._oo_shipyard.optional_equipment.indexOf(eqKey) >= 0) found = expandDescription("[shipcomp_optional]");
	return found;
}
//-------------------------------------------------------------------------------------------------------------
this.$forwardWeaponType = function (def) {
	var found = expandDescription("[shipcomp_none]");
	var facings = parseInt(def["weapon_facings"]);
	if (facings == 0) return expandDescription("[shipcomp_na]");
	if (!def._oo_shipyard) return expandDescription("[shipcomp_data_error]");
	if (def._oo_shipyard.standard_equipment.forward_weapon_type && def._oo_shipyard.standard_equipment.forward_weapon_type != "") {
		var eq = EquipmentInfo.infoForKey(def._oo_shipyard.standard_equipment.forward_weapon_type);
		if (eq) {
			found = eq.name;
		} else {
			found = expandDescription("[shipcomp_unknown]");
		}
	}
	return found;
}
//-------------------------------------------------------------------------------------------------------------
this.$aftWeaponType = function (def) {
	var found = expandDescription("[shipcomp_none]");
	var facings = parseInt(def["weapon_facings"]);
	if (facings == 0 || facings == 1) return expandDescription("[shipcomp_na]");
	if (!def._oo_shipyard) return expandDescription("[shipcomp_data_error]");
	if (def._oo_shipyard.standard_equipment.aft_weapon_type && def._oo_shipyard.standard_equipment.aft_weapon_type != "") {
		var eq = EquipmentInfo.infoForKey(def._oo_shipyard.standard_equipment.forward_weapon_type);
		if (eq) {
			found = eq.name;
		} else {
			found = expandDescription("[shipcomp_unknown]");
		}
	}
	return found;
}
//-------------------------------------------------------------------------------------------------------------
this.$cleanUpEquipSpace = function $cleanUpEquipSpace() {
	var newData = [];
	for (var i = 0; i < this._equipSpace.length; i++) {
		var found = false;
		for (var j = 0; j < newData.length; j++) {
			if (newData[j].shipType == this._equipSpace[i].shipType) {
				found = true;
				break;
			}
		}
		if (found === false) {
			newData.push(JSON.parse(JSON.stringify(this._equipSpace[i])));
		}
	}
	this._equipSpace.length = 0;
	this._equipSpace = newData;
}
//-------------------------------------------------------------------------------------------------------------
// external routine to return the mass for a ship key
// we'll create a temporary ship object and use that to get the details
this.$getMassFromShipKey = function $getMassFromShipKey(shipKey) {
	var result = 0;
	// find a spot well away from any possible objects
	if (system.sun) {
		var temppos = system.sun.position.cross(system.mainPlanet.position).direction().multiply(4E9).subtract(system.mainPlanet.position);
	} else {
		var temppos = Vector3D(0, 0, 0);
	}
	// add a ship with the particular key
	var tempship = system.addShips("[" + shipKey + "]", 1, temppos, 0);
	if (tempship != null) {
		// force this ship to be "dead" in space - we don't want it to do anything disruptive!
		tempship[0].switchAI("oolite-nullAI.js");
		result = this.$getShipMass(tempship[0]);
		// clean up our temp ship
		tempship[0].remove(true);
	} else {
		// this could happen if the ship is not allowed to be spawned in this system due to constraints in the shipdata.plist file for the ship
		result = 0;
	}
	return result;
}
//-------------------------------------------------------------------------------------------------------------
// returns the ships mass, either from the exception list, or from the ship itself
this.$getShipMass = function $getShipMass(ship) {
	var mass = this._shipMassExceptions[ship.shipClassName];
	if (!mass) mass = ship.mass;
	return mass;
}
//-------------------------------------------------------------------------------------------------------------
// 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;
		}
	}
}
 |