Back to Index Page generated: Nov 12, 2024, 11:02:04 PM

Expansion Hyperspace Hangar

Content

Warnings

  1. Unknown key 'upload_date' at https://wiki.alioth.net/img_auth.php/4/44/HyperspaceHangar-1.17.0.oxz!manifest.plist

Manifest

from Expansion Manager's OXP list from Expansion Manifest
Description Invented by a small time noodle shop owner, it allows storage of ships in the Hyperspace Hangar service. OXPConfig recommended. Invented by a small time noodle shop owner, it allows storage of ships in the Hyperspace Hangar service. OXPConfig recommended.
Identifier oolite.oxp.Ramen.Hyperspace_Hangar oolite.oxp.Ramen.Hyperspace_Hangar
Title Hyperspace Hangar Hyperspace Hangar
Category Mechanics Mechanics
Author Ramen Ramen
Version 1.17.0 1.17.0
Tags
Required Oolite Version
Maximum Oolite Version
Required Expansions
  • oolite.oxp.CaptMurphy.ShipStorageHelper:0.35
  • oolite.oxp.CaptMurphy.ShipStorageHelper:0.35
  • Optional Expansions
  • oolite.oxp.Svengali.Library:1.7.1
  • oolite.oxp.Svengali.Library:1.7.1
  • Conflict Expansions
    Information URL http://wiki.alioth.net/index.php/Hyperspace_Hangar n/a
    Download URL https://wiki.alioth.net/img_auth.php/4/44/HyperspaceHangar-1.17.0.oxz http://wiki.alioth.net/img_auth.php/6/61/Hyperspace_Hangar-1.14.9.oxz
    License CC-BY-NC-SA 3.0 CC-BY-NC-SA 3.0
    File Size n/a
    Upload date 1669214381

    Documentation

    Also read http://wiki.alioth.net/index.php/Hyperspace%20Hangar

    README.md

    #Oolite-Hyperspace-Hangar
    Written by Ramen, with credit to Capt. Murphy and Norby, and Norby again, for
    providing a fix for the one-ship-type-only bug, and lots more Norby.
    Credit goes to Kheng for providing the hangar image.
    (http://kheng.deviantart.com)
    
    For installation instructions, see INSTALL.md
    
    Development thread located at: http://aegidian.org/bb/viewtopic.php?f=4&t=17437
    
    Git repository: https://github.com/rotaryphone111/Oolite-Hyperspace-Hangar
    
    Maintainer contact information:
    Name: Kenji Takashima. Email: knjtkshm@gmail.com
    ##Changelog:
    --------------------------------------------------------------------------------
    ### Version 1.17.0
    * Preserves libconfig option to savefile;
    * Portable and pylon equipment is not passed on to bought ship if old ship was stored.
    * Shows Station Interface even if no ship is stored in Hyperspace Hangar.
    * Sell price when selling ships through HH is 15% of full price.
    * Damage might occur when switching into ships in station of TL<7.
    * Nest try/catch in the sell stored ship option so if an error occur when replacing ship we can revert the operation.
    * Check and reports insufficient funds for a ship transfer.
    * Creates a in-transit state for stroed ships: ordering ships transfered to current system do not advance the game clock to the ship arrival moment anymore.
    * Fixes sell option station interface screens flux.
    * Selling a stores ship doesn't advance game clock.
    * Expands "ships in transit" to all order or send orders.
    * Shows ships in transit in the Hyperspace Hangar station interface.
    * Transit time information in hours is displayed with single decimal digit
    
    ### Version 1.16.0 
    * Fixed compatibility issue with Ship Version, as well as adding pagination for when more that 1 page of ships are stored (courtesy of dybal).
    
    ### Version 1.15.1
    * Included Hypercargo bays (including Vortex and Maelstom ships) when removing 
    cargo on ship before storage.
    
    ### Version 1.15.0 
    * Fixed major bug with not taking away the correct number of credits when storing ships, meaning players could easily get massive numbers of credits by buying, storing and selling ships.
    * Adjusted position of text on transfer ship map screens.
    * Various tweaks to exit screens and menu flow.
    
    ### Version 1.14.12
    * Bug fixes, spelling corrections. 
    * Made default exit points for mission screens to be F4 interfaces page.
    
    ### Version 1.14.11
    * Attempt to fix issue with New Lasers not being able to be reinstalled.
    
    ### Version 1.14.5
    * Thu Aug 13 13:07:18 PDT 2015 - Added INSTALL.md, 
    * Renamed LICENSE.txt and README.txt to .md files. Added git repo to github.
    
    ### Version 1.14.4
    * Mon Aug 10 15:48:16 PDT 2015 - Removed test message that was left in by accident.
    
    ### Version 1.14.3
    * Mon Aug 10 15:21:34 PDT 2015 - Fixed incorrect ship pricing, which was causing several bugs, including ships being sold, Ships Not Being Removed(tm), etc.
    * Also fixed the hangar not being activated. Turned oxp into git repo. (Not on the internet yet.)
    
    ### Version 1.14.2
    * Fri Jul  3 12:58:53 PDT 2015 - I moved to California, so dates will now be in PDT. 
    * I am also updating the changelog format to include version numbers from this point forward.
    
    Older Versions:
    -------------------------------------------------------------------------------
    - Tue Jun 16 23:28:02 EDT 2015 - Reinstated switch damage, due to annoying
    implementation bugs. Added short range chart functionality if target world is
    less than or equal to 7.0 LY away. Added intergalactic ordering.
    - Mon Jun 15 17:34:06 EDT 2015 - Changed damage so that it happens when sending
    and ordering, not switching.
    - Sun Jun 14 20:28:05 EDT 2015 - Added galactic chart display to send function.
    Added ability to use function keys to switch out of some hangar screens.
    (not all)
    - Sun Jun 14 12:49:25 EDT 2015 - Added a need to order ahead or wait for ships to
    arrive if switching. Selling remains unchanged due to the galactic internet. (or
    something) Added possibility of damage if the docked station's tech level is
    below 6. 
    - Fri Jun 12 11:54:43 EDT 2015 - Made it so that only stations with shipyards have
    the hangar. Fixed changelog typo.
    - Wed Jun 10 00:07:37 EDT 2015 - Added messages confirming actions, e.g. selling
    a ship. Also fixed a bug with ships selling when they are not supposed to.
    Also added spiffy lines to changelog.(Now to write a program MADE from spiffy
    lines that adds MORE spiffy lines that do the same thing.)
    - Tue Jun  9 22:44:15 EDT 2015 - fixed a ship-won't-sell bug and added Xenon UI
    compatibility
    - Tue Jun  9 15:00:36 EDT 2015 - added image of hangar with rotating model of ship
    (thanks to Kheng: http://kheng.deviantart.com). Also fixed another storage bug.
    - Mon Jun  8 14:56:22 EDT 2015 - added ship removal option and made OXPConfig
    optional
    - Sun Jun  7 18:49:06 EDT 2015 - fixed ship-non-deletion bug. (hopefully)
    - Sun Jun  7 12:11:14 EDT 2015 - rediscovered ship-non-deletion bug. Fixed pricing
    bug. (Thanks Day!)
    - Fri Jun  5 14:08:11 EDT 2015 - fixed bug where ships would not delete themselves
    after not buying anything and moving away from the shipyard interface.
    - Wed May 27 16:16:01 EDT 2015 - Updated hangar so that options have to be
    activated from OXPConfig. Added OXPConfig as a dependency. Removed accidentally
    added .diff files from oxz.
    - Fri May 15 14:09:05 EDT 2015 - implemented bug fix from Norby, added
    instructions for activating hangar.
    - Wed May 13 18:33:33 EDT 2015 - added selling of stored ships and warning
    messages for selling ships. also improved code readability.
    - Wed May 13 17:59:04 EDT 2015 - added many things, forgot to put them here.
    Added a ship storage delay and fee, made those adjustable, and created options
    menu. Also added defaults restore option and added displays of the current delay
    and price. 
    - Sat May  9 20:31:16 EDT 2015 - fixed one-ship-type-only bug, thanks Norby!
    Also changed dating system.
    - 5/9/15 - updated manifest.plist to include license.
    - 5/9/15 - first version.
    

    Equipment

    This expansion declares no equipment. This may be related to warnings.

    Ships

    This expansion declares no ships. This may be related to warnings.

    Models

    This expansion declares no models. This may be related to warnings.

    Scripts

    Path
    Scripts/hyperhangar.js
    "use strict";
    this.name           = "HyperspaceHangar";
    this.author         = "Kenji Takashima";
    this.copyright      = "(C) 2015 Kenji Takashima.";
    this.licence        = "CC-BY-NC-SA 3.0";
    this.description    = "Provides a hangar for your ships across the stars.";
    this.version        = "1.17.0";
    
    this._currentNames = {};
    this._shipsStored = {};
    this._shipsInTransit = {};
    this._currentShip = "";
    this._currentName = "";
    this._currentPortable = [];
    this._currentPylonMounted = [];
    //Defaults
    this._defaultpriceOption = 250000;
    this._defaulttimeOption = 1800;
    //OXPConfig related options
    this._showOptions = false; // show Options in HH mission screen at Station Interfaces?
    this._percentPrice = false; // Storage fee is based on ship's price?
    this._shipPercent = 10;
    this._shipDepreciation = 0.15; // How much of the ship trade in value we pay
    this._allowsCargo = true;
    this._svEnabled = false;  // ShipVersion installed?
    this._cost = 0;
    this._tradeIn = 0;
    this._hudHidden;
    this._debug = false;
    
    // settings for Library Config
    this._hhSettings = {
        Name:   this.name,
        Display:"Settings",
        Alias:  "Hyperspace Hangar",
        Alive:  "_hhSettings",
        Bool: {
            B0: {
                Name:   "_showOptions",
                Desc:   "Show Options",
                Def:    false,
            },
            B1: {
                Name:   "_percentPrice",
                Desc:   "Storage Fee",
                Def:    false,
                Notify: "$storageFeeChanged"
            },
            Info:   "Show Options - Displays price and time options at HH menu\n"+
                    "Storage Fee - true: based on ship 's price, false: flat fee"
        },
        SInt: {
            S0: {
                Name:   "_shipPercent",
                Desc:   "Storage Fee",
                Def:    10,
                Min:    1,
                Max:    50,
                Hide:   true
            },
            Info:   "Storage Fee - percent of ship's price, values from 1 to 50"
        }
    };
    
    
    //
    // Event Handlers
    //
    
    //---------------------------------------------------------------------------------//
    this.startUp = function _HH_startUp() {
        //first time setup
        if (!missionVariables.hyperSpace_namesStored) {
            missionVariables.hyperSpace_namesStored = JSON.stringify({"0_EXIT": "EMPTY"});
            missionVariables.hyperSpace_shipsStored = JSON.stringify({});
            missionVariables.hyperSpace_shipsInTransit = JSON.stringify({});
            missionVariables.hyperSpace_priceOption = this._defaultpriceOption;
            missionVariables.hyperSpace_timeOption = this._defaulttimeOption;
            missionVariables.hyperSpace_showOptions = 0;
            missionVariables.hyperSpace_percentPrice = 0;
            missionVariables.hyperSpace_shipPercent = 10;
        }
        //mission setup
        if (!missionVariables.hyperSpace_mission) {
            missionVariables.hyperSpace_mission = "NOT_ACTIVATED";
        }
    
        //check if ShipVersion is installed (it changes the ship buying screen sequence)
        if (worldScripts.shipversion) this._svEnabled = true;
    
        //names of ships stored. Used because $shipsStored would give JSON strings
        this._currentNames = JSON.parse(missionVariables.hyperSpace_namesStored);
        if (this._currentNames["1_EXIT"]) delete this._currentNames["1_EXIT"];
    
        //values of ships stored.
        this._shipsStored = JSON.parse(missionVariables.hyperSpace_shipsStored);
        //value sof ships in transit
        if (missionVariables.hyperSpace_shipsInTransit) this._shipsInTransit = JSON.parse(missionVariables.hyperSpace_shipsInTransit);
        this.$cleanUp();
    
    
        if (this._debug) {
            log(this.name, "There are "+Object.keys(this._shipsStored).length+" ships stored");
            for (k in this._currentNames) {
                let name = this._currentNames[k];
                log(this.name, k+": "+name+", "+JSON.stringify(this._shipsStored[k]));
            }
        }
        //constants for tests
        this._shipPrice = 0;
        this._actuallyBought = 0;
        this._delay = false;
    
        //current stored ship value; very important
        this._shipTempKey = null;
    
        //self explanatory
        this._oldChoice = 0;
    
        //saved percentPrice option
        this._percentPrice = (missionVariables.hyperSpace_percentPrice == 1 ? true : false);
        this._showOptions = (missionVariables.hyperSpace_showOptions == 1 ? true : false);
        this._shipPercent = (missionVariables.hyperSpace_shipPercent ? missionVariables.hyperSpace_shipPercent : 10);
        this.$storageFeeChanged();
    
        //current prices and storage time
        this._priceOption = missionVariables.hyperSpace_priceOption;
        this._timeOption = missionVariables.hyperSpace_timeOption;
    
        //information for the transfer function
        this._oldShips = [];
        this._arrivalTime = {};
    
        //compatibility with Xenon UI
        var w = worldScripts.XenonUI;
        if (w) w.$addMissionScreenException("HyperspaceHangar-map");
        var wx = worldScripts.XenonReduxUI;
        if (wx) wx.$addMissionScreenException("HyperspaceHangar-map");
    }
    
    //---------------------------------------------------------------------------------//
    this.startUpComplete = function _HH_startUpComplete() {
        if (worldScripts.Lib_Config) 
            worldScripts.Lib_Config._registerSet(this._hhSettings);
        this.$dockedCheck();
    }
    
    //---------------------------------------------------------------------------------//
    this.shipDockedWithStation = function _HH_shipDockedWithStation() {
        this.$dockedCheck();
    }
    
    //---------------------------------------------------------------------------------//
    this.playerEnteredNewGalaxy = function _HH_playerEnteredNewGalaxy() {
        //mark mission system if in G6 and mission has been given
        if (galaxyNumber === 5 && missionVariables.hyperSpace_mission === "DELIVER") {
            mission.markSystem({
                system: 149,
                name: "hyperspaceMission",
                markerColor: "greenColor",
                markerScale: 1.5,
                markerShape: "MARKER_DIAMOND"
            });
        }
    }
    
    //---------------------------------------------------------------------------------//
    this.playerWillSaveGame = function _HH_playerWillSaveGame() {
        this.$cleanUp();
        missionVariables.hyperSpace_namesStored = JSON.stringify(this._currentNames);
        missionVariables.hyperSpace_shipsStored = JSON.stringify(this._shipsStored);
        missionVariables.hyperSpace_shipsInTransit = JSON.stringify(this._shipsInTransit);
        missionVariables.hyperSpace_priceOption = this._priceOption;
        missionVariables.hyperSpace_timeOption = this._timeOption;
        missionVariables.hyperSpace_percentPrice = (this._percentPrice ? 1 : 0);
        missionVariables.hyperSpace_showOptions = (this._showOptions ? 1 : 0);
        missionVariables.hyperSpace_shipPercent = this._shipPercent;
    }
    
    //---------------------------------------------------------------------------------//
    this.guiScreenChanged = function _HH_guiScreenChanged(to, from) {
        var that = _HH_guiScreenChanged;
        var wsSSH = (that.wsSSH = that.wsSSH || worldScripts["Ship_Storage_Helper.js"]);
        //store player's ship if replacement is a possibility.
        if (guiScreen === "GUI_SCREEN_SHIPYARD") {
            var _ship = player.ship;
            this._shipTempKey = clock.absoluteSeconds;
            this._currentShip = wsSSH.storeCurrentShip(player.ship);
            this._currentName = _ship.displayName;
            this._currentPortable = this.$getPortableEquipment(_ship);
            this._currentPylonMounted = this.$clearPylons(_ship);
            this._shipPrice = _ship.price;
            this._tradeIn = this.$tradeInAmount();
            this._actuallyBought = 1;
        } else {
            //has a ship been bought?
            if (from == "GUI_SCREEN_SHIPYARD" && clock.isAdjusting) {
                this._delay = true;
                return;
            } else if (this._delay == false && this._actuallyBought == 1) {
                this.$reinstatePylons(player.ship, this._currentPylonMounted);
                this.$clearStoredShip();
            }
            this._actuallyBought = 0;
        }
    }
    
    //---------------------------------------------------------------------------------//
    this.playerBoughtNewShip = function _HH_playerBoughtNewShip(ship, price) {
        // if ShipVersion is installed this event handler is ineffective: HyperspaceHangar mission screen 
        // will be interrupted by ShipVersion's
        if (this._svEnabled) return;
        // factored the ship storing offer into a separated function so ShipVersion can call it if the 
        // player confirms the acquisition of the new ship
        this.$offerOldShipStorage(ship, price);
    }
    
    
    
    //
    // Internal Functions
    //
    
    //---------------------------------------------------------------------------------//
    this.$printShipsInTransit = function _HH_printShipsInTransit(max_ships) {
        var currentNames = this._currentNames;
        var shipsStored = this._shipsStored;
        var shipsInTransit = this._shipsInTransit;
        var arrival_times = Object.getOwnPropertyNames(shipsInTransit).sort();
        var msg = "";
        var k, s;
        var now = clock.seconds;
        if (arrival_times.length == 0) return "";
        msg += "Ships in Transit\n\n";
        for (var i=0; i<max_ships && i<arrival_times.length; i++) {
            k = arrival_times[i];
            log(this.name, "Ship in transit arriving at "+k+": "+shipsInTransit[k]);
            s = JSON.parse(shipsInTransit[k]);
            msg += currentNames[k]+" in transit to "+s[0][4]+"/G"+s[0][0]+1+", arriving in "+((k-now)/3600).toFixed(2)+" hours\n";
        }
        return msg
    }
    
    //---------------------------------------------------------------------------------//
    this.$storageFeeChanged = function _HH_storageFeeChanged() {
        this._hhSettings.SInt.S0.Hide = !this._percentPrice;
    }
    
    //---------------------------------------------------------------------------------//
    this.$getPortableEquipment = function _HH_getPortableEquipment(ship) {
        var portable_eqs = [];
        var eqList = ship.equipment;
        var i = eqList.length;
        var eqInfo, eqKey;
        while (i--) {
            eqInfo = eqList[i];
            if (eqInfo.isPortableBetweenShips)
                portable_eqs.push(eqInfo.equipmentKey);
        }
        return portable_eqs;
    }
    
    //---------------------------------------------------------------------------------//
    this.$clearPylons = function _HH_clearPylons(ship) {
        var pylon_mounted = [];
        var missiles = ship.missiles;
        var i;
    
        if (ship.missileCapacity > 0) {
            for (i = 0; i < missiles.length; i++) 
                pylon_mounted.push(missiles[i].equipmentKey);
        }
        i = missiles.length;
        while (i--) ship.removeEquipment(missiles[i].equipmentKey);
    
        if (this._debug) log(this.name, "Cleared ship's pylons of "+pylon_mounted);
        return pylon_mounted;
    }
    
    //---------------------------------------------------------------------------------//
    this.$reinstatePylons = function _HH_reinstatePylons(ship, missiles) {
        for (var i = 0; i < missiles.length; i++)
            ship.awardEquipment(missiles[i]);
        if (this._debug && missiles.length > 0) log(this.name, "Reinstated ship's pylons with "+missiles);
    }
    
    //---------------------------------------------------------------------------------//
    this.$clearStoredShip = function _HH_clearStoredShip() {
        this._delay = false;
        this._shipTempKey = null;
        this._currentShip = "";
        this._currentName = "";
        this._currentPortable.length = 0;
        this._currentPylonMounted.length = 0;
    }
    
    //---------------------------------------------------------------------------------//
    this.$dockedCheck = function _HH_dockedCheck() {
        //create hangar interface if docked and available. remove it otherwise
        var _ship = player.ship;
    
        if (_ship.docked && _ship.dockedStation.hasShipyard) {
            _ship.dockedStation.setInterface(this.name, {
                title: "Hyperspace Hangar",
                category: "Services",
                summary: "Provides access to ship storage facilities.",
                callback: this.$storeShips.bind(this)
            });
        } else {
            _ship.dockedStation.setInterface(this.name, null);
        }
        this.$checkShipArrivals();
    }
    
    //---------------------------------------------------------------------------------//
    this.$offerOldShipStorage = function _HH_offerOldShipStorage(ship, price) {
        // can't use stored player credit amount and price to set player credits,
        // because this doesn't take the trade-in value into account
        // the displayed trade in amount appears to be
        //            Math.round((player.ship.price * (player.ship.serviceLevel / 100) * 0.75) / 1000, 0) * 1000
        // however, (this._playerCredit + tradeInValue) - price still doesn't reflect actual calculated values
        // must also include sale price of any cargo, but only cargo that was able to be offloaded onto the station's market
    
        // that said, we do need to take away the trade in value from the player if they store the ship, otherwise it's free money
        this._cost = 0;
        if (this._percentPrice == true) {
            this._cost = (this._shipPrice * (this._shipPercent * 0.01)); //Credits
        } else if (missionVariables.hyperSpace_mission != "COMPLETE") {
            this._cost = this._priceOption * 0.1; //Credits
        } else {
            this._cost = this._priceOption * 0.05; //Credits
        }
        // we can only offer to store if the player would have enough remaining cash after the trade in amount is removed
        if ((player.credits - this._tradeIn) >= this._cost && this._actuallyBought == 1) {
            if (this._debug) log(this.name, "percent fee? "+this._percentPrice+", ship full price: "+formatCredits(this._shipPrice, true, true)+", percent fee: "+this._shipPercent+"%, flat fee: "+formatCredits(this._priceOption/10, true, true)+" actual fee: "+formatCredits(this._cost, true, true));
            mission.runScreen({
                    screenID: "HyperspaceHangar",
                    model: "[" + JSON.parse(this._currentShip)[1] + "]",
                    modelPersonality: JSON.parse(this._currentShip)[13],
                    spinModel: true,
                    background: {
                        name: "launch_bay_wip5_by_kheng-d7fmvuj.png",
                        height: 480
                    },
                    title: "Hyperspace Hangar",
                    message: "Would you like to store your ship?",
                    exitScreen: "GUI_SCREEN_STATUS",
                    choicesKey: "hyperSpace_askstore"
                },
                this.$askStore, this);
                // the ship storing fee was calculated at the beginning of the function
                mission.addMessageText("Current price: " + formatCredits(this._cost, true, true));
                mission.addMessageText("Current storage time: " + (this._timeOption / 60).toFixed(1) + " minutes");
        } else {
            // not enough credits
            this.$reinstatePylons(player.ship, this._currentPylonMounted);
            this.$clearStoredShip();
        }
        this._actuallyBought = 0;
    }
    
    //---------------------------------------------------------------------------------//
    this.$askStore = function _HH_askStore(choice) { 
        if (choice == "1_STORE") {
            var key = clock.absoluteSeconds;
            var shipinfo2 = JSON.stringify(data);
            // take away the trade-in amount if we're storing the ship
            // the storing fee (this._cost) was calculated before offering the storing option
            if (player.credits - this._tradeIn >= this._cost) {
                player.credits -= this._tradeIn;
                if (!this._allowsCargo) {
                    var wsSTH = worldScripts["Ship_Storage_Helper.js"];
                    var data;
                    if (wsSTH.storeCurrentShipToArray)
                        data = wsSTH.storeCurrentShipToArray(player.ship);
                    else {
                        let shipinfo = wsSTH.storeCurrentShip(player.ship);
                        data = JSON.parse(shipinfo);
                    }
                    this.$removeCargo(data);
                    this._currentShip = JSON.stringify(data);
                }
                this._shipsStored[this._shipTempKey] = this._currentShip;
                this._currentNames[this._shipTempKey] = this._currentName;
                log("HyperspaceHangar", "Stored ship " + this._currentNames[this._shipTempKey]);
                player.consoleMessage("Stored " + this._currentNames[this._shipTempKey] + ".");
                // remove portable equipment from new ship 
                log(this.name, "Removing portable equipment from new ship: "+this._currentPortable);
                var _ship = player.ship;
                var i = this._currentPortable.length;
                while (i--) _ship.removeEquipment(this._currentPortable[i]);
                //activate hangar
                this._currentNames["0_EXIT"] = "Exit";
                player.credits -= this._cost;
                clock.addSeconds(this._timeOption);
                this.$dockedCheck();
            }
            else {
                log("HyperspaceHangar", "Player doesn't have enough credits for the fee");
                this.$reinstatePylons(player.ship, this._currentPylonMounted) 
            }
        } else {
            this.$reinstatePylons(player.ship, this._currentPylonMounted) 
        }
        this.$clearStoredShip();
    }
    
    //---------------------------------------------------------------------------------//
    this.$removeCargo = function _HH_removeCargo(shipDataArray) {
        log(this.name, "Removing cargo and passageners from "+shipDataArray[0][3]+" before storing in Hyperspace Hangar");
        shipDataArray[8].length = 0; // cargo bay
        shipDataArray[10].length = 0; // passengers
        shipDataArray[15] = null; // new cargoes manifest
        shipDataArray[16] = null; // HyperCargo
        shipDataArray[17] = null; // Vortex
        shipDataArray[31] = shipDataArray[32] = ""; // smuggler compartment
    }
    
    //---------------------------------------------------------------------------------//
    this.$storeShips = function _HH_storeShips() {
        log(this.name,"_HH_storeShips");
        this.$checkShipArrivals();
        var msg = this.$printShipsInTransit(12);
        //are options available?
        if (this._showOptions == false) {
            if (this._oxpConf_check != false) {
                player.consoleMessage("Options are available through OXPConfig.");
            } else if (this._libConf_check != false) {
                player.consoleMessage("Options are available through Library Config.");
            } else {
                player.consoleMessage("OXPConfig and Library Config not enabled/installed. Cannot provide options.");
            }
        }
        //deactivate hangar if empty
        if (Object.keys(this._currentNames) == "0_EXIT") {
            this._currentNames["0_EXIT"] = "EMPTY";
        }
        //offer mission
        if (galaxyNumber == 2 && missionVariables.hyperSpace_mission == "NOT_ACTIVATED") {
            missionVariables.hyperSpace_mission = "ACTIVATED"
        }
        //not activated unless another ship is bought
        if (this._currentNames["0_EXIT"] == "EMPTY") {
            if (this._showOptions == false) {
                player.consoleMessage("You must purchase an additional ship in order to use the hangar.");
            } else {
                mission.runScreen({
                        screenID: "HyperspaceHangar",
                        background: {
                            name: "launch_bay_wip5_by_kheng-d7fmvuj.png",
                            height: 480
                        },
                        allowInterrupt: true,
                        title: "Hyperspace Hangar",
                        message: "No ships stored.",
                        exitScreen: "GUI_SCREEN_INTERFACES",
                        choicesKey: "hyperSpace_notstored"
                    },
                    this.$noneStored, this);
                mission.addMessageText("You must purchase an additional ship in order to use the hangar.");
            }
        }
        //no options
        else if (this._showOptions == false) {
            mission.runScreen({
                    screenID: "HyperspaceHangar",
                    background: {
                        name: "launch_bay_wip5_by_kheng-d7fmvuj.png",
                        height: 480
                    },
                    allowInterrupt: true,
                    title: "Hyperspace Hangar",
                    message: msg+"\nChoose an option.",
                    exitScreen: "GUI_SCREEN_INTERFACES",
                    choicesKey: "hyperSpace_nooptions"
                },
                this.$choice, this);
        }
        //options
        else {
            mission.runScreen({
                    screenID: "HyperspaceHangar",
                    background: {
                        name: "launch_bay_wip5_by_kheng-d7fmvuj.png",
                        height: 480
                    },
                    allowInterrupt: true,
                    title: "Hyperspace Hangar",
                    message: msg+"\nChoose an option.",
                    exitScreen: "GUI_SCREEN_INTERFACES",
                    choicesKey: "hyperSpace_choices"
                },
                this.$choice, this);
        }
    }
    
    
    //---------------------------------------------------------------------------------//
    this.$localizeShips = function _HH_localizeShips() {
        var systemID = system.ID;
        var currentNames = this._currentNames;
        var shipsStored = this._shipsStored;
        var shipsInTransit = this._shipsInTransit;
        var k, ship;
    
        this._inStationNames = {};
        this._otherStationNames = {};
        this._inTransitNames = {};
        log(this.name, "current system: "+System.systemNameForID(systemID)+"(G"+(galaxyNumber+1)+"/"+systemID+")");
        for (k in currentNames) {
            if (k != "0_EXIT") {
                if (k in shipsStored) {
                    ship = JSON.parse(shipsStored[k]);
                    if (systemID == ship[0][1]) {
                        this._inStationNames[k] = currentNames[k];
                        if (this._debug) log(this.name, k+": Ship "+currentNames[k]+" in current system ("+ship[0][1]+")");
                    } else {
                        this._otherStationNames[k] = "(in " +ship[0][4] + "/G"+formatInteger(ship[0][0]+1)+") " + currentNames[k];
                        if (this._debug) log(this.name, k+": Ship "+currentNames[k]+" in "+ship[0][4]+" ("+ship[0][1]+"/G"+ship[0][0]+1+")");
                    }
                } else if (k in shipsInTransit) {
                    this._inTransitNames[k] = currentNames[k];
                    if (this._debug) log(this.name, k+": Ship "+currentNames[k]+" in transit");
                } else {
                    log(this.name, currentNames[k]+" not found in stored or in transit ships, removing");
                    delete currentNames[k];
                }
            }
        }
        if (this._debug) {
            log(this.name, "Ships in system: "+JSON.stringify(this._inStationNames));
            log(this.name, "Ships in other systems: "+JSON.stringify(this._otherStationNames));
            log(this.name, "Ships in transit: "+JSON.stringify(this._inTransitNames));
        }
    }
    
    //---------------------------------------------------------------------------------//
    //menu function, used for almost every main menu, exepting no ships
    this.$choice = function _HH_choice(choice) {
        var parameters;
    
        log(this.name,"_HH_choice: "+choice);
        this.$localizeShips();
        if (choice == "GARAGE_1_SWITCH") {
            parameters = {
                    screenID: "HyperspaceHangar",
                    background: {
                        name: "launch_bay_wip5_by_kheng-d7fmvuj.png",
                        height: 480
                    },
                    allowInterrupt: false, 
                    title: "Hyperspace Hangar",
                    message: "Choose a ship:",
                    exitScreen: "GUI_SCREEN_INTERFACES",
                    choices: this._inStationNames
            };
            worldScripts["Paginated Screen"].$runScreen(parameters, this.$choice_switch, this, true);
            mission.addMessageText("Current storage time: " + (this._timeOption / 60) + " minutes");
        } else if (choice == "GARAGE_2_SELL") {
            mission.runScreen({
                    screenID: "HyperspaceHangar",
                    background: {
                        name: "launch_bay_wip5_by_kheng-d7fmvuj.png",
                        height: 480
                    },
                    allowInterrupt: true,
                    title: "Hyperspace Hangar",
                    message: "Choose an option.",
                    exitScreen: "GUI_SCREEN_INTERFACES",
                    choicesKey: "hyperSpace_sellChoices"
                },
                this.$choice_sell, this);
            mission.addMessageText("WARNING: ALL SOLD SHIPS WILL BE PERMANENTLY LOST.");
        } else if (choice == "GARAGE_3_TRANSFER") {
            mission.runScreen({
                    screenID: "HyperspaceHangar",
                    background: {
                        name: "launch_bay_wip5_by_kheng-d7fmvuj.png",
                        height: 480
                    },
                    allowInterrupt: true,
                    title: "Hyperspace Hangar",
                    message: "Choose an option.",
                    exitScreen: "GUI_SCREEN_INTERFACES",
                    choicesKey: "hyperSpace_transferChoices"
                },
                this.$choice_transfer, this);
        } else if (choice == "GARAGE_4_OPTIONS") {
            mission.runScreen({
                    screenID: "HyperspaceHangar",
                    background: {
                        name: "launch_bay_wip5_by_kheng-d7fmvuj.png",
                        height: 480
                    },
                    allowInterrupt: true,
                    title: "Hyperspace Hangar",
                    message: "Choose an option.",
                    exitScreen: "GUI_SCREEN_INTERFACES",
                    choicesKey: "hyperSpace_options"
                },
                this.$choice_options, this);
            mission.addMessageText("Current selling time: " + (this._timeOption / 60) + " minutes");
            mission.addMessageText("Current value: " + formatCredits(this._priceOption * 0.1, true, true));
        } else if (choice == "GARAGE_5_INFO") {
            //"normal" info screen
            if (missionVariables.hyperSpace_mission != "COMPLETED") {
                mission.runScreen({
                    screenID: "HyperspaceHangar",
                    title: "Station Database: Norby-Ramen Quirium Accuracy Drive",
                    exitScreen: "GUI_SCREEN_INTERFACES",
                    messageKey: "hyperSpace_QAD"
                });
            }
            //complete info screen
            else if (missionVariables.hyperSpace_mission == "COMPLETED") {
                mission.runScreen({
                        screenID: "HyperspaceHangar",
                        title: "Station Database: Norby-Ramen Quirium Accuracy Drive",
                        messageKey: "hyperSpace_QAD_authorized",
                        exitScreen: "GUI_SCREEN_INTERFACES",
                        choicesKey: "hyperSpace_QAD_nextPage"
                    },
                    this.$info_next_page, this);
            }
        }
    }
    
    //---------------------------------------------------------------------------------//
    this.$choice_switch = function _HH_choice_switch(choice) {
        //give chance to exit. ditto for sell function.
        log(this.name,"choice: "+choice);
        if (["Z99_EXIT","0_EXIT","99_EXIT"].indexOf(choice) > -1) {
            this.$storeShips();
            return;
        } else if (system.ID != JSON.parse(this._shipsStored[choice])[0][1]) {
            player.consoleMessage("Ship not in hangar. Transfer ship first to switch.");
            this.$storeShips();
            return;
        }
        this._oldChoice = choice;
        //confirm choice with possible damage
        if (player.ship.dockedStation.equivalentTechLevel < 6 && missionVariables.hyperSpace_mission != "COMPLETE") {
            mission.runScreen({
                    screenID: "HyperspaceHangar",
                    model: "[" + JSON.parse(this._shipsStored[choice])[1] + "]",
                    modelPersonality: JSON.parse(this._shipsStored[choice])[13],
                    background: {
                        name: "launch_bay_wip5_by_kheng-d7fmvuj.png",
                        height: 480
                    },
                    title: "Hyperspace Hangar",
                    message: "Transferring at the currently docked station may be damaging. Continue anyway?",
                    exitScreen: "GUI_SCREEN_INTERFACES",
                    choicesKey: "hyperSpace_confirm"
                },
                this.$choice_confirm_damage, this);
        }
        //confirm choice
        else {
            mission.runScreen({
                    screenID: "HyperspaceHangar",
                    model: "[" + JSON.parse(this._shipsStored[choice])[1] + "]",
                    modelPersonality: JSON.parse(this._shipsStored[choice])[13],
                    spinModel: true,
                    background: {
                        name: "launch_bay_wip5_by_kheng-d7fmvuj.png",
                        height: 480
                    },
                    title: "Hyperspace Hangar",
                    message: "Switch ship to " + this._currentNames[choice] + "?",
                    exitScreen: "GUI_SCREEN_STATUS",
                    choicesKey: "hyperSpace_confirm"
                },
                this.$choice_confirm_switch, this);
        }
    }
    
    
    //sell menus
    //---------------------------------------------------------------------------------//
    this.$choice_sell = function _HH_choice_sell(choice) {
        var parameters;
        log(this.name,"_HH_choice_sell: "+choice);
        if (choice == "1_SELLCURRENT") {
            parameters = {
                    screenID: "HyperspaceHangar",
                    background: {
                        name: "launch_bay_wip5_by_kheng-d7fmvuj.png",
                        height: 480
                    },
                    allowInterrupt: false,
                    title: "Hyperspace Hangar",
                    message: "Choose a ship to switch into after the sale.",
                    exitScreen: "GUI_SCREEN_INTERFACES",
                    choices: this._inStationNames
                };
            worldScripts["Paginated Screen"].$runScreen(parameters, this.$choice_sell_sub1, this, true);
        } else if (choice == "2_SELLSTORED") {
            var ships = {};
            var s;
            for (s in this._inStationNames) 
                ships[s] = "(in current system) "+this._inStationNames[s];
            for (s in this._otherStationNames)
                ships[s] = this._otherStationNames[s];
            parameters = {
                    screenID: "HyperspaceHangar",
                    background: {
                        name: "launch_bay_wip5_by_kheng-d7fmvuj.png",
                        height: 480
                    },
                    allowInterrupt: false,
                    title: "Hyperspace Hangar",
                    message: "Choose a ship to sell.",
                    exitScreen: "GUI_SCREEN_INTERFACES",
                    choices: ships
                };
            worldScripts["Paginated Screen"].$runScreen(parameters, this.$choice_sell_sub2, this, true);
        }
    }
    
    //---------------------------------------------------------------------------------//
    this.$choice_sell_sub1 = function _HH_choice_sell_sub1(choice) {
        //don't sell your options!
        if (["Z99_EXIT","0_EXIT","99_EXIT"].indexOf(choice) > -1) {
            this.$storeShips();
            return;
        }
        this._oldChoice = choice;
        mission.runScreen({
                screenID: "HyperspaceHangar",
                model: "[" + player.ship.dataKey + "]",
                modelPersonality: player.ship.entityPersonality,
                spinModel: true,
                background: {
                    name: "launch_bay_wip5_by_kheng-d7fmvuj.png",
                    height: 480
                },
                title: "Hyperspace Hangar",
                message: "Sell " + player.ship.displayName + "?",
                exitScreen: "GUI_SCREEN_INTERFACES",
                choicesKey: "hyperSpace_confirm"
            },
            this.$choice_confirm_sell1, this);
    }
    
    //---------------------------------------------------------------------------------//
    this.$choice_sell_sub2 = function _HH_choice_sell_sub2(choice) {
        log(this.name,"_HH_choice_sell_sub2: "+choice);
        //don't sell your options!
        if (["Z99_EXIT","0_EXIT","99_EXIT"].indexOf(choice) > -1) {
            this.$storeShips();
            return;
        }
        this._oldChoice = choice;
        mission.runScreen({
                screenID: "HyperspaceHangar",
                model: "[" + JSON.parse(this._shipsStored[choice])[1] + "]",
                modelPersonality: JSON.parse(this._shipsStored[choice])[13],
                spinModel: true,
                background: {
                    name: "launch_bay_wip5_by_kheng-d7fmvuj.png",
                    height: 480
                },
                allowInterrupt: true,
                title: "Hyperspace Hangar",
                message: "Sell " + this._currentNames[choice] + "?",
                exitScreen: "GUI_SCREEN_INTERFACES",
                choicesKey: "hyperSpace_confirm"
            },
            this.$choice_confirm_sell2, this);
    }
    
    //transfer menu
    //---------------------------------------------------------------------------------//
    this.$choice_transfer = function _HH_choice_transfer(choice) {
        var parameters;
        if (choice == "1_SEND") {
            var ships = {};
            var s;
            for (s in this._inStationNames) 
                ships[s] = "(in current system "+this._inStationNames[s];
            for (s in this._otherStationNames) 
                ships[s] = this._otherStationNames[s];
            parameters = {
                    screenID: "HyperspaceHangar",
                    background: {
                        name: "launch_bay_wip5_by_kheng-d7fmvuj.png",
                        height: 480
                    },
                    allowInterrupt: false,
                    title: "Hyperspace Hangar",
                    message: "Choose a ship to send to the location currently marked on the map: " + System.systemNameForID(player.ship.targetSystem) + ".",
                    exitScreen: "GUI_SCREEN_INTERFACES",
                    choices: ships
                };
            worldScripts["Paginated Screen"].$runScreen(parameters, this.$choice_transfer_send, this, true);
        } else if (choice == "2_ORDER") {
            parameters = {
                    screenID: "HyperspaceHangar",
                    background: {
                        name: "launch_bay_wip5_by_kheng-d7fmvuj.png",
                        height: 480
                    },
                    allowInterrupt: false,
                    title: "Hyperspace Hangar",
                    message: "Have a ship sent to the current docked station.",
                    exitScreen: "GUI_SCREEN_INTERFACES",
                    choices: this._otherStationNames
                };
            worldScripts["Paginated Screen"].$runScreen(parameters, this.$choice_transfer_order, this, true);
        }
    }
    
    //---------------------------------------------------------------------------------//
    this.$choice_transfer_send = function _HH_choice_transfer_send(choice) {
        if (["Z99_EXIT","99_EXIT","0_EXIT"].indexOf(choice) > -1) {
            this.$storeShips();
            return;
        }
        this._oldChoice = choice;
        this._oldShips = JSON.parse(this._shipsStored[choice]);
        //redundancy check
        if (player.ship.targetSystem == JSON.parse(this._shipsStored[choice])[0][1]) {
            player.consoleMessage("Your ship is already at the selected system.");
            this.$storeShips();
            return;
        }
        //galaxy consistency check
        else if (this._oldShips[0][0] != galaxyNumber) {
            player.consoleMessage("Your ship is in another galaxy. Please order instead.");
            this.$storeShips();
            return;
        }
        this.$hudHidden = player.ship.hudHidden;
        player.ship.hudHidden = true;
        //is the short range chart usable?
        if (System.infoForSystem(this._oldShips[0][0], this._oldShips[0][1]).distanceToSystem(System.infoForSystem(galaxyNumber, player.ship.targetSystem)) <= 7.0 &&
            this._oldShips[0][0] == galaxyNumber) {
            mission.runScreen({
                    screenID: "HyperspaceHangar-map",
                    model: "[" + this._oldShips[1] + "]",
                    modelPersonality: this._oldShips[13],
                    spinModel: true,
                    backgroundSpecial: "SHORT_RANGE_CHART",
                    title: "Hyperspace Hangar",
                    message: "\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\nTransfer " + this._currentNames[choice] + " from " + JSON.parse(this._shipsStored[choice])[0][4] + " to " +
                        System.infoForSystem(galaxyNumber, player.ship.targetSystem).name + "?",
                    exitScreen: "GUI_SCREEN_INTERFACES",
                    choicesKey: "hyperSpace_confirm"
                },
                this.$choice_confirm_transfer_send, this);
        }
        //what mode is the map in?
        else {
            mission.runScreen({
                    screenID: "HyperspaceHangar-map",
                    model: "[" + this._oldShips[1] + "]",
                    modelPersonality: this._oldShips[13],
                    spinModel: true,
                    backgroundSpecial: (player.ship.routeMode != "OPTIMIZED_BY_TIME" ? "LONG_RANGE_CHART_SHORTEST" : "LONG_RANGE_CHART_QUICKEST"),
                    title: "Hyperspace Hangar",
                    message: "\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\nTransfer " + this._currentNames[choice] + " from " + JSON.parse(this._shipsStored[choice])[0][4] + " to " +
                        System.infoForSystem(galaxyNumber, player.ship.targetSystem).name + "?",
                    exitScreen: "GUI_SCREEN_INTERFACES",
                    choicesKey: "hyperSpace_confirm"
                },
                this.$choice_confirm_transfer_send, this);
        } 
        mission.addMessageText("Transfer price: " +
            formatCredits(System.infoForSystem(this._oldShips[0][0], this._oldShips[0][1]).routeToSystem(System.infoForSystem(galaxyNumber, player.ship.targetSystem)).distance * 90, true, true)+"   Transfer time: "+System.infoForSystem(this._oldShips[0][0], this._oldShips[0][1]).routeToSystem(System.infoForSystem(galaxyNumber, player.ship.targetSystem, player.ship.routeMode)).time.toFixed(1)+" hours");
    }
    
    //---------------------------------------------------------------------------------//
    this.$choice_transfer_order = function _HH_choice_transfer_order(choice) {
        if (["Z99_EXIT","99_EXIT","0_EXIT"].indexOf(choice) > -1) {
            this.$storeShips();
            return;
        }
        this._oldChoice = choice;
        this._oldShips = JSON.parse(this._shipsStored[choice]);
        //redundancy check
        if (this._oldShips[0][1] == system.ID) {
            player.consoleMessage("Ship already present.");
            this.$storeShips();
            return;
        }
        //intergalactic order 
        else if (this._oldShips[0][0] != galaxyNumber) {
            mission.runScreen({
                    screenID: "HyperspaceHangar",
                    model: "[" + this._oldShips[1] + "]",
                    modelPersonality: this._oldShips[13],
                    spinModel: true,
                    background: {
                        name: "launch_bay_wip5_by_kheng-d7fmvuj.png",
                        height: 480
                    },
                    title: "Hyperspace Hangar",
                    message: "Transfer " + this._currentNames[choice] + " to Galaxy " + (galaxyNumber + 1) + "?",
                    exitScreen: "GUI_SCREEN_INTERFACES",
                    choicesKey: "hyperSpace_confirm"
                },
                this.$choice_confirm_transfer_order_interGalactic, this);
            mission.addMessageText("Current location: " + System.infoForSystem(this._oldShips[0][0], this._oldShips[0][1]).name);
            mission.addMessageText("Current order cost: " + formatCredits(Math.abs(galaxyNumber - this._oldShips[0][0]) * 1000, true, true));
            mission.addMessageText("Current shipping time: " + Math.abs(galaxyNumber - this._oldShips[0][0]) * 72 + " hours");
        }
        //regular order
        else {
            var fee = system.info.routeToSystem(System.infoForSystem(this._oldShips[0][0], this._oldShips[0][1]), player.ship.routeMode).distance * 90;
            var message;
            if (fee <= player.credits) {
                mission.runScreen({
                    screenID: "HyperspaceHangar",
                    model: "[" + this._oldShips[1] + "]",
                    modelPersonality: this._oldShips[13],
                    spinModel: true,
                    background: {
                        name: "launch_bay_wip5_by_kheng-d7fmvuj.png",
                        height: 480
                    },
                    title: "Hyperspace Hangar",
                    message: "",
                    exitScreen: "GUI_SCREEN_INTERFACES",
                    choicesKey: "hyperSpace_confirm"
                },
                this.$choice_confirm_transfer_order, this);
                message = "Transfer " + this._currentNames[choice] + " to " + system.name + "?";
            } else {
                mission.runScreen({
                    screenID: "HyperspaceHangar",
                    model: "[" + this._oldShips[1] + "]",
                    modelPersonality: this._oldShips[13],
                    spinModel: true,
                    background: {
                        name: "launch_bay_wip5_by_kheng-d7fmvuj.png",
                        height: 480
                    },
                    title: "Hyperspace Hangar",
                    exitScreen: "GUI_SCREEN_INTERFACES",
                    choicesKey: "hyperSpace_order"
                },
                this.$choice_transfer, this);
                message = "Insufficient funds for transfer!";
            }
            mission.addMessageText("Current location: " + System.infoForSystem(this._oldShips[0][0], this._oldShips[0][1]).name);
            mission.addMessageText("Current order cost: " + formatCredits(fee, true, true));
            mission.addMessageText("Current shipping time: " + system.info.routeToSystem(System.infoForSystem(this._oldShips[0][0], this._oldShips[0][1]), player.ship.routeMode).time.toFixed(0) + " hours\n");
             mission.addMessageText(message); 
        }
    }
    
    //---------------------------------------------------------------------------------//
    this.$choice_options = function _HH_choice_options(choice) {
        if (choice == "1_PRICE") {
            if (this._percentPrice == true) {
                player.consoleMessage("Standard pricing disabled. Check " + this._configType + ". Current value: " + this._shipPercent * 10 + "%.");
            } else {
                mission.runScreen({
                        screenID: "HyperspaceHangar",
                        background: {
                            name: "launch_bay_wip5_by_kheng-d7fmvuj.png",
                            height: 480
                        },
                        allowInterrupt: true,
                        title: "Hyperspace Hangar",
                        message: "Input price",
                        exitScreen: "GUI_SCREEN_INTERFACES",
                        textEntry: "TRUE"
                    },
                    this.$choice_price, this);
                mission.addMessageText("Current value: " + formatCredits(this._priceOption / 10, true, true));
            }
        } else if (choice == "2_TIME") {
            mission.runScreen({
                    screenID: "HyperspaceHangar",
                    background: {
                        name: "launch_bay_wip5_by_kheng-d7fmvuj.png",
                        height: 480
                    },
                    allowInterrupt: true,
                    title: "Hyperspace Hangar",
                    message: "Input time (in seconds)",
                    exitScreen: "GUI_SCREEN_INTERFACES",
                    textEntry: "TRUE"
                },
                this.$choice_time, this);
            mission.addMessageText("Current value: " + (this._timeOption / 60) + " minutes");
        } else if (choice == "3_RESET") {
            this._priceOption = this._defaultpriceOption;
            this._timeOption = this._defaulttimeOption;
        }
        //removes ships entirely
        else if (choice == "4_PURGE") {
            var _ships = {};
            var s;
            for (s in this._inStationNames)
                _ships[s] = "(in current system) "+this._inStationNames[s];
            for (s in this._otherStationNames)
                _ships[s] = this._otherStationNames[s];
            parameters = {
                    screenID: "HyperspaceHangar",
                    background: {
                        name: "launch_bay_wip5_by_kheng-d7fmvuj.png",
                        height: 480
                    },
                    allowInterrupt: false,
                    title: "Hyperspace Hangar",
                    message: "Choose ship to remove. WARNING: REMOVAL IS PERMANENT!",
                    exitScreen: "GUI_SCREEN_INTERFACES",
                    choices: _ships
                };
            worldScripts["Paginated Screen"].$runScreen(parameters, this.$purge, this, true);
        }
    }
    
    //---------------------------------------------------------------------------------//
    this.$choice_price = function _HH_choice_price(choice) {
        this._priceOption = choice * 10;
    }
    
    //---------------------------------------------------------------------------------//
    this.$choice_time = function _HH_choice_time(choice) {
        this._timeOption = choice;
    }
    
    //---------------------------------------------------------------------------------//
    this.$purge = function _HH_purge(choice) {
        if (["Z99_EXIT","99_EXIT","0_EXIT"].indexOf(choice) > -1) {
            this.$storeShips();
            return;
        }
        this._oldChoice = choice;
        mission.runScreen({
                screenID: "HyperspaceHangar",
                model: "[" + JSON.parse(this._shipsStored[choice])[1] + "]",
                modelPersonality: JSON.parse(this._shipsStored[choice])[13],
                spinModel: true,
                background: {
                    name: "launch_bay_wip5_by_kheng-d7fmvuj.png",
                    height: 480
                },
                title: "Hyperspace Hangar",
                message: "Remove " + this._currentNames[choice] + "?",
                choicesKey: "hyperSpace_confirm"
            },
            this.$choice_confirm_purge, this);
    }
    
    //menu for no ships
    //---------------------------------------------------------------------------------//
    this.$noneStored = function _HH_noneStored(choice) {
        if (choice == "1_OPTIONS") {
            mission.runScreen({
                    screenID: "HyperspaceHangar",
                    background: {
                        name: "launch_bay_wip5_by_kheng-d7fmvuj.png",
                        height: 480
                    },
                    allowInterrupt: true,
                    title: "Hyperspace Hangar",
                    message: "Choose an option.",
                    exitScreen: "GUI_SCREEN_INTERFACES",
                    choicesKey: "hyperSpace_options"
                },
                this.$choice_options, this);
        } else if (choice == "2_INFO") {
            if (missionVariables.hyperSpace_mission != "COMPLETED") {
                mission.runScreen({
                    screenID: "HyperspaceHangar",
                    title: "Station Database: Norby-Ramen Quirium Accuracy Drive",
                    exitScreen: "GUI_SCREEN_INTERFACES",
                    messageKey: "hyperSpace_QAD"
                });
            } else if (missionVariables.hyperSpace_mission == "COMPLETED") {
                mission.runScreen({
                        screenID: "HyperspaceHangar",
                        title: "Station Database: Norby-Ramen Quirium Accuracy Drive",
                        messageKey: "hyperSpace_QAD_authorized",
                        exitScreen: "GUI_SCREEN_INTERFACES",
                        choices: ({
                            "1_NEXT": "Next page"
                        })
                    },
                    this.$info_next_page, this);
            }
        }
    }
    
    //---------------------------------------------------------------------------------//
    this.$choice_confirm_switch = function _HH_choice_confirm_switch(choice) {
        if (choice == "1_YES") {
            //switch ships
            var wsSTH = worldScripts["Ship_Storage_Helper.js"];
            var hud = player.ship.hud;
            var credits = player.credits;
            var data;
            if (wsSTH.storeCurrentShipToArray)
                data = wsSTH.storeCurrentShipToArray(player.ship);
            else {
                let shipinfo = wsSTH.storeCurrentShip(player.ship);
                data = JSON.parse(shipinfo);
            }
            if (!this._allowsCargo) this.$removeCargo(data);
            var key = clock.absoluteSeconds;
            this._shipsStored[key] = JSON.stringify(data);
            this._currentNames[key] = player.ship.displayName;
            log(this.name, "Saving player ship "+this._currentNames[key]+": "+this._shipsStored[key]);
            if (this._svEnabled) { worldScripts.shipversion.$hyperspaceHangarRestoredShip = true; }
            try {
                worldScripts["Ship_Storage_Helper.js"].restoreStoredShip(this._shipsStored[this._oldChoice]);
            } catch (err) {
                log(this.name, "!!ERROR: " + err);
            }
            player.credits = credits;
            player.ship.hud = hud;
    
            //don't keep switched ship stored!
            player.consoleMessage("Switched ship to " + this._currentNames[this._oldChoice] + ".");
            delete this._shipsStored[this._oldChoice];
            delete this._currentNames[this._oldChoice];
            this._oldChoice = "";
            clock.addSeconds(this._timeOption);
        } else this.$storeShips();
    }
    
    //---------------------------------------------------------------------------------//
    this.$choice_confirm_damage = function _HH_choice_confirm_damage(choice) {
        if (choice == "1_YES") {
            //switch ships
            var wsSTH = worldScripts["Ship_Storage_Helper.js"];
            var hud = player.ship.hud;
            var credits = player.credits;
            var data;
            if (wsSTH.storeCurrentShipToArray)
                data = wsSTH.storeCurrentShipToArray(player.ship);
            else {
                let shipinfo = wsSTH.storeCurrentShip(player.ship);
                data = JSON.parse(shipinfo);
            }
            if (!this._allowsCargo) this.$removeCargo(data);
            var key = clock.absoluteSeconds;
            var shipinfo2 = JSON.stringify(data);
            this._shipsStored[key] = shipinfo2;
            this._currentNames[key] = player.ship.displayName;
            if (this._svEnabled) { worldScripts.shipversion.$hyperspaceHangarRestoredShip = true; }
            try {
                worldScripts["Ship_Storage_Helper.js"].restoreStoredShip(this._shipsStored[this._oldChoice]);
            } catch (err) {
                log(this.name, "!!ERROR: " + err);
            }
            player.credits = credits;
            player.ship.hud = hud;
    
            //don't keep switched ship stored!
            player.consoleMessage("Switched ship to " + this._currentNames[this._oldChoice] + ".");
            delete this._shipsStored[this._oldChoice];
            delete this._currentNames[this._oldChoice];
            this._oldChoice = "";
            clock.addSeconds(this._timeOption);
    
            //possibly damage ship
            if (player.ship.takeInternalDamage() === true) {
                player.consoleMessage("Ship damaged during switch.");
            }
        } else this.$storeShips();
    }
    
    //---------------------------------------------------------------------------------//
    this.$choice_confirm_sell1 = function _HH_choice_confirm_sell1(choice) {
        log(this.name, "_HH_choice_confirm_sell1: "+choice);
        if (choice == "1_YES") {
            //"sell" ship
            var hud = player.ship.hud;
            var cost = this._shipDepreciation * this.$tradeInAmount();
            var old_name = player.ship.displayName;
            player.credits += cost;
            try {
                worldScripts["Ship_Storage_Helper.js"].restoreStoredShip(this._shipsStored[this._oldChoice]);
            } catch (err) {
                log(this.name, "!!ERROR: " + err);
            }
            player.ship.hud = hud;
            player.consoleMessage("Sold " + old_name + " for " + formatCredits(cost, false, true) + ".");
            log(this.name, "Sold  " + old_name + " for " + formatCredits(cost, false, true) + ".");
            delete this._shipsStored[this._oldChoice];
            delete this._currentNames[this._oldChoice];
            this._oldChoice = "";
            this.$localizeShips();
            clock.addSeconds(this._timeOption);
        } else this.$storeShips();
    }
    
    //---------------------------------------------------------------------------------//
    this.$choice_confirm_sell2 = function _HH_choice_confirm_sell2(choice) {
        log(this.name, "_HH_choice_confirm_sell2: "+choice);
        if (choice == "1_YES") {
            //store ship
            var hud = player.ship.hud;
            //this._shipTempKey = clock.absoluteSeconds;
            var holdShip = worldScripts["Ship_Storage_Helper.js"].storeCurrentShip(player.ship);
            
            //reload ship for pricing
            if (worldScripts.ShipConfiguration_Core) {
                worldScripts.ShipConfiguration_Core._resprayActive = true;
                worldScripts.ShipConfiguration_Core._adding = true;
            }
            try {
                worldScripts["Ship_Storage_Helper.js"].restoreStoredShip(this._shipsStored[this._oldChoice]);
                var cost = this._shipDepreciation * this.$tradeInAmount();
                player.credits += cost;
                player.consoleMessage("Sold " + this._currentNames[this._oldChoice] + " for " + formatCredits(cost, false, true) + ".");
                log(this.name, "Sold  " + this._currentNames[this._oldChoice] + " for " + formatCredits(cost, false, true) + ".");
                delete this._shipsStored[this._oldChoice];
                delete this._currentNames[this._oldChoice];
                this._oldChoice = "";
                this.$localizeShips();
    
                //reload original ship
                try {
                    worldScripts["Ship_Storage_Helper.js"].restoreStoredShip(holdShip);
                    if (worldScripts.ShipConfiguration_Core) {
                        worldScripts.ShipConfiguration_Core._resprayActive = false;
                        worldScripts.ShipConfiguration_Core._adding = false;
                    }
                    player.ship.hud = hud;
                    this._confirm = false;
                } catch (err) {
                    log(this.name, "!!ERROR reloading player ship: " + err);
                }
            } catch (err) {
                log(this.name, "!!ERROR loading ship to sell: " + err);
            }
        } 
        this.$choice_sell("2_SELLSTORED");
    }
    
    //---------------------------------------------------------------------------------//
    this.$choice_confirm_transfer_send = function _HH_choice_confirm_transfer_send(choice) {
        if (choice == "1_YES") {
            var fee = (System.infoForSystem(this._oldShips[0][0], this._oldShips[0][1]).routeToSystem(System.infoForSystem(galaxyNumber, player.ship.targetSystem, player.ship.routeMode)).distance * 90);
            if (fee <= player.credits) {
                var transit_time = System.infoForSystem(this._oldShips[0][0], this._oldShips[0][1]).routeToSystem(System.infoForSystem(galaxyNumber, player.ship.targetSystem, player.ship.routeMode)).time
                var arrival_time = clock.seconds + transit_time * 3600;
                this._oldShips = JSON.parse(this._shipsStored[this._oldChoice]);
                //subtract money
                player.credits -= fee;
                //change values
                this._oldShips[0][0] = galaxyNumber;
                this._oldShips[0][1] = player.ship.targetSystem;
                this._oldShips[0][4] = System.infoForSystem(galaxyNumber, player.ship.targetSystem).name;
                this._shipsInTransit[arrival_time] = JSON.stringify(this._oldShips);
                this._currentNames[arrival_time] = this._currentNames[this._oldChoice];
                delete this._shipsStored[this._oldChoice];
                delete this._currentNames[this._oldChoice];
                this.$localizeShips();
                //give feedback
                log(this.name, "Player confirmed transfer of ship "+this._currentNames[this._oldChoice]+" from "+this._oldShips[0][4]+" to "+System.infoForSystem(galaxyNumber, player.ship.targetSystem).name+" for "+formatCredits(fee, true, true)+ ", arrival in "+transit_time+" hours.");
                player.consoleMessage(this._oldShips[0][3] + " sent to " + System.infoForSystem(this._oldShips[0][0], this._oldShips[0][1]).name + ", arrival in "+transit_time+" hours.");
            } else {
                log(this.name, "Insuficient credits to transfer "+this._oldShips[0][3] + " to " + System.infoForSystem(this._oldShips[0][0], this._oldShips[0][1]).name + ".");
                player.consoleMessage("Insuficient credits to transfer "+this._oldShips[0][3] + " to " + System.infoForSystem(this._oldShips[0][0], this._oldShips[0][1]).name + ".");
            }
        }
        player.ship.hudHidden = this.$hudHidden;
        this.$choice_transfer("1_SEND");
    }
    
    //---------------------------------------------------------------------------------//
    this.$choice_confirm_transfer_order = function _HH_choice_confirm_transfer_order(choice) {
        if (choice == "1_YES") {
            this._oldShips = JSON.parse(this._shipsStored[this._oldChoice]);
            var fee = system.info.routeToSystem(System.infoForSystem(this._oldShips[0][0], this._oldShips[0][1], player.ship.routeMode)).distance * 90;
            if (fee <= player.credits) {
                var transit_time = system.info.routeToSystem(System.infoForSystem(this._oldShips[0][0], this._oldShips[0][1]), player.ship.routeMode).time;
                var arrival_time = clock.seconds + transit_time * 3600;
                //subtract money
                player.credits -= fee;
                //give feedback
                log(this.name, "Ordering " + this._oldShips[0][3] + " from " + System.infoForSystem(this._oldShips[0][0], this._oldShips[0][1]).name + ", arrival in "+transit_time+" hours at "+arrival_time);
                player.consoleMessage("Ordering " + this._oldShips[0][3] + " from " + System.infoForSystem(this._oldShips[0][0], this._oldShips[0][1]).name + ", arrival in "+transit_time+" hours.");
                //change values
                this._oldShips[0][0] = galaxyNumber;
                this._oldShips[0][1] = system.ID;
                this._oldShips[0][4] = system.name;
                this._shipsInTransit[arrival_time] = JSON.stringify(this._oldShips);
                this._currentNames[arrival_time] = this._currentNames[this._oldChoice];
                delete this._shipsStored[this._oldChoice];
                delete this._currentNames[this._oldChoice];
                this.$localizeShips();
            } else {
                player.consoleMessage("Insuficient credits to transfer "+this._oldShips[0][3] + " from " + System.infoForSystem(this._oldShips[0][0], this._oldShips[0][1]).name + ".")
                this.$choice_transfer("2_ORDER");
            }
            this.$choice_transfer("2_ORDER");
        }
    }
    
    //---------------------------------------------------------------------------------//
    this.$choice_confirm_transfer_order_interGalactic = function _HH_choice_confirm_transfer_order_interGalactic(choice) {
        if (choice == "1_YES") {
            var  fee = Math.abs(galaxyNumber - this._oldShips[0][0]) * 900;
            if (fee <= player.credits) {
                var transit_time = Math.abs(galaxyNumber - this._oldShips[0][0]) * 72;
                var arrival_time = clock.seconds + transit_time * 3600;
                //subtract money
                player.credits -= fee;
                //give feedback
                log(this.name, "Ordering " + this._oldShips[0][3] + " from " + System.infoForSystem(this._oldShips[0][0], this._oldShips[0][1]).name + ", arrival in "+transit_time+" hours at "+arrival_time);
                player.consoleMessage("Ordering " + this._oldShips[0][3] + " from " + System.infoForSystem(this._oldShips[0][0], this._oldShips[0][1]).name + ", arrival in "+transit_time+" hours.");
                //change values
                this._oldShips[0][0] = galaxyNumber;
                this._oldShips[0][1] = system.ID;
                this._oldShips[0][4] = system.name;
                this._shipsInTransit[arrival_time] = JSON.stringify(this._oldShips);
                this._currentNames[arrival_time] = this._currentNames[this._oldChoice];
                delete this._shipsStored[this._oldChoice];
                delete this._currentNames[this._oldChoice];
                this.$localizeShips();
            } else {
                player.consoleMessage("Insuficient credits to transfer "+this._oldShips[0][3] + " from " + System.infoForSystem(this._oldShips[0][0], this._oldShips[0][1]).name + ".")
                this.$choice_transfer("2_ORDER");
            }
        } 
        player.ship.hudHidden = this.$hudHidden;
        this.$choice_transfer("1_SEND");
    }
    
    
    //---------------------------------------------------------------------------------//
    this.$choice_confirm_purge = function _HH_choice_confirm_purge(choice) {
        if (choice == "1_YES") {
            player.consoleMessage("Removed " + this._currentNames[this._oldChoice] + ".");
            delete this._currentNames[this._oldChoice];
            delete this._shipsStored[this._oldChoice];
            this._oldChoice = "";
        } else this.$storeShips();
    }
    
    //---------------------------------------------------------------------------------//
    //next page of info for mission completed info screen
    this.$info_next_page = function _HH_info_next_page(choice) {
        mission.runScreen({
            screenID: "HyperspaceHangar",
            title: "Station Database: Norby-Ramen Quirium Accuracy Drive",
            exitScreen: "GUI_SCREEN_INTERFACES",
            messageKey: "hyperSpace_QAD_authorized2"
        });
    }
    
    //---------------------------------------------------------------------------------//
    // clean out the stored ships dictionary, leaving only those ships which are actually stored
    this.$cleanUp = function _HH_cleanUp() {
        var list = Object.keys(this._currentNames);
        var full = Object.keys(this._shipsStored);
        var transit = Object.keys(this._shipsInTransit);
    
        for (var i = 0; i < full.length; i++) {
            if (list.indexOf(full[i]) === -1) {
                delete this._shipsStored[full[i]];
            }
        }
        for (var i = 0; i < transit.length; i++) {
            if (list.indexOf(transit[i]) === -1) {
                delete this._shipsInTransit[transit[i]];
            }
        }
    }
    
    //---------------------------------------------------------------------------------//
    this.$tradeInAmount = function _HH_tradeInAmount() {
        var p = player.ship;
        var amt = Math.round(p.price * (p.serviceLevel / 100) * (0.75) / 1000) * 1000;
        return amt;
    }
    
    
    //---------------------------------------------------------------------------------//
    this.$checkShipArrivals = function _HH_checkShipArrivals() {
        var tempKey = clock.seconds;
        var transit = Object.keys(this._shipsInTransit);
        var newKey = clock.seconds;
    
        for (var i = 0; i < transit.length; i++) {
            if (tempKey > transit[i]) {
                // ship in transit arrived
                for (;this._currentNames[newKey];newKey++);
                log(this.name, this._currentNames[transit[i]]+" arrived at Hyperspace Hangar, key:"+newKey);
                player.consoleMessage(this._currentNames[transit[i]]+" arrived at Hyperspace Hangar");
                this._shipsStored[newKey] = this._shipsInTransit[transit[i]];
                this._currentNames[newKey] = this._currentNames[transit[i]];
                delete this._shipsInTransit[transit[i]];
                delete this._currentNames[transit[i]];
            }
        } 
    }
    
    Scripts/hypermission.js
    this.name           = "HyperspaceHangar-Mission";
    this.author         = "Kenji Takashima";
    this.copyright      = "(C) 2015 Kenji Takashima.";
    this.licence        = "CC-BY-NC-SA 3.0";
    this.description    = "Mission for Hyperspace Hangar.";
    
    "use strict";
    
    this.missionScreenOpportunity = function()
    {
        //start mission
        if (missionVariables.hyperSpace_mission == "ACTIVATED")
        {
            mission.runScreen({
                title: "Message from Ramen Corporation",
                messageKey: "hyperSpace_missionBegin"});
            missionVariables.hyperSpace_mission = "DELIVER";
            mission.setInstructions("Deliver the Ramen Corporation's documents to Diesanen in G6.");
        }
        //end mission
        else if (galaxyNumber == 5 && system.ID == 149 && missionVariables.hyperSpace_mission == "DELIVER")
        {
            mission.runScreen({
                title: "Message from Ramen Corporation",
                messageKey: "hyperSpace_missionEnd"});
            mission.unmarkSystem({
                system: 149,
                name: "hyperspaceMission"
            });
            missionVariables.hyperSpace_mission = "DELIVERED";
        }
        //give reward screen
        else if (missionVariables.hyperSpace_mission == "DELIVERED")
        {
            mission.runScreen({
                title: "Message from Ramen Corporation",
                messageKey: "hyperSpace_missionReward"});
            missionVariables.hyperSpace_mission = "COMPLETED";
            missionVariables.hyperSpaceTimeCompleted = clock.days;
        }
    }
    
    Scripts/paginated_screen.js
    "use strict";
    this.name = "Paginated Screen";
    this.author = "Dybal";
    this.licence = "CC-BY-NC-SA 3.0";
    this.description = "Provides a paginated mission screen" 
    
    this._hudHidden;
    this._maxContentRows = 21;
    this._msRows = 23;
    this._itemColor = "greenColor";
    this._menuColor = "orangeColor";
    this._maxPage;
    this._curPage;
    this._contentChoices = [];
    this._contentCallback;
    this._contentThisObj;
    this._contentParameters;
    this._contentExitChoice;
    
    this.$runScreen = function _runPaginatedScreen(parameters, callback, this_obj, sort_entries) {
        this._contentCallback = callback;
        this._contentThisObj = this_obj;
        this._contentParameters = parameters;
    
        log(this.name, "original choices: "+JSON.stringify(parameters.choices));
        var _ship = player.ship;
        this.$hudHidden = player.ship.hudHidden;
        player.ship.hudHidden = true;
    
        if (!parameters.choices) return "'choices' not found in 'parameters'";
        var k;
        var original_choices = parameters.choices;
        this._contentChoices = [];
        for (k in original_choices) {
            if ((k.toUpperCase()).indexOf("EXIT") > -1) {
                this._contentExitChoice = k;
                log(this.name, "Original Exit choice: "+this._contentExitChoice);
            } else {
                this._contentChoices.push({key:k, value:original_choices[k]});
            }
        }
        if (sort_entries) {
            log(this.name, "sorting entries");
            this._contentChoices.sort(this._compareEntries);
        }
    
        if (!this._contentExitChoice) this._contentExitChoice = "0_EXIT";
        log(this.name, "Exit choice: "+this._contentExitChoice);
        this._maxPage = Math.ceil(this._contentChoices.length / this._maxContentRows)-1;
        if (this._maxPage < 0) this._maxPage = 0; // if original choices is empty...
        this._curPage = 0;
        log(this.name, this._contentChoices.length+" entries, "+this._maxPage+" pages");
        this._showPage(0);
    }
    
    this._compareEntries = function(a,b) {
        var _a, _b;
        if (typeof a.value === 'object' && a.value.text) {
            _a = a.value.text;
        } else {
            _a = a.value;
        }
        if (typeof b.value === 'object' && b.value.text) {
            _b = b.value.text;
        } else {
            _b = b.value;
        }
        return (_a > _b);    
    }
    
    
    this._showPage = function(page) {
        if (page < 0) { this._curPage = 0; }
        else if (page > this._maxPage) { this._curPage = this._maxPage }
        else { this._curPage = page; }
      
        var j;
        for (j=0; j < this._contentChoices.length; j++) {
            log(this.name, j+": "+this._contentChoices[j].key+": "+this._contentChoices[j].value);
        }
    
        var _first_line = this._curPage * this._maxContentRows;
        var choices = {};
        var i = 0;
        var c, _key, _padding, j;
        var _zero = "0";
     
        log(this.name, "Showing page "+page+", starts at "+_first_line+", pages with "+this._maxContentRows+" lines");
        while (i < this._maxContentRows && _first_line + i < this._contentChoices.length ) {
            c = this._contentChoices[_first_line+i];
            _key = (_first_line+i).toString();
            if (_key.length < 4) {
                j = 4 - _key.length;
                while (j--) {
                    _key = "0" + _key;
                }
                _key = "C_"+ _key;
            }
            choices[_key] = this._formatChoice(c.value);
            i++;
        }
        while (i < this._msRows) {
            choices['Z96_SPACER_'+i] = "";
            i++;
        }
        if (this._curPage > 0) { 
            choices['Z97_PREVIOUS_PAGE'] = {text:"Previous Page", color:this._menuColor, alignment:"CENTER"}; 
        }
        if (this._curPage < this._maxPage ) { 
            choices['Z98_NEXT_PAGE'] = {text:"Next Page", color:this._menuColor, alignment:"CENTER"}; 
        }
        choices['Z99_EXIT'] = {text:"Exit", color:this._menuColor, alignment:"CENTER"};
        this._contentParameters.choices = choices;
    
        var k;
        var c;
        var i = 0;
        for (k in this._contentParameters.choices) {
            c = this._contentParameters.choices[k];
            log(this.name, "linha "+i+": "+k+":{text:"+c.text+"}"); 
            i++;
        }
    
        player.ship.hudHidden = true;
        mission.runScreen(this._contentParameters, this._pChoice, this);
    }
    
    
    this._formatChoice = function (choice) {
        if (typeof choice === "object") {
            return { text: choice.text, color:(choice.color? choice.color : this._itemColor), alignment: (choice.algnment ? choice.alignment : "LEFT")};
        } else {
            return { text: choice, color:this._itemColor, alignment:"LEFT"} ;
        }
    }
    
    
    this._pChoice = function (choice) {
        log(this.name, "choice: "+choice);
        if (choice === 'Z97_PREVIOUS_PAGE') {
            this._showPage(this._curPage - 1);
        }
        else if (choice === 'Z98_NEXT_PAGE') {
            this._showPage(this._curPage + 1);
        }
        else if (choice === "Z99_EXIT") {
            player.ship.hudHidden = this._hudHidden;
            this._contentCallback.call(this._contentThisObj, this._contentExitChoice);
        }
        else {
            var c;
            c = choice.replace("C_", "") * 1;
            log(this.name, choice+"("+c +"): "+this._contentChoices[c].key+" ("+this._contentChoices[c].value+") chosen");
            player.ship.hudHidden = this._hudHidden;
            this._contentCallback.call(this._contentThisObj, this._contentChoices[c].key);
        }
    }