| Scripts/xenonhud.js | "use strict";
this.name = "XenonHUD";
this.author = "phkb";
this.copyright = "2018 phkb";
this.description = "Controls HUD selector compatibility and inflight GUI screen changes";
this.licence = "CC BY-NC-SA 4.0";
this._wc_fcb = null;
this._hc_fcb = null;
this._lastSwitch = 0;
this._dockingCrosshairs = "xenon_crosshairs_alt5.png";
this._holdFilter = 0; // hold spot for the sunGlareFilter value for the ship
this._destination = -1; // current witchspace destination
this._lastSource = -1; // used to establish the start point for destination system calculations
this._glareFilterInstalled = false; // flag to indicate whether the glare filter OXP is installed
this._glareClarifiedInstalled = false; // flag to indicate whether the glare clarifier OXP is installed
this._glareTimer = null; // timer used to check when the glare filter is turned on
this._doChecks = false; // flag to indicated that the glare filter has been turned on and therefore checks need to be made
this._defaultGlareFilter = 0.4; // default glare filter value provided by this OXP
this._hudVanisherInstalled = false; // flag to indicate whether the HUD Vanisher OXP is installed
this._mode = ""; // HUD mode, either blank for standard, or "_hicontrast" for hicontrast mode
this._amber = false; // controls whether blue (false) or amber (true) display is used
this._crosshairMode = 0; // Crosshair mode: 0 = normal, 1 = off in green/yellow, on in red, 2 = off in green, on in red/yellow
this._simplified = false;
this._dockTimer = null; // timer used when docking to determine if the player has exceeded the allotted time
this._ironHideArmour = false; // flag to indicate that IronHide armour is installed
this._shipConfigArmour = false; // flag to indicate that Ship Configuration armour is installed
this._holdTarget = 0;
this._jumpCountdownStarted = false; // flag used to monitor when a jump countdown has started
this._mapScreenChangeTimer = null;
this._mapScreen = "";
this._disableDockingHUD = false;
this._disableMissileWarning = false;
this._disableCloakIndicator = false;
this._disableFuelLeakWarning = false;
this._removePrimableEquip = false;
this._trueValues = ["yes", "1", 1, "true", true];
this._crosshairList = {
	"EQ_WEAPON_NONE": "xenon_crosshairs_none.png",
	"EQ_WEAPON_PULSE_LASER": "xenon_crosshairs_pulse.png",
	"EQ_WEAPON_BEAM_LASER": "xenon_crosshairs_beam.png",
	"EQ_WEAPON_MINING_LASER": "xenon_crosshairs_mining.png",
	"EQ_WEAPON_MILITARY_LASER": "xenon_crosshairs_military.png"
};
// configuration settings for use in Lib_Config
this._xenonHUDConfig = {
	Name: this.name,
	Alias: "Xenon HUD",
	Display: "Config",
	Alive: "_xenonHUDConfig",
	Notify: "$changeSettings",
	Bool: {
		B0: {
			Name: "_disableDockingHUD",
			Def: false,
			Desc: "Disable docking HUD"
		},
		B1: {
			Name: "_disableMissileWarning",
			Def: false,
			Desc: "Disable missile warning"
		},
		B2: {
			Name: "_disableCloakIndicator",
			Def: false,
			Desc: "Disable cloak indicator"
		},
		B3: {
			Name: "_disableFuelLeakWarning",
			Def: false,
			Desc: "Disable fuel leak warning"
		},
		B4: {
			Name: "_removePrimableEquip",
			Def: false,
			Desc: "Removes primable equipment"
		},
		B5: {
			Name: "_amber",
			Def: false,
			Desc: "Amber color"
		},
		B6: {
			Name: "_simplified",
			Def: false,
			Desc: "Simplified crosshairs"
		},
		Info: "0 - Disable docking HUD; 1 - Disable missile warning; 2 - Disable cloak indicator;\n3 - Disable fuel leak warning; 4 - Remove HUD Mode selector primable equipment;\n5 - Switch amber color on; 6 - Simplified crosshairs;"
	},
	SInt: {
		S0: {
			Name: "_crosshairMode",
			Def: 0,
			Min: 0,
			Max: 2,
			Desc: "HUD Crosshairs mode"
		},
		Info: "0 - Setting 0 = On in all conditions, 1 = On in Red condition only, 2 = On in Red/Yellow condtions"
	}
};
//-------------------------------------------------------------------------------------------------------------
// Allows OXP lasers to use a different crosshair png file
// usage: lcobject is an object containing two properties:
//		laser: 		the name of the equipment key of the laser
//		filename:	the filename of the crosshairs file to use
this.$customCrosshairs = function $customCrosshairs(lcobject) {
	if (!lcobject.laser || lcobject.laser === "") {
		throw "Invalid laser equipment object specified. Make sure the passed object has a 'laser:\"EQ_WEAPON_MYLASER\"' specified.";
	}
	if (!lcobject.filename || lcobject.filename === "") {
		throw "Invalid crosshair filename specified. Make sure the passed object has a 'filename:\"specific_crosshairs.png\"' specified. See readme.txt file for possible filenames.";
	}
	var ch = this._crosshairList;
	ch[lcobject.laser] = lcobject.filename;
}
//=============================================================================================================
// ship interfaces
//-------------------------------------------------------------------------------------------------------------
// for compatibility with HUD selector
this.startUp = function () {
	var p = player.ship;
	var ps = p.script;
	p.hud = this.name + "_docked" + (this._amber === false ? "" : "_amber") + ".plist";
	var h = worldScripts.hudselector;
	if (h) h.$HUDSelectorAddHUD("Xenon HUD", this.name);
	ps._currentCrosshairs = "";
	ps._missiles = [];
	ps._lastCheck = true;
	ps._missileWarning = false;
	ps._missileWarningChanged = false;
	ps._fuelLeakWarning = false;
	ps._playerIsDocking = false;
	ps._mediumTemp = false;
	ps._redoUnderlays = false;
	//ps._wc_fcb = null;
	//ps._previousCrosshairs = null;
	ps.$selectCrosshairs = this.$selectCrosshairs;
	//ps.$weaponsOnlineCheck = this.$weaponsOnlineCheck;
	ps.$checkForIncomingMissiles = this.$checkForIncomingMissiles;
	var sr = worldScripts.ShipRespray;
	if (sr) {
		//sr.$addPreSprayCall(this.name, "$preRespray");
		//sr.$addPostSprayCall(this.name, "$postRespray");
	}
}
//-------------------------------------------------------------------------------------------------------------
this.startUpComplete = function () {
	var p = player.ship;
	if (this.playerBoughtNewShip) this.playerBoughtNewShip(p, 0);
	if (worldScripts.GlareClarifier) {
		this._glareClarifiedInstalled = true;
		this._glareFilterInstalled = false;
	}
	if (worldScripts["HUD Vanisher"]) this._hudVanisherInstalled = true;
	// restore the crosshair mode from mission variables
	if (missionVariables.XenonHUD_CrosshairMode) this._crosshairMode = parseInt(missionVariables.XenonHUD_CrosshairMode);
	this._lastSource = system.ID;
	this.$setCurrentSystem();
	// read flags from saved game
	if (missionVariables.XenonHUD_DisableDockHUD) this._disableDockingHUD = (this._trueValues.indexOf(missionVariables.XenonHUD_DisableDockHUD) >= 0 ? true : false);
	if (missionVariables.XenonHUD_DisableMissWarn) this._disableMissileWarning = (this._trueValues.indexOf(missionVariables.XenonHUD_DisableMissWarn) >= 0 ? true : false);
	if (missionVariables.XenonHUD_DisableFuelLeakWarn) this._disableFuelLeakWarning = (this._trueValues.indexOf(missionVariables.XenonHUD_DisableFuelLeakWarn) >= 0 ? true : false);
	if (missionVariables.XenonHUD_DisableCloakInd) this._disableCloakIndicator = (this._trueValues.indexOf(missionVariables.XenonHUD_DisableCloakInd) >= 0 ? true : false);
	if (missionVariables.XenonHUD_RemoveEquip) this._removePrimableEquip = (this._trueValues.indexOf(missionVariables.XenonHUD_RemoveEquip) >= 0 ? true : false);
	if (missionVariables.XenonHUD_Simplified) this._simplified = (this._trueValues.indexOf(missionVariables.XenonHUD_Simplified) >= 0 ? true : false);
	// remove old flag, if it's still there
	if (missionVariables.XenonHUD_DisableMassLock) delete missionVariables.XenonHUD_DisableMassLock;
	// register our settings, if Lib_Config is present
	if (worldScripts.Lib_Config) {
		worldScripts.Lib_Config._registerSet(this._xenonHUDConfig);
		if (missionVariables.XenonHUD_Amber) this._amber = (this._trueValues.indexOf(missionVariables.XenonHUD_Amber) >= 0 ? true : false);
	}
	if (worldScripts.ShipConfiguration_F3HUDControl) {
		if (player.ship.hud.indexOf(this.name) >= 0) this.$updateShipConfigHUD();
	}
	this.$removeHUDEquipment();
	// small fix for hudselector
	var h = worldScripts.hudselector;
	if (h && h.$BG === "") h.$BG = null;
}
//-------------------------------------------------------------------------------------------------------------
this.playerWillSaveGame = function () {
	missionVariables.XenonHUD_CrosshairMode = this._crosshairMode;
	missionVariables.XenonHUD_DisableDockHUD = this._disableDockingHUD;
	missionVariables.XenonHUD_DisableMissWarn = this._disableMissileWarning;
	missionVariables.XenonHUD_DisableFuelLeakWarn = this._disableFuelLeakWarning;
	missionVariables.XenonHUD_DisableCloakInd = this._disableCloakIndicator;
	missionVariables.XenonHUD_RemoveEquip = this._removePrimableEquip;
	missionVariables.XenonHUD_Amber = this._amber;
	missionVariables.XenonHUD_Simplified = this._simplified;
}
//-------------------------------------------------------------------------------------------------------------
this.guiScreenChanged = function (to, from) {
	var p = player.ship;
	this._mapScreen = from;
	if (from !== "GUI_SCREEN_MAIN") {
		if (this._mapScreenChangeTimer == null || this._mapScreenChangeTimer.isRunning === false) this._mapScreenChangeTimer = new Timer(this, this.$checkForNewDestination, 0.3, 0);
	}
	if (p.status === "STATUS_IN_FLIGHT") {
		// when in flight, switch to the alt hud if the player selects anything other that the main screen
		if (guiScreen === "GUI_SCREEN_MAIN") {
			p.hud = this.name + this._mode + (this._amber === false ? "" : "_amber") + ".plist";
			if (p.script._playerIsDocking === true) p.crosshairs = "crosshairs_docking.plist";
		} else {
			// the alt hud moves the speed, energy and shield bars to the bottom left, with all the other gauges
			// to keep them from getting in the way of the Xenon backgrounds
			p.hud = this.name + "_alt" + (this._amber === false ? "" : "_amber") + ".plist";
		}
	} else {
		if ((guiScreen === "GUI_SCREEN_MARKET" || guiScreen === "GUI_SCREEN_MARKETINFO") && worldScripts["market_observer3"]) {
			// do nothing - allow Market Observer to change the hud
		} else {
			p.hud = this.name + "_docked" + (this._amber === false ? "" : "_amber") + ".plist";
		}
	}
}
//-------------------------------------------------------------------------------------------------------------
this.shipWillLaunchFromStation = function (station) {
	var p = player.ship;
	var ps = p.script;
	// reset the missile monitoring array
	if (!ps._missiles) ps._missiles = [];
	ps._missiles.length = 0;
	ps._massLockOn = false;
	ps._missileWarning = false;
	ps._missileWarningChanged = false;
	ps._fuelLeakWarning = false;
	ps._selectedMissile = "";
	if (p.hasOwnProperty("activeMissile") == false) ps._selectedMissile = "N/A";
	if (p.missiles && p.missiles.length > 0 && p.activeMissile < p.missiles.length && p.hasOwnProperty("activeMissile")) {
		ps._selectedMissile = p.missiles[p.activeMissile].name;
	}
	this.$switchAmberCrosshairs();
	if (!ps.$selectCrosshairs) ps.$selectCrosshairs = this.$selectCrosshairs;
	//if (!ps.$weaponsOnlineCheck) ps.$weaponsOnlineCheck = this.$weaponsOnlineCheck;
	if (!ps.$checkForIncomingMissiles) ps.$checkForIncomingMissiles = this.$checkForIncomingMissiles;
	// oolite 1.85 has a new event to handle the change in weapons online status, so this code only applies to earlier versions
	if (oolite.compareVersion("1.85") > 0) {
		ps._checkForWeaponsOnline = true;
	} else {
		ps._checkForWeaponsOnline = false;
	}
	ps._disableMissileWarning = this._disableMissileWarning;
	ps._disableFuelLeakWarning = this._disableFuelLeakWarning;
	if (this._glareClarifiedInstalled === false && worldScripts["glare filter"] && p.equipmentStatus("EQ_GLARE_FILTER") === "EQUIPMENT_OK") {
		this._glareFilterInstalled = true;
		// force the default glare filter level to be the maximum
		worldScripts["glare filter"].filterPointer = 2;
	}
	// add the viewmode selector and centre equipment items regardless of whether this is the current HUD or not.
	// that way, if HUD selector changes to/from this HUD mid-flight, the right equipment is already installed and the HUD should work as normal
	if (this._removePrimableEquip === false) p.awardEquipment("EQ_XENONHUD_VIEWMODE");
	else p.removeEquipment("EQ_XENONHUD_VIEWMODE");
	if (this._simplified == true) p.removeEquipment("EQ_XENONHUD_CENTRE_CROSSHAIR");
	this.$displayCrosshairs();
	// because there's no way of centring items, we'll move the centred position of the clock based on the width of the font
	var test = defaultFont.measureString("000");
	if (test >= 1.7) {
		p.awardEquipment("EQ_XENONHUD_CLOCK_1")
	} else if (test > 1.4 && test < 1.7) {
		p.awardEquipment("EQ_XENONHUD_CLOCK_2");
	} else {
		p.awardEquipment("EQ_XENONHUD_CLOCK_3");
	}
	var ratio = oolite.gameSettings.gameWindow.width / oolite.gameSettings.gameWindow.height;
	if (ratio < 1.5418) this._mode = "_narrow";
	p.hud = this.name + this._mode + (this._amber === false ? "" : "_amber") + ".plist";
	if (this._glareClarifiedInstalled === false) {
		// make a note of the player's ship filter value
		this._holdFilter = p.sunGlareFilter;
		// only set the filter if the ship's filter level is less than 50%.
		if (this._holdFilter < this._defaultGlareFilter) {
			p.sunGlareFilter = this._defaultGlareFilter;
		}
	}
	// preset the crosshairs
	ps._currentCrosshairs = this.$selectCrosshairs(p.viewDirection);
	this.$setCrosshairs();
	// set up the glare filter check timer
	if (this._glareFilterInstalled) {
		// if the glare filter is installed, start a timer to monitor when it's turned on
		this._glareTimer = new Timer(this, this.$checkGlareFilter, 5, 5);
	}
	this._lastSource = system.ID;
	ps._mediumTemp = false;
	ps._playerIsDocking = false;
	if (p.equipmentStatus("EQ_IRONHIDE") === "EQUIPMENT_OK") this._ironHideArmour = true;
	var sc = worldScripts.ShipConfiguration_Core;
	if (sc) {
		var a = sc._armour;
		if (a && a.length > 0) {
			for (var i = 0; i <= a.length; i++) {
				if (a[i] && p.equipmentStatus(a[i]) === "EQUIPMENT_OK") {
					this._shipConfigArmour = true;
					break;
				}
			}
		}
	}
	// force an update to the HUD so initial armour value can be displayed
	this.shipTakingDamage(0, null, "");
	// tell manual witchspace alignment which color to use for the nav frame
	if (worldScripts.ManualWitchspaceAlignment) {
		var mwa = worldScripts.ManualWitchspaceAlignment;
		if (mwa._userOverride === false) {
			mwa._color = 0;
			if (this._amber === true) mwa._color = 1;
		}
	}
}
//-------------------------------------------------------------------------------------------------------------
this.shipLaunchedFromStation = function(station) {
	/*var ps = player.ship.script;
	if (!ps._weaponsOnlineTimer || ps._weaponsOnlineTimer.isRunning === false) {
		ps._weaponsOnlineTimer = new Timer(ps, ps.$weaponsOnlineCheck, 0.25, 0.25);
	}*/
	this.$setUpWCFCB();
	this.$setUpHCFCB();
	this.$setMFDUnderlays();
}
//-------------------------------------------------------------------------------------------------------------
this.shipDockedWithStation = function (station) {
	var p = player.ship;
	// remove the xenon hud equipment items
	this.$removeHUDEquipment();
	this.$setCurrentSystem();
	// make sure the normal mode is switched back
	if (this._mode != "") this._mode = "";
	p.hud = this.name + "_docked" + (this._amber === false ? "" : "_amber") + ".plist";
	if (this._glareClarifiedInstalled === false) {
		if (this._holdFilter < this._defaultGlareFilter) {
			// restore the filter level to the stored value, just in case the player switches HUD while docked.
			p.sunGlareFilter = this._holdFilter;
		}
	}
	this.$stopTimers();
	this.$stopFCB();
}
//-------------------------------------------------------------------------------------------------------------
this.shipWillDockWithStation = function (station) {
	var p = player.ship;
	var eqList = ["EQ_XENONHUD_CENTRE", "EQ_XENONHUD_CENTRE_CROSSHAIR", "EQ_XENONHUD_TEMP_LOW", "EQ_XENONHUD_TEMP_MEDIUM",
		"EQ_XENONHUD_MFD1", "EQ_XENONHUD_MFD2", "EQ_XENONHUD_MFD3", "EQ_XENONHUD_MFD4", "EQ_XENONHUD_MFD5",
		"EQ_XENONHUD_MFD6", "EQ_XENONHUD_MFD7", "EQ_XENONHUD_MFD8", "EQ_XENONHUD_MFD9", "EQ_XENONHUD_DOCKING", 
		"EQ_XENONHUD_CLOCK_1", "EQ_XENONHUD_CLOCK_2", "EQ_XENONHUD_CLOCK_3"];
	for (var i = 0; i < eqList.length; i++) {
		p.removeEquipment(eqList[i]);
	}
	// reset the missile monitoring array
	p.script._missiles.length = 0;
	// reset the hud back to the normal one (ie. not the hi-contrast one)
	p.hud = this.name + "_docked" + (this._amber === false ? "" : "_amber") + ".plist";
	this.$stopTimers();
	this.$stopFCB();
}
//-------------------------------------------------------------------------------------------------------------
this.playerStartedJumpCountdown = function (type, seconds) {
	this._jumpCountdownStarted = true;
}
//-------------------------------------------------------------------------------------------------------------
this.playerCancelledJumpCountdown = function () {
	this._jumpCountdownStarted = false;
}
//-------------------------------------------------------------------------------------------------------------
this.playerJumpFailed = function (reason) {
	this._jumpCountdownStarted = false;
}
//-------------------------------------------------------------------------------------------------------------
this.shipDied = function (whom, why) {
	this.$stopTimers();
	this.$stopFCB();
}
//-------------------------------------------------------------------------------------------------------------
this.shipTakingDamage = function (amount, whom, type) {
	var p = player.ship;
	var ps = p.script;
	if (!p || !ps) return;
	// work out how much armour is left in front and aft positions
	var armourFront = 0;
	var armourAft = 0;
	// ironhide only keeps one value for armour, so we'll put that value into both positions
	if (this._ironHideArmour === true) {
		armourFront = missionVariables.ironHide_percentage;
		armourAft = armourFront;
	}
	// ship config has independent front and aft armour values, so we can use those directly
	if (this._shipConfigArmour === true) {
		var arm = worldScripts.ShipConfiguration_Armour;
		if (ps._armourFront >= 0) {
			armourFront = ps._armourFront;
			armourAft = ps._armourAft;
		} else {
			armourFront = arm._armourFront;
			armourAft = arm._armourAft;
		}
	}
	// set the custom dials to the values
	// if no armour is installed, these values will be zero, effectively turning off the hud elements
	p.setCustomHUDDial("armour_forward", armourFront / 100);
	p.setCustomHUDDial("armour_aft", armourAft / 100);
}
//-------------------------------------------------------------------------------------------------------------
this.shipAttackedWithMissile = function (missile, whom) {
	if (this._disableMissileWarning === true) return; // don't add missiles if the indicator is disabled
	// add the missile to our array for checking
	var ps = player.ship.script;
	if (ps) ps._missiles.push(missile);
}
//--------------------------------------------------------------------------------------------------------------
this.alertConditionChanged = function (newCondition, oldCondition) {
	// only update the crosshairs when the condition is something other than docked (0)
	if (newCondition > 0) this.$displayCrosshairs();
	if (player.ship && player.ship.script) {
		player.ship.script._redoUnderlays = true;
	}
}
//--------------------------------------------------------------------------------------------------------------
this.viewDirectionChanged = function (viewString) {
	var p = player.ship;
	var ps = p.script;
	this._lastSwitch = 0;
	// no need to do this if the hud vanisher is installed
	if (this._hudVanisherInstalled === false) {
		if (viewString === "VIEW_CUSTOM") {
			p.hudHidden = true;
		} else {
			p.hudHidden = false;
		}
	}
	ps._currentCrosshairs = this.$selectCrosshairs(viewString);
	p.setCustomHUDDial("crosshairs", ps._currentCrosshairs);
}
//-------------------------------------------------------------------------------------------------------------
this.shipExitedWitchspace = function () {
	if (player.ship.script._missiles) {
		player.ship.script._missiles.length = 0;
	} else {
		player.ship.script._missiles = [];
	}
	if (system.ID != -1) this._lastSource = system.ID;
	this._jumpCountdownStarted = false;
	// reset the hi-contrast mode after exiting witchspace
	this._mode = this._mode.replace("_hicontrast", "");
	this.guiScreenChanged("", "");
	this._destination = this.$playerTargetSystem();
	this.$setDestination();
	this.$setCurrentSystem();
	if (this._dockTimer && this._dockTimer.isRunning) this._dockTimer.stop();
	this.$displayCrosshairs();
}
//-------------------------------------------------------------------------------------------------------------
this.playerBoughtNewShip = function (ship, price) {
	if (!this.$hudSelector && player.ship.hud === "hud.plist") {
		player.ship.hud = this.name + "_docked" + (this._amber === false ? "" : "_amber") + ".plist";
		this.$setDestination();
		this.$setCurrentSystem();
	}
}
//-------------------------------------------------------------------------------------------------------------
this.shipCloakActivated = function () {
	if (this._disableCloakIndicator === true) return;
	player.ship.setCustomHUDDial("local_cloakIndicator", "CLOAKED");
}
//-------------------------------------------------------------------------------------------------------------
this.shipCloakDeactivated = function () {
	if (this._disableCloakIndicator === true) return;
	player.ship.setCustomHUDDial("local_cloakIndicator", "");
}
//-------------------------------------------------------------------------------------------------------------
this.playerRequestedDockingClearance = function (message) {
	if (this._disableDockingHUD === false) {
		var p = player.ship;
		var ps = p.script;
		if (message === "DOCKING_CLEARANCE_GRANTED" || message === "DOCKING_CLEARANCE_EXTENDED" || message === "DOCKING_CLEARANCE_DENIED_TRAFFIC_INBOUND" || message === "DOCKING_CLEARANCE_DENIED_TRAFFIC_OUTBOUND") {
			ps._playerIsDocking = true;
			//ps._previousCrosshairs = p.crosshairs;
			p.removeEquipment("EQ_XENONHUD_CENTRE");
			p.removeEquipment("EQ_XENONHUD_CENTRE_CROSSHAIR");
			p.removeEquipment("EQ_XENONHUD_TEMP_LOW");
			p.removeEquipment("EQ_XENONHUD_TEMP_MEDIUM");
			p.awardEquipment("EQ_XENONHUD_DOCKING");
			p.crosshairs = "crosshairs_docking.plist";
			if (message === "DOCKING_CLEARANCE_GRANTED" || message === "DOCKING_CLEARANCE_EXTENDED") {
				// start a timer so we can track when the clearance expires
				if (oolite.compareVersion("1.83") > 0) {
					if (this._dockTimer && this._dockTimer.isRunning) this._dockTimer.stop();
					this._dockTimer = new Timer(this, this.$dockingExpired, 121, 0);
				}
			}
		} else {
			ps._playerIsDocking = false;
			this.$displayCrosshairs();
			//p.crosshairs = ps._previousCrosshairs;
			if (this._dockTimer && this._dockTimer.isRunning) this._dockTimer.stop();
		}
	}
}
//-------------------------------------------------------------------------------------------------------------
// for oolite 1.85++
this.playerDockingClearanceCancelled = function () {
	var p = player.ship;
	var ps = p.script;
	ps._playerIsDocking = false;
	this.$displayCrosshairs();
	//p.crosshairs = ps._previousCrosshairs;
	if (this._dockTimer && this._dockTimer.isRunning) this._dockTimer.stop();
}
//-------------------------------------------------------------------------------------------------------------
// for oolite 1.83++
this.playerDockingClearanceExpired = function () {
	this.playerDockingClearanceCancelled();
}
//-------------------------------------------------------------------------------------------------------------
this.weaponsSystemsToggled = function (state) {
	var p = player.ship;
	var ps = p.script;
	if (ps) {
		ps._currentCrosshairs = this.$selectCrosshairs(p.viewDirection);
		p.setCustomHUDDial("crosshairs", ps._currentCrosshairs);
		if (!state && ps._selectedMissile != "N/A") {
			p.setCustomHUDDial("local_selectedMissile", "");
			ps._selectedMissile = "";
		}
	}
}
//-------------------------------------------------------------------------------------------------------------
this.mfdKeyChanged = function(index, key) {
	this.$setMFDUnderlays();
}
//=============================================================================================================
// helper functions
//-------------------------------------------------------------------------------------------------------------
// HUD selector callback to turn things on/off with the currently selected HUD
this.$HUDSelectorCallBack = function $HUDSelectorCallBack(off) {
	var w = worldScripts.XenonHUD;
	if (off) {
		w.$stopTimers();
		w.$stopFCB();
		// do things to disable HUD like rename functions
		if (w.guiScreenChanged) {
			w.$save_guiScreenChanged = w.guiScreenChanged;
			delete w.guiScreenChanged;
		}
		if (w.shipWillLaunchFromStation) {
			w.$save_shipWillLaunchFromStation = w.shipWillLaunchFromStation;
			delete w.shipWillLaunchFromStation;
		}
		if (w.shipLaunchedFromStation) {
			w.$save_shipLaunchedFromStation = w.shipLaunchedFromStation;
			delete w.shipLaunchedFromStation;
		}
		if (w.shipDockedWithStation) {
			w.$save_shipDockedWithStation = w.shipDockedWithStation;
			delete w.shipDockedWithStation;
		}
		if (w.shipWillDockWithStation) {
			w.$save_shipWillDockWithStation = w.shipWillDockWithStation;
			delete w.shipWillDockWithStation;
		}
		if (w.shipDied) {
			w.$save_shipDied = w.shipDied;
			delete w.shipDied;
		}
		if (w.shipTakingDamage) {
			w.$save_shipTakingDamage = w.shipTakingDamage;
			delete w.shipTakingDamage;
		}
		if (w.shipAttackedWithMissile) {
			w.$save_shipAttackedWithMissile = w.shipAttackedWithMissile;
			delete w.shipAttackedWithMissile;
		}
		if (w.alertConditionChanged) {
			w.$save_alertConditionChanged = w.alertConditionChanged;
			delete w.alertConditionChanged;
		}
		if (w.viewDirectionChanged) {
			w.$save_viewDirectionChanged = w.viewDirectionChanged;
			delete w.viewDirectionChanged;
		}
		if (w.shipExitedWitchspace) {
			w.$save_shipExitedWitchspace = w.shipExitedWitchspace;
			delete w.shipExitedWitchspace;
		}
		if (w.playerJumpFailed) {
			w.$save_playerJumpFailed = w.playerJumpFailed;
			delete w.playerJumpFailed;
		}
		if (w.playerStartedJumpCountdown) {
			w.$save_playerStartedJumpCountdown = w.playerStartedJumpCountdown;
			delete w.playerStartedJumpCountdown;
		}
		if (w.playerCancelledJumpCountdown) {
			w.$save_playerCancelledJumpCountdown = w.playerCancelledJumpCountdown;
			delete w.playerCancelledJumpCountdown;
		}
		if (w.playerBoughtNewShip) {
			w.$save_playerBoughtNewShip = w.playerBoughtNewShip;
			delete w.playerBoughtNewShip;
		}
		if (w.playerRequestedDockingClearance) {
			w.$save_playerRequestedDockingClearance = w.playerRequestedDockingClearance;
			delete w.playerRequestedDockingClearance;
		}
		if (w.playerDockingClearanceCancelled) {
			w.$save_playerDockingClearanceCancelled = w.playerDockingClearanceCancelled;
			delete w.playerDockingClearanceCancelled;
		}
		if (w.playerDockingClearanceExpired) {
			w.$save_playerDockingClearanceExpired = w.playerDockingClearanceExpired;
			delete w.playerDockingClearanceExpired;
		}
		if (w.weaponsSystemsToggled) {
			w.$save_weaponsSystemsToggled = w.weaponsSystemsToggled;
			delete w.weaponsSystemsToggled;
		}
		if (w.shipCloakActivated) {
			w.$save_shipCloakActivated = w.shipCloakActivated;
			delete w.shipCloakActivated;
		}
		if (w.shipCloakDeactivated) {
			w.$save_shipCloakDeactivated = w.shipCloakDeactivated;
			delete w.shipCloakDeactivated;
		}
		if (w.mfdKeyChanged) {
			w.$save_mfdKeyChanged = w.mfdKeyChanged;
			delete w.mfdKeyChanged;
		}
		player.ship.removeEquipment("EQ_XENONHUD_VIEWMODE");
		w.$revertShipConfigHUD();
		
	} else {
		// do things to activate HUD like restore disabled functions
		if (!w.guiScreenChanged) eval("w.guiScreenChanged = " + w.$save_guiScreenChanged);
		if (!w.shipWillLaunchFromStation) eval("w.shipWillLaunchFromStation = " + w.$save_shipWillLaunchFromStation);
		if (!w.shipLaunchedFromStation) eval("w.shipLaunchedFromStation = " + w.$save_shipLaunchedFromStation);
		if (!w.shipDockedWithStation) eval("w.shipDockedWithStation = " + w.$save_shipDockedWithStation);
		if (!w.shipWillDockWithStation) eval("w.shipWillDockWithStation = " + w.$save_shipWillDockWithStation);
		if (!w.shipDied) eval("w.shipDied = " + w.$save_shipDied);
		if (!w.shipTakingDamage) eval("w.shipTakingDamage = " + w.$save_shipTakingDamage);
		if (!w.shipAttackedWithMissile) eval("w.shipAttackedWithMissile = " + w.$save_shipAttackedWithMissile);
		if (!w.alertConditionChanged) eval("w.alertConditionChanged = " + w.$save_alertConditionChanged);
		if (!w.viewDirectionChanged) eval("w.viewDirectionChanged = " + w.$save_viewDirectionChanged);
		if (!w.shipExitedWitchspace) eval("w.shipExitedWitchspace = " + w.$save_shipExitedWitchspace);
		if (!w.playerJumpFailed) eval("w.playerJumpFailed = " + w.$save_playerJumpFailed);
		if (!w.playerStartedJumpCountdown) eval("w.playerStartedJumpCountdown = " + w.$save_playerStartedJumpCountdown);
		if (!w.playerCancelledJumpCountdown) eval("w.playerCancelledJumpCountdown = " + w.$save_playerCancelledJumpCountdown);
		if (!w.playerBoughtNewShip) eval("w.playerBoughtNewShip = " + w.$save_playerBoughtNewShip);
		if (!w.playerRequestedDockingClearance) eval("w.playerRequestedDockingClearance = " + w.$save_playerRequestedDockingClearance);
		if (!w.playerDockingClearanceCancelled) eval("w.playerDockingClearanceCancelled = " + w.$save_playerDockingClearanceCancelled);
		if (!w.playerDockingClearanceExpired) eval("w.playerDockingClearanceExpired = " + w.$save_playerDockingClearanceExpired);
		if (!w.weaponsSystemsToggled) eval("w.weaponsSystemsToggled = " + w.$save_weaponsSystemsToggled);
		if (!w.shipCloakActivated) eval("w.shipCloakActivated = " + w.$save_shipCloakActivated);
		if (!w.shipCloakDeactivated) eval("w.shipCloakDeactivated = " + w.$save_shipCloakDeactivated);
		if (!w.mfdKeyChanged) eval("w.mfdKeyChanged = " + w.$save_mfdKeyChanged);
		// force an update
		w.shipExitedWitchspace();
		w.shipWillLaunchFromStation();
		w.viewDirectionChanged(player.ship.viewDirection);
		w.$setMFDUnderlays();
		w.$setUpHCFCB();
		w.$setUpWCFCB();
		w.$updateShipConfigHUD();
		var h = worldScripts.hudselector;
		if (h) {
			h.$HUDSelectorScanner = 3;
		}
	}
}
//-------------------------------------------------------------------------------------------------------------
this.$checkForNewDestination = function $checkForNewDestination() {
	// check if our destination hasn't been set yet (-1) or if our targetSystem has changed, or we've just left the long/short range chart
	if (this._destination === -1 || this._holdTarget != player.ship.targetSystem || (this._mapScreen === "GUI_SCREEN_SHORT_RANGE_CHART" || this._mapScreen === "GUI_SCREEN_LONG_RANGE_CHART")) {
		this._destination = this.$playerTargetSystem();
		this.$setDestination();
	}
}
//-------------------------------------------------------------------------------------------------------------
// work out whether to show crosshair controls
this.$displayCrosshairs = function $displayCrosshairs() {
	var p = player.ship;
	if (!p || !p.script) return;
	
	p.removeEquipment("EQ_XENONHUD_DOCKING");
	p.crosshairs = null; //if (p.script) p.script._previousCrosshairs;
	switch (this._crosshairMode) {
		case 0: // on in all conditions
			if (this._simplified === false) p.awardEquipment("EQ_XENONHUD_CENTRE_CROSSHAIR");
			p.awardEquipment("EQ_XENONHUD_CENTRE");
			if (p.temperature < 0.5) {
				p.awardEquipment("EQ_XENONHUD_TEMP_LOW");
				p.removeEquipment("EQ_XENONHUD_TEMP_MEDIUM");
				p.script._mediumTemp = false;
			} else {
				p.awardEquipment("EQ_XENONHUD_TEMP_MEDIUM");
				p.removeEquipment("EQ_XENONHUD_TEMP_LOW");
				p.script._mediumTemp = true;
			}
			break;
		case 1: // on in combat red alert only
			if (p.alertCondition === 3 && player.alertHostiles) {
				if (this._simplified === false) p.awardEquipment("EQ_XENONHUD_CENTRE_CROSSHAIR");
				p.awardEquipment("EQ_XENONHUD_CENTRE");
				if (p.temperature < 0.5) {
					p.awardEquipment("EQ_XENONHUD_TEMP_LOW");
					p.removeEquipment("EQ_XENONHUD_TEMP_MEDIUM");
					p.script._mediumTemp = false;
				} else {
					p.awardEquipment("EQ_XENONHUD_TEMP_MEDIUM");
					p.removeEquipment("EQ_XENONHUD_TEMP_LOW");
					p.script._mediumTemp = true;
				}
			} else {
				p.removeEquipment("EQ_XENONHUD_CENTRE");
				p.removeEquipment("EQ_XENONHUD_CENTRE_CROSSHAIR");
				p.removeEquipment("EQ_XENONHUD_TEMP_LOW");
				p.removeEquipment("EQ_XENONHUD_TEMP_MEDIUM");
				p.crosshairs = "crosshairs_mini.plist";
			}
			break;
		case 2: // on in yellow and red only
			if (p.alertCondition >= 2) {
				if (this._simplified === false) p.awardEquipment("EQ_XENONHUD_CENTRE_CROSSHAIR");
				p.awardEquipment("EQ_XENONHUD_CENTRE");
				if (p.temperature < 0.5) {
					p.awardEquipment("EQ_XENONHUD_TEMP_LOW");
					p.removeEquipment("EQ_XENONHUD_TEMP_MEDIUM");
					p.script._mediumTemp = false;
				} else {
					p.awardEquipment("EQ_XENONHUD_TEMP_MEDIUM");
					p.removeEquipment("EQ_XENONHUD_TEMP_LOW");
					p.script._mediumTemp = true;
				}
			} else {
				p.script._mediumTemp = false;
				p.removeEquipment("EQ_XENONHUD_CENTRE");
				p.removeEquipment("EQ_XENONHUD_CENTRE_CROSSHAIR");
				p.removeEquipment("EQ_XENONHUD_TEMP_LOW");
				p.removeEquipment("EQ_XENONHUD_TEMP_MEDIUM");
				p.crosshairs = "crosshairs_mini.plist";
			}
			break;
	}
}
//-------------------------------------------------------------------------------------------------------------
// checks to see if the glare filter is ever turned on, and if so, monitors the glare filter level so it stays at the default level
this.$checkGlareFilter = function $checkForGlareFilter() {
	var w = worldScripts["glare filter"];
	var p = player.ship;
	// check to see if the filter ever gets turned on
	if (w.$filterOn) this._doChecks = true;
	// once it's been turned on, monitor the glare filter and reset it to the default
	if (this._doChecks) {
		// if the filter is on, but it's set to a low level, force it to the max level
		if (w.$filterPointer < 2) {
			w.$filterPointer = 2;
			w.$adjustFilter();
		}
		if (p.sunGlareFilter < this._defaultGlareFilter) p.sunGlareFilter = this._defaultGlareFilter;
	}
	// if the filter has been turned of, we can stop doing the checks
	if (!w.$fiterOn) this._doChecks = false;
}
//-------------------------------------------------------------------------------------------------------------
// sets the HUD dials to the current crosshair images
this.$setCrosshairs = function $setCrosshairs() {
	var p = player.ship;
	if (p.hud != this.name + "_alt" + (this._amber === false ? "" : "_amber") + ".plist") {
		p.setCustomHUDDial("crosshairs", p.script._currentCrosshairs);
		p.setCustomHUDDial("crosshairsDocking", this._dockingCrosshairs);
	}
}
//-------------------------------------------------------------------------------------------------------------
// sets the witchspace destination labels to the next destination (to fix bug in 1.82 with the builtin dial that only shows the final destination)
this.$setDestination = function $setDestination() {
	var p = player.ship;
	var planet = "";
	if (this._destination >= 0 && this._destination != system.ID) planet = System.systemNameForID(this._destination);
	p.setCustomHUDDial("witchspace_destination", planet);
	this._holdTarget = p.targetSystem;
}
//-------------------------------------------------------------------------------------------------------------
// sets the current location label
this.$setCurrentSystem = function $setCurrentSystem() {
	var p = player.ship;
	var sys = "";
	if (system.ID === -1) {
		sys = "Interstellar space";
	} else {
		sys = system.name;
	}
	var stn = "";
	if (p.dockedStation) {
		stn = p.dockedStation.displayName;
		if (stn === "") stn = p.dockedStation.name;
	}
	p.setCustomHUDDial("current_system", sys);
	p.setCustomHUDDial("current_station", stn);
}
//-------------------------------------------------------------------------------------------------------------
// select the appropriate crosshairs based on the weapon in the selected view
this.$selectCrosshairs = function $selectCrosshairs(view) {
	var ws = worldScripts.XenonHUD;
	var p = player.ship;
	if (p.weaponsOnline === false) return ws.$weaponCrosshairs("EQ_WEAPON_NONE");
	var imageFile = "";
	switch (view) {
		case "VIEW_FORWARD":
			imageFile = ws.$weaponCrosshairs(p.forwardWeapon.equipmentKey);
			break;
		case "VIEW_AFT":
			imageFile = ws.$weaponCrosshairs(p.aftWeapon.equipmentKey);
			break;
		case "VIEW_PORT":
			imageFile = ws.$weaponCrosshairs(p.portWeapon.equipmentKey);
			break;
		case "VIEW_STARBOARD":
			imageFile = ws.$weaponCrosshairs(p.starboardWeapon.equipmentKey);
			break;
	}
	return imageFile;
}
//-------------------------------------------------------------------------------------------------------------
// select the correct crosshairs image based on the weapon
this.$weaponCrosshairs = function $weaponCrosshairs(weapon) {
	var ch = this._crosshairList;
	var file = (this._amber === false ? "xenon_crosshairs_other.png" : "xenon_crosshairs_other_amber.png");
	if (ch && ch[weapon]) {
		file = ch[weapon];
	}
	return file;
}
//-------------------------------------------------------------------------------------------------------------
// checks whether the player has turned off weapons, and resets the crosshairs
this.$setUpWCFCB = function $setUpWCFCB() {
	this._wc_fcb = addFrameCallback(function (delta) {
		var p = player.ship;
		var ps = p.script;
		var x = worldScripts.XenonHUD;
		if (p && p.isInSpace) {
			if (p.weaponsOnline == true) {
				// only do the missile name if the property is available (Oolite 1.89ff)
				if (ps._selectedMissile != "N/A") {
					if (p.missilesOnline == false || !p.missiles || p.missiles.length == 0 && ps._selectedMissile != "") {
						ps._selectedMissile = "";
						p.setCustomHUDDial("local_selectedMissile", "");
					}
					if (p.missilesOnline == true && p.missiles && p.missiles.length > 0 && p.activeMissile < p.missiles.length && ps._selectedMissile != p.missiles[p.activeMissile].equipmentKey) {
						ps._selectedMissile = p.missiles[p.activeMissile].equipmentKey;
						p.setCustomHUDDial("local_selectedMissile", "Selected missile: " + p.missiles[p.activeMissile].name);
					}
				}
			}
		
			var showTemp = false;
			switch (x._crosshairMode) {
				case 0: // on in all conditions
					showTemp = true;
					break;
				case 1:
					if (p.alertCondition === 3 && player.alertHostiles) showTemp = true;
					break;
				case 2:
					if (p.alertCondition >= 2) showTemp = true;
					break;
			}
			if (p.temperature >= 0.5 && ps._mediumTemp === false) {
				if (showTemp === true) {
					p.removeEquipment("EQ_XENONHUD_TEMP_LOW");
					p.awardEquipment("EQ_XENONHUD_TEMP_MEDIUM");
				}
				ps._mediumTemp = true;
			} else if (p.temperature < 0.5 && ps._mediumTemp === true) {
				if (showTemp === true) {
					p.awardEquipment("EQ_XENONHUD_TEMP_LOW");
					p.removeEquipment("EQ_XENONHUD_TEMP_MEDIUM");
				}
				ps._mediumTemp = false;
			}
			if (ps._checkForWeaponsOnline === true) {
				if (p.weaponsOnline != ps._lastCheck) {
					ps._lastCheck = p.weaponsOnline;
					ps._currentCrosshairs = ps.$selectCrosshairs(p.viewDirection);
					p.setCustomHUDDial("crosshairs", ps._currentCrosshairs);
				}
			}
			if (ps._disableMissileWarning === false) {
				ps.$checkForIncomingMissiles();
				if (ps._missileWarning === true && ps._missileWarningChanged === true) {
					p.setCustomHUDDial("local_missileWarning", "MISSILE");
					ps._missileWarningChanged = false;
				}
				if (ps._missileWarning === false && ps._missileWarningChanged === true) {
					p.setCustomHUDDial("local_missileWarning", "");
					ps._missileWarningChanged = false;
				}
			}
			if (ps._disableFuelLeakWarning === false) {
				if (p.fuelLeakRate > 0 && p.fuel > 0) {
					if (ps._fuelLeakWarning === false) {
						ps._fuelLeakWarning = true;
						p.setCustomHUDDial("local_fuelLeakWarning", "LEAK");
					}
				} else {
					if (ps._fuelLeakWarning === true) {
						ps._fuelLeakWarning = false;
						p.setCustomHUDDial("local_fuelLeakWarning", "");
					}
				}
			}
			if (ps._playerIsDocking === false && (player.alertCondition > 1 || player.alertMassLocked === true)) {
				// we're keeping track of the mass lock state via a flag, so we don't have to keep updating the HUD with the same value each second.
				if (ps._massLockOn === false) {
					p.setCustomHUDDial("local_masslockIndicator", "MASS LOCK");
				}
				ps._massLockOn = true;
			} else {
				if (ps._massLockOn === true) {
					p.setCustomHUDDial("local_masslockIndicator", "");
				}
				ps._massLockOn = false;
			}
			// check for change of screen size
			var o = oolite.gameSettings.gameWindow
			var ratio = o.width / o.height;
			if (!ps._xenon_lastRatio) ps._xenon_lastRatio = ratio;
			if (ratio != ps._xenon_lastRatio) {
				ps._xenon_lastRatio = ratio;
				if (ratio < 1.5418 && x._mode.indexOf("_narrow") === -1) {
					x._mode = "_narrow" + x._mode;
					player.ship.hud = x.name + x._mode + (x._amber === false ? "" : "_amber") + ".plist";
				} else if (x._mode.indexOf("_narrow") >= 0) {
					x._mode = x._mode.replace("_narrow", "");
					player.ship.hud = x.name + x._mode + (x._amber === false ? "" : "_amber") + ".plist";
				}
			}
			
			if (ps._redoUnderlays === true) {
				ps._redoUnderlays = false;
				x.$setMFDUnderlays();
			}
		}
	});
}
//-------------------------------------------------------------------------------------------------------------
// returns the player's target system (1.80) or the next jump to their target system (1.82)
this.$playerTargetSystem = function $playerTargetSystem() {
	var p = player.ship;
	if (p.hasOwnProperty("nextSystem")) return p.nextSystem;
	// need to check for the ANA when route checking - if no ANA it's just the targetSystem - no routing available
	var target = p.targetSystem;
	if (oolite.compareVersion("1.81") < 0 && p.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 (target != this._lastSource) {
			var myRoute = System.infoForSystem(galaxyNumber, this._lastSource).routeToSystem(System.infoForSystem(galaxyNumber, target), p.routeMode);
			if (myRoute && myRoute.route.length > 1) {
				target = myRoute.route[1];
			}
		}
	}
	return target;
}
//-------------------------------------------------------------------------------------------------------------
// stops the dock timer
this.$stopTimers = function $stopTimers() {
	var p = player.ship;
	var ps = p.script;
	// stop the glare filter check timer if it's running
	if (this._glareTimer && this._glareTimer.isRunning) this._glareTimer.stop();
	// stop the dock timer check timer if it's running
	if (this._dockTimer && this._dockTimer.isRunning) this._dockTimer.stop();
	// stop the weapons online check timer if it's running
	//if (ps && ps._weaponsOnlineTimer && ps._weaponsOnlineTimer.isRunning) ps._weaponsOnlineTimer.stop();
}
//-------------------------------------------------------------------------------------------------------------
// checks for an expired docking request
this.$dockingExpired = function $dockingExpired() {
	var p = player.ship;
	var ps = p.script;
	if (ps._playerIsDocking === true) {
		this.$displayCrosshairs();
		//p.crosshairs = null; ps._previousCrosshairs;
		ps._playerIsDocking = false;
	}
}
//-------------------------------------------------------------------------------------------------------------
// performs a scan of the missile array to see if any are still tracking the player
this.$checkForIncomingMissiles = function $checkForIncomingMissiles() {
	if (!this.ship || !this.ship.position) return;
	var found = false;
	var p = this.ship;
	var ps = p.script;
	// do we have any missiles in the queue?
	if (ps._missiles.length === 0) {
		if (ps._missileWarning === true) ps._missileWarningChanged = true;
		ps._missileWarning = false;
		return;
	}
	// are they in range and valid?
	var ml = ps._missiles.length;
	for (var i = 0; i < ml; i++) {
		var m = ps._missiles[i];
		if (m.isValid && m.isInSpace && m.position.distanceTo(p) < p.scannerRange) {
			if (ps._missileWarning === false) ps._missileWarningChanged = true;
			ps._missileWarning = true;
			found = true;
			break;
		}
	}
	// has the flag state changed
	if (found === false && ps._missileWarning === true) {
		if (ps._missileWarning === true) ps._missileWarningChanged = true;
		ps._missileWarning = false;
		ps._missileWarningChanged = true;
	}
}
//-------------------------------------------------------------------------------------------------------------
this.$preRespray = function $preRespray() {
	var sch = worldScripts.ShipConfiguration_F3HUDControl;
	if (sch) this._holdHUD = sch._holdHUD;
}
//-------------------------------------------------------------------------------------------------------------
this.$postRespray = function $postRespray() {
	var sch = worldScripts.ShipConfiguration_F3HUDControl;
	if (sch) sch._holdHUD = this._holdHUD;
}
//-------------------------------------------------------------------------------------------------------------
this.$xenonHUDActive = function $xenonHUDActive() {
	return (player.ship.hud.indexOf("XenonHUD") >= 0);
}
//-------------------------------------------------------------------------------------------------------------
this.$switchAmberCrosshairs = function $switchAmberCrosshairs() {
	var defList = ["xenon_crosshairs_alt1", "xenon_crosshairs_alt2", "xenon_crosshairs_alt3", "xenon_crosshairs_alt4", "xenon_crosshairs_alt5",
		"xenon_crosshairs_beam", "xenon_crosshairs_military", "xenon_crosshairs_mining", "xenon_crosshairs_none", "xenon_crosshairs_other", "xenon_crosshairs_pulse"
	];
	var ch = this._crosshairList;
	var extra = "";
	if (this._amber === true) extra = "_amber";
	this._dockingCrosshairs = "xenon_crosshairs_alt5" + extra + ".png";
	// look for an existing entry and update the filename if found
	var list = Object.keys(ch);
	for (var i = 0; i < list.length; i++) {
		var laser = list[i];
		for (var j = 0; j < defList.length; j++) {
			if (ch[laser].indexOf(defList[j]) >= 0) {
				ch[laser] = defList[j] + extra + ".png";
			}
		}
	}
}
//-------------------------------------------------------------------------------------------------------------
this.$updateShipConfigHUD = function $updateShipConfigHUD() {
	// tell ShipConfig to use our HUD on the F3 Equip Ship screen
	var sch = worldScripts.ShipConfiguration_F3HUDControl;
	var p = player.ship;
	if (sch && p.hud.indexOf(this.name) >= 0) {
		if (!sch._holdHUD) sch._holdHUD = sch._equipSpaceHUD;
		sch._equipSpaceHUD = this.name + "_docked" + (this._amber === false ? "" : "_amber") + ".plist";
	}
}
//-------------------------------------------------------------------------------------------------------------
this.$revertShipConfigHUD = function $revertShipConfigHUD() {
	// tell ShipConfig to go back to it's former HUD on the F3 Equip Ship screen
	var sch = worldScripts.ShipConfiguration_F3HUDControl;
	var p = player.ship;
	if (sch && sch._holdHUD) {
		sch._equipSpaceHUD = sch._holdHUD;
		delete sch._holdHUD;
	}
}
//-------------------------------------------------------------------------------------------------------------
this.$removeHUDEquipment = function $removeHUDEquipment() {
	var p = player.ship;
	var remove = ["EQ_XENONHUD_CLOCK_1", "EQ_XENONHUD_CLOCK_2", "EQ_XENONHUD_CLOCK_3"];
	for (var i = 0; i < remove.length; i++) p.removeEquipment(remove[i]);
	// reset other elements
	p.setCustomHUDDial("local_missileWarning", "");
	p.setCustomHUDDial("local_selectedMissile", "");
	p.setCustomHUDDial("local_fuelLeakWarning", "");
	p.setCustomHUDDial("local_masslockIndicator", "");
	p.setCustomHUDDial("local_cloakIndicator", "");
}
//-------------------------------------------------------------------------------------------------------------
this.$changeSettings = function $changeSettings() {
	this.$updateShipConfigHUD();
}
//-------------------------------------------------------------------------------------------------------------
// setup a FCB to monitor when hicontrast mode should be turned on/off
this.$setUpHCFCB = function $setUpHCFCB() {
	// isSunlit is a feature only in the latest release
	if (!("isSunlit" in player.ship)) return;
	// if the primable equipment is still active, don't start the FCB - player wants manual control
	if (this._removePrimableEquip == false) return;
	this._hc_fcb = addFrameCallback(this.$hiContrastFCB);
}
//-------------------------------------------------------------------------------------------------------------
this.$hiContrastFCB = function (delta) {
	// only do anything if we're on the main display
	if (guiScreen != "GUI_SCREEN_MAIN") return;
	var that = $hiContrastFCB;
	var w = (that.w = that.w || worldScripts.XenonHUD);
	var p = (that.p = that.p || player.ship);
	if (!p || !p.position) return; // probably player has died
	var mode = w._mode;
	var switchHi = false;
	var switchNrml = false;
	// auto switch if we're in shadow but the mode is hicontrast
	if (p.isSunlit == false && mode == "_hicontrast") {
		switchNrml = true;
	// don't need hi-contrast in interstellar space
	} else if (mode == "_hicontrast" && system.isInterstellarSpace) {
		switchNrml = true;
	} else {
		// no sun in interstellar space, so no need to switch
		if (system.isInterstellarSpace) return;
		var s = (that.s = that.s || system.sun);
		var cf = (that.cf = that.cf || system.info.corona_flare);
		if (!s || !s.position) {
			s = system.sun;
			cf = system.info.corona_flare;
		}
		var sgf = p.sunGlareFilter;
		// point at which sun glare will become a problem
		var dst_rng = [1900000, 1800000, 1700000, 1600000, 1500000, 1400000, 1270000, 1120000, 1120000, 1120000, 1120000];
		var rng = dst_rng[parseInt(sgf * 10)];
		// only check for the requirements if we're at a point where glare becomes a problem.
		var dist = p.position.distanceTo(s);
		var radius = s.radius * (1 + (8.0 * cf));
		//log("fcb", "dist " + dist + ", radius " + radius + ", rng " + rng + ", cf " + cf + ", sgf " + sgf);
		if (dist < rng) {
			var deviation = 0;
			switch (p.viewDirection) {
				case "VIEW_FORWARD":
					deviation = p.vectorForward.angleTo(s.position.subtract(p.position));
					break;
				case "VIEW_AFT":
					deviation = p.vectorForward.angleTo(p.position.subtract(s.position));
					break;
				case "VIEW_PORT":
					deviation = p.vectorRight.angleTo(p.position.subtract(s.position));
					break;
				case "VIEW_STARBOARD":
					deviation = p.vectorRight.angleTo(s.position.subtract(p.position));
					break;
			}
			deviation *= sgf; 
			var calc = radius / dist;
			// by this point, we should be ignoring heading
			if (calc > (0.90 - cf)) deviation = 0; 
			//log("fcb", "dist " + dist + ", rad " + radius + ", cf " + cf + ", calc " + calc + ", dev " + deviation + ", curr mode " + w._mode);
			// pointing at main part of sun
			if (mode == "" && deviation < calc) switchHi = true;
			// pointing away from main part of sun
			if (mode == "_hicontrast" && deviation > calc) switchNrml = true;
		} else {
			if (mode == "_hicontrast") switchNrml = true;
		}
	}
	if (switchHi == true && clock.absoluteSeconds > w._lastSwitch + 1) {
		w._mode = "_hicontrast";
		p.hud = "XenonHUD_hicontrast" + (w._amber === false ? "" : "_amber") + ".plist";
		if (p.script._playerIsDocking === true) p.crosshairs = "crosshairs_docking.plist";
		w._lastSwitch = clock.absoluteSeconds;
		return;
	}
	if (switchNrml == true && clock.absoluteSeconds > w._lastSwitch + 1) {
		w._mode = "";
		p.hud = "XenonHUD" + (w._amber === false ? "" : "_amber") + ".plist";
		if (p.script._playerIsDocking === true) p.crosshairs = "crosshairs_docking.plist";
		w._lastSwitch = clock.absoluteSeconds;
		return;
	}
}
//-------------------------------------------------------------------------------------------------------------
// turn off the FCB
this.$stopFCB = function() {
	if (isValidFrameCallback(this._hc_fcb)) removeFrameCallback(this._hc_fcb);
	if (isValidFrameCallback(this._wc_fcb)) removeFrameCallback(this._wc_fcb);
}
//-------------------------------------------------------------------------------------------------------------
// adds/removes equipment items for controlling the mfd underlays (used in hi-contrast mode)
this.$setMFDUnderlays = function $setMFDUnderlays() {
	var p = player.ship;
	var mfd = p.multiFunctionDisplayList;
	for (var i = 0; i < mfd.length; i++) {
		if (mfd[i] != null && mfd[i] != "" && this.$confirmMFDEquipment(mfd[i]) == true) {
			p.awardEquipment("EQ_XENONHUD_MFD" + (i + 1).toString());
		} else {
			p.removeEquipment("EQ_XENONHUD_MFD" + (i + 1).toString());
		}
	}
}
//-------------------------------------------------------------------------------------------------------------
// because there is no direct connection between equipment items and MFD ID's we're doing a translation here
this.$confirmMFDEquipment = function $confirmMFDEquipment(mfdID) {
	var list = null;
	switch (mfdID) {
		case "combat_MFD": list = ["EQ_COMBATMFD"]; break;
		case "telescope": list = ["EQ_TELESCOPE"]; break;
		case "escortdeck": list = ["EQ_ESCORTDECK", "EQ_ESCORTDECKXL"]; break;
		case "CommsLogMFD": list = ["EQ_COMMSLOGMFD", "EQ_COMMSLOGMFD_PASSIVE"]; break;
		case "BroadcastCommsMFD": list = ["EQ_BROADCASTCOMMSMFD"]; break;
		case "trophy_mfd": list = ["EQ_TROPHY_MFD"]; break;
		case "navi_mfd": list = ["EQ_NAVIGATION_MFD"]; break;
		case "market_inquirer": list = ["EQ_MARKET_INQUIRER_MFD"]; break;
		case "manifest_mfd": list = ["EQ_MANIFEST_MFD"]; break;
		case "useful_MFD_general_info": list = null; break; // no equip for this one
		case "Waypoint_Here_MFD": list = ["EQ_WPH_ASC_UPGRADE"]; break;
		case "DamageReportMFD": list = ["EQ_DAMAGE_REPORT_MFD", "EQ_DAMAGE_REPORT_MFD_PASSIVE"]; break;
		case "RangeFinderMFD": list = ["EQ_GCM_RANGE_FINDER_MFD"]; break;
		case "LaunchQueueMFD": list = null; break; // no equip for this one
		case "BountySystem_WarrantScanner": list = ["EQ_WARRANT_SCANNER"]; break;
	}
	if (list == null) return true;
	var p = player.ship;
	for (var i = 0; i < list.length; i++) {
		if (p.equipmentStatus(list[i]) == "EQUIPMENT_OK") return true;
	}
	return false;
} |