Back to Index Page generated: Dec 20, 2024, 7:22:09 AM

Expansion HUD Selector with Large HUD

Content

Manifest

from Expansion Manager's OXP list from Expansion Manifest
Description Change your HUD during flight between the supported ones. You can set your default MFDs and Scanner settings for any HUD. Contains ExtraLarge, Large and Small HUDs, all with 10 MFDs. Change your HUD during flight between the supported ones. You can set your default MFDs and Scanner settings for any HUD. Contains ExtraLarge, Large and Small HUDs, all with 10 MFDs.
Identifier oolite.oxp.Norby.HUDSelector oolite.oxp.Norby.HUDSelector
Title HUD Selector with Large HUD HUD Selector with Large HUD
Category HUDs HUDs
Author Norby Norby
Version 1.27 1.27
Tags
Required Oolite Version
Maximum Oolite Version
Required Expansions
Optional Expansions
  • oolite.oxp.Norby.CombatMFD:1.11
  • oolite.oxp.CommonSenseOTB.NumericHUD:3.27
  • oolite.oxp.Norby.CombatMFD:1.11
  • oolite.oxp.CommonSenseOTB.NumericHUD:3.27
  • Conflict Expansions
    Information URL https://wiki.alioth.net/index.php/HUDSelector n/a
    Download URL https://wiki.alioth.net/img_auth.php/4/47/HUDSelector-1.27.oxz n/a
    License CC BY-NC-SA 4 CC BY-NC-SA 4
    File Size n/a
    Upload date 1713496460

    Documentation

    Also read http://wiki.alioth.net/index.php/HUD%20Selector%20with%20Large%20HUD

    Readme.txt

    HUD Selector
    ------------
    v1.25, by Norby
    
    Install this equipment and change your HUD during flight by priming (Shift+N) and then activating (n).
    You can set your scanner to NonLinear and UltraZoom by pressing the mode key (b) as many times as needed when HUD Selector is primed. Your settings are stored in your savegame.
    
    After you select your HUD, you can sell the HUD Selector equipment as the selection is saved into your savegame.
    
    Initially supported HUDs:
    * Default HUD (this uses the actual hud.plist, so will potentially be any non-HUD Selector-compatible HUD, for example AAD HUD or Compact HUD if installed)
    * ExtraLarge HUD with 10 MFDs (included in this package, for 4:3 and 16:9 screens)
    * Large HUD with 10 MFDs (included in this package, as per the original HUD but with a large scanner, for 16:10 also)
    * Numeric HUD with 8 MFDs or 4*4 MFDs (both layouts are selectable in HUDSelector)
    * Small HUD with 10 MFDs (included in this package)
    
    Other HUD's which are compatible:
    * Xenon HUD
    * Vimana HUD
    * Vimana-X HUD
    * Coluber HUD CH01
    
    There is also built-in support for setting the default Multi-Function Displays (MFDs).
    
    The following OXP's have MFDs in them and are compatible with HUD Selector:
    
        BroadcastComms MFD - http://aegidian.org/bb/viewtopic.php?f=4&t=16826
        Combat MFD - http://wiki.alioth.net/index.php/CombatMFD
        CommsLogMFD - http://wiki.alioth.net/index.php/CommsLogMFD
        EscortDeck - http://wiki.alioth.net/index.php/EscortDeck
        Manifest MFD - http://aegidian.org/bb/viewtopic.php?f=4&t=16591
        Market Inquirer - http://wiki.alioth.net/index.php/Market_Inquirer
        Navigation MFD - http://aegidian.org/bb/viewtopic.php?f=4&t=16411
        Telescope - http://wiki.alioth.net/index.php/Telescope
        Trophy Collector - http://wiki.alioth.net/index.php/Trophy_Collector
        Useful MFDs - http://aegidian.org/bb/viewtopic.php?f=4&t=16104
    
    
    In the Interfaces (F4) screen you can select from the installed HUDs and set the default for your MFDs.
    
    There is also a Combat MFD Custom Dials setup in HUD Selector Interface, where you can turn on/off the numeric and bar displays provided by Combat MFD. If your HUD uses Combat MFD custom dials, and shows your energy in numeric form, but you would like to stay with the bars only, then you can turn off the "combatEnergy" dial here.
    
    Large and Original HUDs have implements custom dials if Combat MFD OXP is installed:
    * Numeric speed value in speed bar
    * Target distance, speed and fired missiles counter on top of the speedbar (need Combat MFD equipment)
    * Compass target distance meter
    * Number of damaged equipment items and the name of last damaged item below the scanner
    * Player bounty and legal status below the damaged equipment
    * Telescope's last detected target over the shield bar
    * Reserve fuel in ly within the fuel bar
    * Distance to the nearest planetary object in the altitude bar,
    * Cargo and Hull (service level) bars between the status light and clock.
    
    
    Requirements from other HUDs
    ----------------------------
    
    In order for a HUD to become compatible with HUD selector, it must:
    * name the plist something other than "hud.plist"
    * define a worldScript with similar this.name to the plist file (for example in Config/script.js)
    * set your HUD and add your HUD name with the plist name into HUDSelector in startUp:
    
        this.name = "yourhud";
        this.startUp() = function () {
            player.ship.hud =  this.name + ".plist";
            var h = worldScripts.hudselector;
            if( h ) h.$HUDSelectorAddHUD("Your HUD", this.name); //name of plist without extension
        }
    
    * if your HUD defines different plists for 4:3, 16:10 and 16:9 screens named named (for example) "yourhud.plist", "yourhud10.plist" and "yourhud9.plist" then add all of them to the $HUDSelectorAddHUD function call:
    
        if( h ) h.$HUDSelectorAddHUD("Your HUD", this.name, this.name+"10", this.name+"9" );
    
    HUDSelector will set the proper plist for the current screen but will not follow the window resizes, for example if you switch to fullscreen during flight. In this case you can set your HUD again by activating the HUDSelector equipment.
    
    * You can define a callback function in your worldScript to start/stop some parts of your scripted HUD. You would do this if your HUD is performing calculations or monitoring system events in order to give feedback to the player via HUD elements. However, those events might not be appropriate to run if your HUD is not currently active. The $HUDSelectorCallback function allows you to start or stop things to make sure the game performs well.
    In this example, we are enabling or disabling the "shipWillLaunchFromStation" worldscript event in our HUD:
    
        this.$HUDSelectorCallBack = function ( off ) {
            var w = worldScripts["yourhud"];
            if( off ) { //do things to disable your HUD like rename functions
            if( w.shipWillLaunchFromStation ) {
                w.$save_shipWillLaunchFromStation = w.shipWillLaunchFromStation;
                delete w.shipWillLaunchFromStation;
            }
            } else { //do things to activate your HUD like restore disabled functions
                if( !w.shipWillLaunchFromStation )
                eval("w.shipWillLaunchFromStation = "+w.$save_shipWillLaunchFromStation);
            }
        }
    
    
    New MFDs can register in startUp
    --------------------------------
    
    If you are developing an MFD you would like to see including in the MFD setup, you can do the following.
    
    If the key you are using for your MFD is the same as your worldScript name, the call can be as simple as this:
    
        var h = worldScripts.hudselector;
        if( h && h.$HUDSelectorAddMFD ) h.$HUDSelectorAddMFD(this.name);
    
    If the key you are using for your MFD is different to the worldscript name then must give the name in the second parameter:
    
        var h = worldScripts.hudselector;
        if( h && h.$HUDSelectorAddMFD ) h.$HUDSelectorAddMFD(this.name, "my_MFD_key");
    
    If you want to give your MFD a more meaningful name (rather than simply using the worldscript name or MFD key), you can also specify a "displayName":
    
        var h = worldScripts.hudselector;
        if( h && h.$HUDSelectorAddMFD ) h.$HUDSelectorAddMFD(this.name, "my_MFD_key", "My Special MFD");
    
    If you don't specify a displayName, it will default to the MFD key when not provided (which will itself default to the worldscript name if not provided).
    
    
    License
    -------
    
    This work is licensed under the Creative Commons Attribution-NonCommercial-ShareAlike License version 4.0.
    If you are re-using any piece of this OXP, please let me know by sending an e-mail to norbylite@gmail.com.
    
    
    Changelog
    ---------
    2024.04.19. v1.27 (phkb)
    * Added console message when changing HUD's via the F4 Interface page.
    * Small tweak to MFD setting, so MFD's aren't set to "undefined".
    * Small code fix for missing declaration.
    
    2023.10.15. v1.26 (phkb)
    * Corrections to Telescope MFD entries in array.
    
    2023.10.11. v1.25 (phkb)
    * Removed debug messages.
    
    2023.10.08. v1.24 (phkb)
    * Really fixed issue with reading default MFD's from new dataset method.
    
    2023.10.08. v1.23 (phkb)
    * Fixed issue with reading default MFD's from new dataset method.
    
    2023.10.07. v1.22 (phkb)
    * Reapplied old code that read default MFDs, so there should be continuity between old and new versions.
    
    2023.09.23. v1.20 (phkb)
    * New, more consistent method to save MFD arrangement.
    
    2023.09.06. v1.19 (phkb)
    * Gave MFD's a "displayName" to make interface far more user friendly
    * Added some text alignment code to the lists, to make them a little easier to read. Also added some helper text to each screen, to better explain what they do.
    * Expanded the default list of MFD's.
    * Removed need for FCB's on mission screens.
    * After the game has started and all compatible HUD's have been registered, the callback function will only be called on the HUD being disabled and the HUD being enabled, reducing the time it takes to switch HUDs and preventing timeout issues.
    * Changed the flow of screens, so exiting from the MFD or Combat HUD custom dials screens returns to the main HUD listing, instead of exiting back to the F4 Interfaces page.
    * Setting the initial HUD after load, and running all the callbacks, is now only performed once, rather than twice.
    
    2020.11.21. v1.18
    * Display HUD Selector Removal in red at Ship Outfitting screen.
    * Sets Station Interface (F4) when loading savefile (previously done only when docking).
    * Deals with more than one HUD from the same worldscript.
    * Adds Primeable Equipment MFD to the default MFDs list.
    
    2016.08.15. v1.17 
    * Removed "(adjusting)" text from timer in Large and other included HUDs.
    * Always turn on the HUD at launch and at Options(F2) to fix problems in any addons.
    * A comma added into "Press Space, Commander".
    
    2015.05.26. v1.16 
    *Fixed timeout caused by missing images, thanks to CdrC64.
    
    2015.05.25. v1.15 
    * ExtraLarge HUD got narrower scanner in 16:10 to fit 10 MFDs from Oolite 1.81.
    * HUDs can define different plists for 4:3, 16:10 and 16:9 screens in AddHUD call.
    
    2015.05.24. v1.14
    * ExtraLarge HUD is the new name of XL HUD.
    * Removed Original HUD and Wide HUD in favor of Large HUD and ExtraLarge HUD.
    * Fixed fuel indicator in ExtraLarge HUD.
    * Small HUD got alert sensitive scanner.
    
    2015.05.23. v1.13
    * Large HUD is optimized for 16:10 screens, scanner is narrower to show 10 MFDs.
    * Wide HUD is a bit polished variant of the previous Large HUD for 16:9 screens.
    * XL HUD is a polished variant of the Large HUD in v1.11 with very large scanner.
    * Clock and custom dials are repositioned from bottom center in these HUDs.
    
    2015.05.21. v1.12
    * Large HUD's messages and comms boxes are wider and aligned to the sides.
    * Reduced Large scanner height for better aspect ratio and more room.
    * Fixed needed fuel indicator, thanks to Anonymissimus.
    * Removed the nonexistent Scanner HUD from the selectable HUD list.
    
    2015.05.17. v1.11
    * Large HUD added with as large scanner as in NumericHUD.
    
    2015.04.27. v1.10
    * Fixed restore of MFDs if the current HUD remove them in the dock.
    * Interface screens use background from BGS or Better Screens if available.
    
    2015.01.10. v1.9
    * MFD selector list limited to the actual number of MFDs.
    * Exit back to the Interfaces screen from HUD and MFD interfaces.
    
    2014.10.20. v1.8
    * Original HUD got many numeric displays from CombatMFD.
    * Custom Dials setup in Hud Selector Interface.
    * Support for more than 20 HUDs and MFDs.
    
    2014.10.16. v1.7
    * Original and Small HUDs has numeric speed display with CombatMFD in Oolite 1.81.
    
    2014.09.25. v1.6
    * Scanner settings compatible with Auto Crosshairs OXP and plist-changer huds.
    
    2014.09.24. v1.5
    * Fixed the saving of scanner settings if the equipment is uninstalled.
    
    2014.09.23. v1.4
    * Scanner settings are adjustable by mode key (b) if HUD Selector primed.
    
    2014.08.29. v1.3
    * MFD setup changes during flight are saved (need Oolite 1.81).
    * Original and Small HUDs has 10 MFDs and scanner zoom up to 16x.
    * Support in Original and Small HUDs for CombatMFD's target status.
    
    2014.08.12. v1.2
    * Small fix for MarketObserver.
    
    2014.08.11. v1.1
    * Interfaces and MFD handling.
    * Selections are saved into the savegame.
    * The included Original and Small HUDs has 8 MFDs.
    
    2014.07.05. v1.0
    * First release.
    

    Equipment

    Name Visible Cost [deci-credits] Tech-Level
    HUD Selector yes 100 5+
    Remove HUD Selector yes 0 1+

    Ships

    This expansion declares no ships.

    Models

    This expansion declares no models.

    Scripts

    Path
    Scripts/hudselector.js
    "use strict";
    this.name = "hudselector";
    this.author = "Norby";
    this.copyright = "2014 Norby";
    this.description = "Change your HUD during flight between the supported ones.";
    this.license = "CC BY-NC-SA 4.0";
    this.version = "1.25";
    
    this._hairSpace = String.fromCharCode(31);
    this._hairSpaceLength = defaultFont.measureString(this._hairSpace);
    this._ellip = "…";
    
    this.$HUDSelectorHUDs = [ //worldscript, name and .plists
        [null, "Default HUD", "hud"],
        //the first plist for 4:3, then optionally for 16:10 and 16:9 screens if different
        ["hudselector", "ExtraLarge HUD with 10 MFDs and custom dials", "hudselector-extralarge",
            "hudselector-extralarge10"], //for 16:10
        //large is a copy of the oolite's stock hud.plist but with large scanner and more MFDs
        ["hudselector", "Large HUD with 10 MFDs and custom dials", "hudselector-large"],
        //copy of stock hud-small.plist but with more MFDs
        ["hudselector", "Small HUD with 10 MFDs and custom dials", "hudselector-small"]
    ];
    
    this.$HUDSelectorOldHUDs = [
        [null, "Default HUD", "hud"], //without custom dials for Oolite 1.80 and below
        ["hudselector", "ExtraLarge HUD with 10 MFDs", "hudselector-oldextralarge"],
        ["hudselector", "Large HUD with 10 MFDs", "hudselector-oldlarge"],
        ["hudselector", "Small HUD with 10 MFDs", "hudselector-oldsmall"],
    ];
    
    //default MFD 1-10 worldScript names
    this.$HUDSelectorMFDs = [
        ["combat_MFD", "combat_MFD", "Combat MFD"],
        ["telescope", "TelescopeMFD", "Telescope MFD"],
        ["telescope", "TelescopeAuxMFD", "Telescope Auxiliary MFD"],
        ["escortdeck", "escortdeck", "Escort Deck MFD"],
        ["CommsLogMFD", "CommsLogMFD", "Comms Log MFD"],
        ["BroadcastCommsMFD", "BroadcastCommsMFD", "Broadcast Comms MFD"],
        ["trophy_col", "trophy_mfd", "Trophy Collector MFD"], //mfd name is different
        ["navi_mfd", "navi_mfd", "Navigation MFD"],
        ["market_inquirer", "inquirer_mfd", "Market Inquirer MFD"], //mfd name is not equal with worldScript name
        ["manifest_mfd", "manifest_mfd", "Manifest MFD"],
        ["useful_MFDs", "useful_MFD_general_info", "Useful MFDs - General Info"], //mfd name is different
        ["useful_MFDs", "useful_MFD_market", "Useful MFDs - Market"],
        ["PrimeableEquipmentMFD", "nm_primeable-mfd", "Primeable Equipment MFD"],
        ["GalCopBB_RangeFinder_MFD", "RangeFinderMFD", "Range Finder MFD"],
        ["NavBeaconsMFD", "NavigationBeaconsMFD", "Navigation Beacons MFD"],
        ["Waypoint_Here_MFD", "Waypoint_Here_MFD", "Waypoint Here MFD"],
        ["StationDockControl_MFD", "LaunchQueueMFD", "Station Launch Queue MFD"],
        ["DamageReportMFD", "DamageReportMFD", "Damage Report MFD"],
        ["BountySystem_WarrantScanner", "BountySystem_WarrantScanner", "Warrant Scanner MFD"],
        ["RandomStationNames", "RandomStationNames", "Galactic Almanac MFD"],
        ["EQ_ADVANCED_SYSTEM_DATA_MFD", "EQ_ADVANCED_SYSTEM_DATA_MFD", "Advanced System Data MFD"],
        ["Fighters", "Fighters", "Fighters MFD"],
        ["SSLaneIndicator", "SSLaneIndicator", "Star System Lane Indicator MFD"],
        ["TargetSystemPlugins_ShortRangeSnapshot", "tsplugins_srs", "Short Range Snapshot MFD"],
        ["TargetSystemPlugins_TargetMemoryInterface", "tsplugins_tmi", "Target Memory Interface MFD"],
        ["Ships Library", "ships-library", "Ships Library MFD"],
    ];
    
    this.$HUDSelectorScanSet = [
        "Linear, NormalZoom",
        "NonLinear, NormalZoom",
        "Linear, UltraZoom",
        "NonLinear, UltraZoom"
    ];
    
    //internal properties, should not touch
    this.$BG = null; //mission screen background
    this.$combatWS = null; //pointer to the worldScript of CombatMFD
    this.$HUDSelectorDefaultMFDs = []; //load the default MFD settings into this array
    this.$HUDSelectorDialNo = 0; //where will start the Dial Interface
    this.$HUDSelectorChoice = null; //store the last selection in missionscreens
    this.$HUDSelectorEqSel = 2; //number of the selected HUD ( 2 = Large HUD )
    this.$HUDSelectorFix = true; //turn on the fix of scanner settings problem caused by Auto Crosshairs OXP
    this.$HUDSelectorFixInEveryFrame = false; //needed if both Auto Crosshairs OXP and Telescope OXP is installed
    this.$HUDSelectorFixFCB = null; //FCB for fixing crosshairs change bug
    this.$HUDSelectorScanner = 0; //store the combined number of NonLinear and UltraZoom scanner settings
    this.$HUDSelectorTimer = null; //store a 0.1s delay timer for other startUps
    this.$C = -1; //store the last crosshairs setting for FixFCB
    this.$H = -1; //store the last hud setting for FixFCB
    this.$L = -1; //store the last scanner linearity setting for FixFCB
    this.$Z = -1; //store the last scanner zoom setting for FixFCB
    this.$debug = true;
    
    //equipment events
    //---------------------------------------------------------------------------------
    this.activated = function () {
        var h = worldScripts.hudselector;
        var prev = h.$HUDSelectorEqSel;
        h.$HUDSelectorEqSel++; //select next HUD
        if (h.$HUDSelectorEqSel >= h.$HUDSelectorHUDs.length) h.$HUDSelectorEqSel = 0;
        player.consoleMessage(h.$HUDSelectorHUDs[h.$HUDSelectorEqSel][1] + " selected", 10);
        h.$HUDSelectorSetHUD(prev);
    }
    
    //---------------------------------------------------------------------------------
    this.mode = function () {
        var h = worldScripts.hudselector;
        var b = 0;
        if (player.ship.scannerNonLinear) b += 1;
        if (player.ship.scannerUltraZoom) b += 2;
        b++;
        if (b > 3) b = 0;
        player.consoleMessage("Scanner: " + h.$HUDSelectorScanSet[b], 10);
        this.$HUDSelectorSetScanner(b);
    }
    
    //worldscript events
    //---------------------------------------------------------------------------------
    this.startUp = function () {
        this.$BG = null; //mission screen background
        if (worldScripts["BGS-M"]) this.$BG = "bgs-i_interface_parcel1.png";
        else if (worldScripts["Better Screens"]) this.$BG = "bs-base-bg.png";
    
        this.$combatWS = worldScripts.combat_MFD;
        var s = missionVariables.$HUDSelectorDataShow;
        if (this.$combatWS && this.$combatWS.$DataShow && s) {
            var d = JSON.parse(s);
            for (var key in d) {
                this.$combatWS.$DataShow[key] = d[key];
            }
            //do not use this.$combatWS.$DataShow=d; to avoid cropping DataShow array
        }   //in the case of a new CombatMFD with longer array and loading an old savegame
    
        if (!player.ship.setCustomHUDDial) {//for Oolite 1.80 and below
            this.$HUDSelectorHUDs[1] = this.$HUDSelectorOldHUDs[1]; //oldextralarge
            this.$HUDSelectorHUDs[2] = this.$HUDSelectorOldHUDs[2]; //oldlarge
            this.$HUDSelectorHUDs[3] = this.$HUDSelectorOldHUDs[3]; //oldsmall
        }
    
        this.$HUDSelectorScanner = missionVariables.$HUDSelectorScanner;
        // check for the previous version of the default MFD dataset
        if (missionVariables.$HUDSelectorDefaultMFDs) {
            var d = missionVariables.$HUDSelectorDefaultMFDs;
            if (!d) this.$HUDSelectorDefaultMFDs = [];
            else if ((d + "").indexOf(",")) this.$HUDSelectorDefaultMFDs = (d + "").split(","); //need +"" for bugfix of a single item
            else this.$HUDSelectorDefaultMFDs = [d];
            delete missionVariables.$HUDSelectorDefaultMFDs;
        }
        // check for the new version of the default MFD data set.
        if (missionVariables.$HUDSelectorDefaultMFDs_new) {
            this.$HUDSelectorDefaultMFDs = JSON.parse(missionVariables.$HUDSelectorDefaultMFDs_new);
        }
    
        if (player.ship && player.ship.docked) this.shipDockedWithStation(player.ship.dockedStation); //set interface
        else if (system) this.shipDockedWithStation(system.mainStation);//fallback
    
        if (worldScripts["Auto Crosshairs"] && worldScripts.telescope)
            this.$HUDSelectorFixInEveryFrame = true; //needed if both Auto Crosshairs OXP and Telescope OXP is installed
    
        if (this.$HUDSelectorFix) //FCB for fixing crosshairs change bug
            this.$HUDSelectorFixFCB = addFrameCallback(this.$HUDSelector_FixFCB.bind(this));
    }
    
    //---------------------------------------------------------------------------------
    this.startUpComplete = function () {
        // start the process of switching to the saved HUD
        // first, make the current hud the saved one, but don't do any of the callbacks yet
        // this is so other OXP's that might need to check on the type/size of the HUD can do so reliably
        this.$HUDSelectorRestoreHUD();
        this.$HUDSelectorSetHUD();
    
        if (!this.$HUDSelectorDefaultMFDs || !(this.$HUDSelectorDefaultMFDs.length > 0)) {
            //set default MFDs first time in order of $HUDSelectorMFDs array
            this.$HUDSelectorDefaultMFDs = [];
            for (var i = 0; i < this.$HUDSelectorMFDs.length; i++) {
                if (this.$HUDSelectorMFDs[i] && this.$HUDSelectorMFDs[i][0]) {
                    var w = this.$HUDSelectorMFDs[i][0]; //worldScript name
                    if (worldScripts[w]) {
                        this.$HUDSelectorDefaultMFDs[i] = w;
                    }
                }
            }
        }
        if (this.$debug) {
            log(this.name, "HUDs: " + this.$HUDSelectorHUDs);//debug
            for (var i = 0; i < this.$HUDSelectorHUDs.length; i++)
                log(this.name, i + ": " + this.$HUDSelectorHUDs[i]);
        }
    
        this.$setInterface();
    }
    
    //---------------------------------------------------------------------------------
    this.guiScreenChanged = function (to, from) {
        if (!player.ship) return;
        if (to === "GUI_SCREEN_OPTIONS") {
            player.ship.hudHidden = false; //fix problems in other addons
        }
    }
    
    //---------------------------------------------------------------------------------
    this.playerBoughtEquipment = function (equipmentKey) {
        if (equipmentKey == "EQ_HUDSELECTOR_REMOVE") {
            var eq = equipmentKey.substr(0, equipmentKey.length - 7);//the real eq
            player.ship.removeEquipment(eq); //remove it
            player.ship.removeEquipment(equipmentKey); //remove the "bought" remove eq
        }
    }
    
    //---------------------------------------------------------------------------------
    this.playerWillSaveGame = function (message) {
        missionVariables.$HUDSelectorHUD = this.$HUDSelectorHUDs[this.$HUDSelectorEqSel][2];
        //missionVariables.$HUDSelectorDefaultMFDs = this.$HUDSelectorDefaultMFDs;  //store default MFDs
        missionVariables.$HUDSelectorDefaultMFDs_new = JSON.stringify(this.$HUDSelectorDefaultMFDs);
        missionVariables.$HUDSelectorScanner = this.$HUDSelectorScanner;
        if (this.$combatWS && this.$combatWS.$DataShow)
            missionVariables.$HUDSelectorDataShow = JSON.stringify(this.$combatWS.$DataShow);
    }
    
    //---------------------------------------------------------------------------------
    this.shipLaunchedFromStation = function () {
        this.$HUDSelectorSetScanner(this.$HUDSelectorScanner); //restore the scanner linear and zoom settings
        player.ship.hudHidden = false; //fix problems in other addons
    }
    
    //---------------------------------------------------------------------------------
    this.shipTargetAcquired = this.shipTargetLost = function () {
        //Must fix after each target loss due to Auto Crosshairs OXP break the scanner settings
        if (worldScripts["Auto Crosshairs"]) this.$C = -1;
    }
    
    //---------------------------------------------------------------------------------
    this.shipWillDockWithStation = function (station) {
        var l = player.ship.multiFunctionDisplayList;
        if (l) { //from Oolite 1.81
            //        log(this.name, "MFDList: "+l);//debug
            worldScripts.hudselector.$HUDSelectorDefaultMFDs = l; //save in-flight changes
        }
    }
    
    //---------------------------------------------------------------------------------
    this.shipDockedWithStation = function (station) {
        this.$setInterface();
    }
    
    //---------------------------------------------------------------------------------
    this.shipWillLaunchFromStation = function () {
        this.$HUDSelectorSetMFDs(); //must setup MFDs here and not earlier in startup()
        player.ship.hudHidden = false; //fix problems in other addons
    }
    
    //
    //HUDSelector methods
    //
    
    //---------------------------------------------------------------------------------
    this.$setInterface = function () {
        this.$HUDSelectorDialNo = 0;
        player.ship.dockedStation.setInterface("HUD and MFD selector", {
            title: "HUD and MFD selector",
            category: "Ship Systems",
            summary: "Select your HUD and the default Multi-Function Displays (MFDs).",
            callback: this.$HUDSelector_Interface.bind(this)
        });
    }
    
    //---------------------------------------------------------------------------------
    this.$HUDSelectorAddHUD = function (hudname, plist4, plist10, plist9) { //for 4:3, 16:10 and 16:9
        var ws = null;
        if (worldScripts[plist4])
            ws = plist4;
        else {
            var i = plist4.indexOf("-");
            if (i >= 0 && worldScripts[plist4.slice(0, i)])
                ws = plist4.slice(0, i);
        }
        worldScripts.hudselector.$HUDSelectorHUDs.push([ws, hudname, plist4, plist10, plist9]);
    }
    
    //---------------------------------------------------------------------------------
    this.$HUDSelectorAddMFD = function (mfdworldscript, mfdname, displayName) {
        if (!mfdworldscript) return; //this one is really needed
        if (!mfdname) mfdname = mfdworldscript; //if the mfd name is missing then assume than equal with wsname
        if (!displayName) displayName = mfdname;
    
        var mfdin = false;
        for (var i = 0; i < this.$HUDSelectorMFDs.length; i++) {
            if (this.$HUDSelectorMFDs[i] && mfdworldscript == this.$HUDSelectorMFDs[i][0] &&
                (!mfdname || mfdname === mfdworldscript || mfdname === this.$HUDSelectorMFDs[i][1]))
                mfdin = true;
        }
        if (!mfdin) this.$HUDSelectorMFDs.push([mfdworldscript, mfdname, displayName]);
    }
    
    //---------------------------------------------------------------------------------
    this.$HUDSelectorRestoreHUD = function $HUDSelectorRestoreHUD() { //call after all startUp finished where other worldScripts can add HUDs
        var hud = missionVariables.$HUDSelectorHUD; //restore saved HUD
        if (hud && hud.length > 0) {
            var i = this.$HUDSelectorHUDs.length;
            while (i--) {
                if (this.$HUDSelectorHUDs[i][2] == hud) {
                    this.$HUDSelectorEqSel = i;
                    break;
                }
            }
        }
    }
    
    //---------------------------------------------------------------------------------
    this.$HUDSelectorSetHUD = function $HUDSelectorSetHUD(prev) {
        var s = this.$HUDSelectorHUDs[this.$HUDSelectorEqSel];
        var p = s[2]; //plist for 4:3, universal plist or scripted in the oxp of the selected HUD
        if (s[3] || s[4]) {
            var o = oolite.gameSettings.gameWindow;
            var wide = o.width / o.height; ///
            var wn = 2; //4:3 (default)
            if (wide > 1.75) wn = 4; //1.77 = 16:9 screen
            else if (wide > 1.55) wn = 3; //1.6 = 16:10 screen, used in extralarge hud
            //        log(this.name, "Wide: "+w+" "+wn);//debug
            if (s[wn]) p = s[wn];
        }
        player.ship.hud = p + ".plist"; //set the selected HUD
    
        this.$HUDSelectorSetScanner(this.$HUDSelectorScanner); //restore the scanner linear and zoom settings
    
        var sel_ws = this.$HUDSelectorHUDs[this.$HUDSelectorEqSel][0];
    
        // there might be more than one HUD in the same worldScript, and the chosen might not be the last...
        // first de-activate the un-chosen ones
        if (prev && prev >= 0 && prev < this.$HUDSelectorHUDs.length) {
            // just call the previous hud's callback
            var wh = worldScripts[this.$HUDSelectorHUDs[prev][0]];
            if (wh && wh.$HUDSelectorCallBack)
                wh.$HUDSelectorCallBack(true);
        } else {
            var i = this.$HUDSelectorHUDs.length;
            var calls = [];
            while (i--) { //call callback functions
                if (i != this.$HUDSelectorEqSel) {
                    var ws = this.$HUDSelectorHUDs[i][0];
                    var wh = worldScripts[ws];
                    // only call the callback if it's valid, the function exists, 
                    //  it's not the worldscript of our selected hud and we haven't called this worldscript already
                    if (wh && wh.$HUDSelectorCallBack && ws != sel_ws && calls.indexOf(ws) == -1) {
                        calls.push(ws);
                        wh.$HUDSelectorCallBack(true);
                    }
                }
            }
        }
        // activate the chosen one 
        wh = worldScripts[sel_ws];
        if (wh && wh.$HUDSelectorCallBack) {
            wh.$HUDSelectorCallBack(false);
        }
        this.$HUDSelectorSetMFDs();
    }
    
    //---------------------------------------------------------------------------------
    this.$HUDSelectorSetMFDs = function () {
        var def_mfds = this.$HUDSelectorDefaultMFDs;
        var mfds = this.$HUDSelectorMFDs;
        var i = def_mfds.length;
        while (i--) {
            if (def_mfds[i]) {
                var w = def_mfds[i]; //worldScript or MFD name
                var mfd = -1;
                if (w && w.length > 0 && w != "undefined") {
                    var j = mfds.length;
                    while (j--) {
                        if (mfds[j][0] == w || mfds[j][1] == w) {
                            mfd = j; 
                            break;
                        }
                    }
                }
                var m = null;
                if (mfd > -1) m = mfds[mfd][1]; //MFD name
                if (!m) m = w; //mfd name is equal with worldScript name
                if (m && w != "undefined" && worldScripts[w])
                    player.ship.setMultiFunctionDisplay(i, m);
                else if (w && w.length > 0 && w != "undefined") 
                    player.ship.setMultiFunctionDisplay(i, w);
                else 
                    player.ship.setMultiFunctionDisplay(i, "");
            }
        }
    }
    
    //---------------------------------------------------------------------------------
    this.$HUDSelectorSetScanner = function (b) {
        if (b == 1 || b == 3) player.ship.scannerNonLinear = true;
        else player.ship.scannerNonLinear = false;
        if (b == 2 || b == 3) player.ship.scannerUltraZoom = true;
        else player.ship.scannerUltraZoom = false;
        this.$HUDSelectorScanner = b;
    }
    
    //---------------------------------------------------------------------------------
    this.$HUDSelector_FixFCB = function (delta) { //FrameCallBack for fixing crosshairs change bug
        if (player && player.ship) {
            //detect crosshairs change made by Auto Crosshairs OXP
            if (this.$C != player.ship.crosshairs && this.$HUDSelectorFix) { //can be disabled by this variable
                this.$C = player.ship.crosshairs;
                this.$HUDSelectorSetScanner(this.$HUDSelectorScanner); //fix scanner settings cleared by crosshairs change
            }
            if (this.$H != player.ship.hud && this.$HUDSelectorFix) { //can be disabled by this variable
                this.$H = player.ship.hud; //a scripted hud changed the actual plist and reverted back the scanner settings
                this.$HUDSelectorSetScanner(this.$HUDSelectorScanner); //fix scanner settings cleared by crosshairs change
            }
            var l = player.ship.scannerNonLinear;
            var z = player.ship.scannerUltraZoom;
            if (this.$L != l || this.$Z != z) { //log scanner settings changes and crosshair problems
                this.$L = l;
                this.$Z = z;
                if (this.$HUDSelectorFixInEveryFrame) this.$HUDSelectorSetScanner(this.$HUDSelectorScanner);
                else log(this.name, "Scanner NonLinear:" + l + " UltraZoom:" + z
                    + " HUD:" + player.ship.hud + " Crosshairs: " + player.ship.crosshairs
                    + ", scanner settings is not fixed.");
            }
        }
    }
    
    //---------------------------------------------------------------------------------
    this.$HUDSelector_Interface = function () {
        player.ship.hudHidden = false;
        //this.$HUDSelectorSetHUD();
        var c = [];
        var title = "HUD selector";
        var len = this.$HUDSelectorHUDs.length;
        var maxc = 18; //max.22 lines in total with mfd, custom dials, more and exit
        if (!player.ship.hudAllowsBigGui) maxc = 14;
        var dialno = this.$HUDSelectorDialNo;
        if (dialno >= len) dialno = this.$HUDSelectorDialNo = 0; //go back to the start
        for (var j = dialno; j < dialno + maxc && j < len; j++)
            c[j] = this.$padText((j + 1) + ". " + this.$HUDSelectorHUDs[j][1], 20);
        var cds = "Combat MFD custom dials setup";
        if (!player.ship.setCustomHUDDial) cds += " (need Oolite 1.81)";
        if (!this.$combatWS || !this.$combatWS.$DataArray) cds += " (need CombatMFD 1.8)";
        var ch = {
            "_0": { text: "MFD setup", color: "orangeColor" },
            "_00": { text: cds, color: "orangeColor" },
            "_ME": { text: "Exit", color: "orangeColor" },
        };
        for (var i = 0; i < maxc; i++) {
            if (dialno + i < len) ch["_" + (i + 10).toString()] = c[dialno + i];
        }
        if (len > maxc) {
            ch["_M"] = { text: "More", color: "orangeColor" };
        }
        var ic = "_ME";
        if (this.$HUDSelectorEqSel >= dialno && this.$HUDSelectorEqSel < dialno + maxc)
            ic = "_" + (10 + this.$HUDSelectorEqSel - dialno);
        mission.runScreen(
            {
                title: title,
                screenID: "HUDSelector_HUDs",
                message: "Select your preferred HUD from the options below.\nUp/down arrows to select a HUD.\nPress Enter to make it your default.",
                background: this.$BG,
                allowInterrupt: true,
                exitScreen: "GUI_SCREEN_INTERFACES",
                registerKeys: {"esc":[{key:"esc"}]},
                spinModel: false,
                initialChoicesKey: ic,
                choices: ch
            },
            this.$hudSelector_Choice, this);
    }
    
    //-------------------------------------------------------------------------------------------------------------
    this.$hudSelector_Choice = function (choice, key) {
        var maxc = 18; //max.22 lines in total with mfd, custom dials, more and exit
        this.$HUDSelectorChoice = choice;
        if (choice == "_ME" || key== "esc") {
            this.$HUDSelectorDialNo = 0; //start in the first page next time
            return;
        }
        if (choice == "_0") {
            this.$HUDSelectorChoice = null;
            this.$HUDSelectorDialNo = 0;
            this.$HUDSelector_InterfaceMFD();
            this.$HUDSelectorDialNo = 0; //start in the first page next time
            return;
        }
        if (choice == "_00") {
            if (!player.ship.setCustomHUDDial) {
                player.consoleMessage("Custom dials need Oolite 1.81", 10);
            } else if (!this.$combatWS || !this.$combatWS.$DataArray) {
                player.consoleMessage("Custom dials need CombatMFD 1.8", 10);
            } else {
                this.$HUDSelectorChoice = null;
                this.$HUDSelectorDialNo = 0;
                this.$HUDSelector_InterfaceDial();
                this.$HUDSelectorDialNo = 0; //start in the first page next time
                return;
            }
        }
        if (choice == "_M") {
            this.$HUDSelectorChoice = null;
            this.$HUDSelectorDialNo += maxc;
        } else if (choice != "_00") {
            var t = -1;
            if (choice) t = choice.substr(1) - 10; //cut starting "_"
            //               log(this.name, "MFD choice: "+choice+" "+t);//debug
            if (t >= 0) {
                t += this.$HUDSelectorDialNo;
                var prev = this.$HUDSelectorEqSel;
                this.$HUDSelectorEqSel = t;
                player.consoleMessage("HUD set to " + $HUDSelectorHUDs[t][1]);
                this.$HUDSelectorSetHUD(prev);
            }
        }
        this.$HUDSelector_Interface();
    }
    
    //---------------------------------------------------------------------------------
    this.$HUDSelector_InterfaceMFD = function () {
        //    player.ship.hudHidden = true; - cause MarketObserver disappear from F8 screen
        var title = "Default MFD selector";
        var p = player.ship;
        var len = 40;
        if (p && p.isValid) len = p.multiFunctionDisplays;
        var maxc = 20; //max.22 lines in total with done and exit
        var dialno = this.$HUDSelectorDialNo;
        if (dialno >= len) dialno = this.$HUDSelectorDialNo = 0; //go back to the start
        var index = 10;
        var ch = {};
        for (var j = dialno; j < dialno + maxc && j < len; j++) {
            var z = this.$lookupMFDDisplayName(this.$HUDSelectorDefaultMFDs[j]);
            if (!z) z = "undefined";
            ch["_" + index.toString()] = this.$padText("MFD slot " + (j + 1) + ":", 6) + this.$padText(z, 17);
            index += 1;
        }
        // only put the "more" item on if there are more to show
        if (len > maxc) ch["_M"] = { text: "More", color: "orangeColor" };
        ch["_ME"] = { text: "Return", color: "orangeColor" };
        var ic = "_ME";
        if (this.$HUDSelectorChoice) ic = this.$HUDSelectorChoice;
        // checking for the presence of a new function in the global object that indicates the version of Oolite is sufficient for the extra keys to work
        mission.runScreen(
            {
                title: title,
                screenID: "HUDSelector_MFDs",
                message: "Select which MFDs will be displayed in the available slots of your HUD by default. " +
                    "These settings will be saved with your game and restored whenever you launch.\n\nUp/down arrows select MFD slot. " +
                    (global.getColorSaturation ? "Left/right arrows select MFD content." : "Enter to cycle MFD content."),
                background: this.$BG,
                spinModel: false,
                registerKeys: { "left": [{ key: "arrowLeft" }], "right": [{ key: "arrowRight" }], "esc":[{key:"esc"}] },
                allowInterrupt: true,
                initialChoicesKey: ic,
                exitScreen: "GUI_SCREEN_INTERFACES",
                choices: ch
            },
            this.$mfdSelector_Choice, this);
    }
    
    //-------------------------------------------------------------------------------------------------------------
    this.$mfdSelector_Choice = function (choice, key) {
        var maxc = 20; //max.22 lines in total with done and exit
        this.$HUDSelectorChoice = choice;
        if (((!key || key == null || key == "enter") && choice == "_ME") || key == "esc") {
            this.$HUDSelector_Interface();
            return;
        }
        if ((key == null || key == "enter") && choice == "_M") {
            this.$HUDSelectorChoice = null;
            this.$HUDSelectorDialNo += maxc;
        } else {
            var t = -1;
            if (choice) t = choice.substr(1) - 10; //cut starting "_"
            //            log(this.name, "MFD choice: "+choice+" "+t);//debug
            if (t >= 0) {
                t += this.$HUDSelectorDialNo;
                var m = this.$HUDSelectorDefaultMFDs[t];
                var mfd = 0;
                if (m && m.length > 0 && m != "undefined") {
                    for (var i = 0; i < this.$HUDSelectorMFDs.length; i++) {
                        if (this.$HUDSelectorMFDs[i] &&
                            (m == this.$HUDSelectorMFDs[i][0] || m == this.$HUDSelectorMFDs[i][1]))
                            mfd = i + (key == "left" ? -1 : 1); //step to the next MFD
                    }
                }
                if (mfd >= this.$HUDSelectorMFDs.length || mfd < 0) {
                    this.$HUDSelectorDefaultMFDs[t] = "undefined";
                    if (mfd == -1) mfd = this.$HUDSelectorMFDs.length - 1;
                } else {
                    var f = this.$HUDSelectorMFDs[mfd][1]; //mfdname
                    if (!f) f = this.$HUDSelectorMFDs[mfd][0]; //worldscriptname
                    this.$HUDSelectorDefaultMFDs[t] = f;
                }
            }
        }
        this.$HUDSelector_InterfaceMFD();
    }
    
    //---------------------------------------------------------------------------------
    this.$lookupMFDDisplayName = function (mfdname) {
        for (var i = 0; i < this.$HUDSelectorMFDs.length; i++) {
            if (this.$HUDSelectorMFDs[i][1] == mfdname || this.$HUDSelectorMFDs[i][0] == mfdname) return this.$HUDSelectorMFDs[i][2];
        }
        return mfdname;
    }
    
    //---------------------------------------------------------------------------------
    this.$HUDSelector_InterfaceDial = function () {
        var c = [];
        var title = "Combat MFD Custom dials setup";
        var len = this.$combatWS.$DataArray.length;
        var maxc = 20;//max.22 lines in total with more and exit
        if (!player.ship.hudAllowsBigGui) maxc = 14;
        var more = null;
        if (len > maxc) more = "More";
        var dialno = this.$HUDSelectorDialNo;
        if (dialno >= len) dialno = this.$HUDSelectorDialNo = 0; //go back to the start
        for (var j = dialno; j < dialno + maxc && j < len; j++) {
            var combatKey = this.$combatWS.$DataArray[j];
            if (combatKey) {
                c[j] = this.$padText((j + 1) + ". " + combatKey + ": ", 10) + this.$padText(this.$combatWS.$DataShow[combatKey].toString(), 5);
            }
        }
        var ch = {
            "_M": { text: more, color: "orangeColor" },
            "_ME": { text: "Return", color: "orangeColor" },
        };
        for (var i = 0; i < maxc; i++) {
            if (dialno + i < len) ch["_" + (i + 10).toString()] = c[dialno + i];
        }
        var ic = "_M";
        if (this.$HUDSelectorChoice) ic = this.$HUDSelectorChoice;
        mission.runScreen(
            {
                title: title,
                screenID: "HUDSelector_Dials",
                message: "Controls which of the values from Combat MFD will be updated in any custom HUD dials that have been set up in the HUD." +
                    "\nNote: Only HUDs which have added custom dials linked to these Combat HUD values will be impacted.\nUp/down arrows to select value. Enter to change value.",
                background: this.$BG,
                allowInterrupt: true,
                exitScreen: "GUI_SCREEN_INTERFACES",
                registerKeys: {"esc":[{key:"esc"}]},
                spinModel: false,
                initialChoicesKey: ic,
                choices: ch
            },
            this.$dialSelector_Choice, this);
    }
    
    //-------------------------------------------------------------------------------------------------------------
    this.$dialSelector_Choice = function (choice, key) {
        var maxc = 20;//max.22 lines in total with more and exit
        if (player.ship.hudAllowsBigGui == false) maxc = 14;
        this.$HUDSelectorChoice = choice;
        if (choice == "_ME" || key == "esc") {
            this.$HUDSelector_Interface();
            return;
        }
        if (choice == "_M") {
            this.$HUDSelectorChoice = null;
            this.$HUDSelectorDialNo += maxc;
        } else {
            var t = -1;
            if (choice) t = choice.substr(1) - 10; //cut starting "_"
            if (t >= 0) {
                t += this.$HUDSelectorDialNo;
                var combatKey = this.$combatWS.$DataArray[t];
                this.$combatWS.$DataShow[combatKey] = //inverse
                    !this.$combatWS.$DataShow[combatKey];
                if (!this.$combatWS.$DataShow[combatKey]) //clear the old content
                    player.ship.setCustomHUDDial(combatKey, "");
            }
        }
        this.$HUDSelector_InterfaceDial();
    }
    
    //-------------------------------------------------------------------------------------------------------------
    // appends space to currentText to the specified length in 'em'
    this.$padText = function $padText(currentText, desiredLength, leftSwitch, centreSwitch) {
        var that = $padText;
        var hairSpace = (that.hairSpace = that.hairSpace || this._hairSpace);
        var ellip = (that.ellip = that.ellip || this._ellip);
        var hairSpaceLength = (that.hairSpaceLength = that.hairSpaceLength || this._hairSpaceLength);
        var measure = (that.measure = that.measure || defaultFont.measureString);
    
        if (currentText == null) currentText = "";
        var currentLength = measure(currentText.replace(/%%/g, "%"));
        // calculate number needed to fill remaining length
        var padsNeeded = Math.floor((desiredLength - currentLength) / hairSpaceLength);
        if (padsNeeded < 1) {
            // text is too long for column, so start pulling characters off
            var tmp = currentText;
            do {
                tmp = tmp.substring(0, tmp.length - 2) + ellip;
                if (tmp === ellip) break;
            } while (measure(tmp.replace(/%%/g, "%")) > desiredLength);
            currentLength = measure(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) {
            if (!centreSwitch || centreSwitch === false) {
                return currentText + new Array(padsNeeded).join(hairSpace);
            } else {
                return currentText + new Array(parseInt(padsNeeded / 2)).join(hairSpace);
            }
        } else {
            if (!centreSwitch || centreSwitch === false) {
                return new Array(padsNeeded).join(hairSpace) + currentText;
            } else {
                return new Array(parseInt(padsNeeded / 2)).join(hairSpace) + currentText;
            }
        }
    }