| Config/script.js | "use strict";
this.name = "ShipRepurchase";
this.author = "phkb";
this.copyright = "2017 phkb";
this.description = "Implements a ship repurchase process for when the player's ship is destroyed.";
this.licence = "CC BY-NC-SA 4.0";
this._debug = false;
this._doRepurchase = false;
this._repurchasePercent = 0.1;
this._baseShipCost = 0;
this._fullShipCost = 0;
this._jamesonPoint = 16;
this._portable = [];
this._roles = [];
this._lastEjectDates = [];
this._ejectDate = 0;
this._simulator = false;
this._insuranceDate = 0;
this._scTimer = null;
this._cargoRecovery = [];
this._cargoRecoverySummary = "";
this._applyPenalty = false;
this._interstellarRecovery = false;
this._interstellarMode = false;
this._interstellarFreeShip = {
    key: "cobramk1-player",
    name: expandDescription("[sr_cobra1]")
};
this._storedShip = "";
// dictionary of equipment key/min techlevel items to apply as an override when auto-repairing equipment
// additional equipment items could be added here for further restrictions on auto-repair
this._autoRepairMinTL = {
    "EQ_CLOAKING_DEVICE": 14,
    "EQ_MILITARY_JAMMER": 99,
    "EQ_MILITARY_SCANNER_FILTER": 99
};
// used to calculate how far back the premium calculation will look for repurchase events. based on player.score
this._periodLevels = {
    "0": 30,
    "8": 30,
    "16": 30,
    "32": 60,
    "64": 90,
    "128": 120,
    "512": 180,
    "2560": 270,
    "6400": 365
};
// dictionary of min values and key/name dictionary
// used to determine which free ship is available based on the original cost of the players current ship
this._freeShipTrader = {
    "650000": {
        key: "anaconda-player",
        name: expandDescription("[sr_anaconda]")
    },
    "495000": {
        key: "boa-mk2-player",
        name: expandDescription("[sr_boa2]")
    },
    "450000": {
        key: "boa-player",
        name: expandDescription("[sr_boa]")
    },
    "200000": {
        key: "python-player",
        name: expandDescription("[sr_python]")
    },
    "150000": {
        key: "cobra3-player",
        name: expandDescription("[sr_cobra3]")
    },
    "145000": {
        key: "morayMED-player",
        name: expandDescription("[sr_moraymed]")
    },
    "125000": {
        key: "moray-player",
        name: expandDescription("[sr_moray]")
    },
    "100000": {
        key: "cobramk1-player",
        name: expandDescription("[sr_cobra1]")
    },
    "65000": {
        key: "adder-player",
        name: expandDescription("[sr_adder]")
    }
};
this._freeShipHunter = {
    "485000": {
        key: "ferdelance-player",
        name: expandDescription("[sr_fdl]")
    },
    "375000": {
        key: "asp-player",
        name: expandDescription("[sr_asp2]")
    },
    "150000": {
        key: "cobra3-player",
        name: expandDescription("[sr_cobra3]")
    },
    "125000": {
        key: "moray-player",
        name: expandDescription("[sr_moray]")
    },
};
this._freePick1 = null; // ship key of the free ship the player chose
this._freePick2 = null;
// some OXP ship packs change the default price of Cobra Mark III's and Mark I's. 
// We're putting overrides in here so we can be consistent.
this._overrideBasePrice = {
    "Cobra Mark III": 150000,
    "Cobra Mark I": 100000
};
//-------------------------------------------------------------------------------------------------------------
this.startUpComplete = function () {
    var trueValues = ["yes", "1", 1, "true", true];
    if (missionVariables.ShipRepurchase_LastEjectDates) this._lastEjectDates = JSON.parse(missionVariables.ShipRepurchase_LastEjectDates);
    if (missionVariables.ShipRepurchase_InsuranceDate) this._insuranceDate = missionVariables.ShipRepurchase_InsuranceDate;
    if (missionVariables.ShipRepurchase_StoredShip) this._storedShip = missionVariables.ShipRepurchase_StoredShip;
    if (missionVariables.ShipRepurchase_InterstellarMode) this._interstellarMode = (trueValues.indexOf(missionVariables.ShipRepurchase_InterstellarMode) >= 0 ? true : false);
    /*if (worldScripts.ShipConfiguration_Armour) {
        // monkey patch shipconfig armour so we can make sure things get done in the right sequence
        var sca = worldScripts.ShipConfiguration_Armour;
        sca.$sr_shipLaunchedFromStation = sca.shipLaunchedFromStation;
        delete sca.shipLaunchedFromStation;
    }*/
}
//-------------------------------------------------------------------------------------------------------------
this.playerWillSaveGame = function () {
    if (this._lastEjectDates.length > 0) {
        missionVariables.ShipRepurchase_LastEjectDates = JSON.stringify(this._lastEjectDates);
    } else {
        delete missionVariables.ShipRepurchase_LastEjectDates;
    }
    missionVariables.ShipRepurchase_InsuranceDate = this._insuranceDate;
    if (this._storedShip != "") {
        missionVariables.ShipRepurchase_StoredShip = this._storedShip;
    } else {
        delete missionVariables.ShipRepurchase_StoredShip;
    }
    missionVariables.ShipRepurchase_InterstellarMode = this._interstellarMode;
}
//-------------------------------------------------------------------------------------------------------------
this.shipLaunchedEscapePod = function (pod) {
    if (this._simulator) return;
    this._ejectDate = clock.adjustedSeconds;
}
//-------------------------------------------------------------------------------------------------------------
// todo: what happens if multiple events turn up simultaneously?
this.shipTakingDamage = function (amount, whom, type) {
    if (this._simulator) return;
    // are we just about to die?
    // award the escape pod and eject
    if (this._debug) log(this.name, "shipTakingDamage " + amount + ", " + type + " : current ship energy " + (player.ship ? player.ship.energy : "no player ship!"));
    if (player.ship && player.ship.energy <= 0) {
        // turn off ship config functions
        var sc = worldScripts.ShipConfiguration_Core;
        if (sc) {
            sc._repairing = true;
            sc._adding = true;
        }
        var p = player.ship;
        if (this._debug) log(this.name, "attempting to award escape pod");
        var result = p.awardEquipment("EQ_ESCAPE_POD");
        if (this._debug) log(this.name, "award escape pod result " + result);
        // if the result of adding an escape pod was false, this means the player already had a functional one
        this._cargoRecoverySummary = "";
        if (result === false) {
            var eq;
            if (p.equipmentStatus("EQ_ESCAPE_POD") == "EQUIPMENT_OK") {
                eq = EquipmentInfo.infoForKey("EQ_ESCAPE_POD");
                this._cargoRecoverySummary = "some";
            }
            if (p.equipmentStatus("EQ_ESCAPE_POD_PLUS") == "EQUIPMENT_OK") {
                eq = EquipmentInfo.infoForKey("EQ_ESCAPE_POD_PLUS")
                this._cargoRecoverySummary = "most";
            }
            var tm = parseInt(eq.scriptInfo["max_rescue_time"]);
            // set the amount of time for the rescue
            if (p.hasOwnProperty("escapePodRescueTime")) p.escapePodRescueTime = ((tm / 2) + (Math.random() * (tm / 2))) * 3600;
            this._repurchasePercent = parseFloat(eq.scriptInfo["repurchase_percent"]);
            // grab a copy of all the cargo on board
            this._cargoRecovery.length = 0;
            var cr = parseFloat(eq.scriptInfo["cargo_recovery"]);
            if (cr > 0) {
                var t = 0;
                var m = p.manifest;
                if (m.list && m.list.length > 0) {
                    for (var i = 0; i < m.list.length; i++) {
                        if (m.list[i].unit == "t" && m.list[i].quantity > 0) {
                            this._cargoRecovery.push(m.list[i]);
                            t += m.list[i].quantity;
                        }
                    }
                }
                var rm = parseInt((1 - cr) * t); // amount of cargo to remove
                if (rm > 0) {
                    var pt = 0;
                    var end = false;
                    do {
                        if (this._cargoRecovery[pt].quantity > 0) {
                            this._cargoRecovery[pt].quantity -= 1;
                            rm -= 1;
                        }
                        pt += 1;
                        if (pt >= this._cargoRecovery.length) pt = 0;
                        if (rm <= 0) end = true;
                    } while (end == false);
                } else {
                    this._cargoRecoverySummary = "";
                    this._cargoRecovery.length = 0;
                }
            } else {
                this._cargoRecoverySummary = "";
            }
        } else {
            // null out any gold, platinum and gems - they won't fit in the lifesuit
            this._cargoRecovery = [];
            p.manifest["gold"] = 0;
            p.manifest["platinum"] = 0;
            p.manifest["gem_stones"] = 0;
            // manually remove passengers and parcels
            // remove parcels - these won't fit either
            for (var i = p.parcels.length - 1; i >= 0; i--) {
                // tell player about this via an arrival report
                player.addMessageToArrivalReport(expandDescription("[parcel-lost]", {
                    name: p.parcels[i].name
                }));
                // send an email message for our special case
                var w = worldScripts.EmailSystem;
                if (w) {
                    var sndr = expandDescription("[parcel-contract-sender]");
                    var msg = expandDescription("[sr-parcel-contract-terminated]", {
                        contractname: p.parcels[i].name,
                        systemname: System.systemNameForID(p.parcels[i].destination),
                        time: global.clock.clockStringForTime(clock.seconds + p.parcels[i].eta)
                    });
                    var subj = expandDescription("[sr-parcel-contract-terminated-subject]");
                    w.$createEmail({
                        sender: sndr,
                        subject: subj,
                        date: global.clock.seconds,
                        message: msg,
                        expiryDays: worldScripts.GalCopAdminServices._defaultExpiryDays
                    });
                }
                var bb = worldScripts.BulletinBoardSystem;
                var cobb = worldScripts.ContractsOnBB;
                if (bb && cobb) {
                    for (var j = 0; j < bb._data.length; j++) {
                        var itm = bb._data[j];
                        if (itm != null && itm.accepted === true) {
                            // is this item the one we're dealing with?
                            if (itm.destination === p.parcels[i].destination && itm.payment === p.parcels[i].fee && itm.source === p.parcels[i].start) {
                                bb.$removeBBMission(itm.ID);
                                break;
                            }
                        }
                    }
                }
                p.removeParcel(p.parcels[i].name);
                player.decreaseParcelReputation();
            }
            // remove passengers - they won't fit either
            var list = "";
            var count = p.passengers.length;
            if (count >= 1) {
                for (var i = count - 1; i >= 0; i--) {
                    list += (list === "" ? "" : (i === count - 1 ? " " + expandDescription("[sr_and]") + " " : ", ")) + p.passengers[i].name;
                    // send an email message for our special case
                    var w = worldScripts.EmailSystem;
                    if (w) {
                        var sndr = expandDescription("[passenger-contract-sender]");
                        var msg = expandDescription("[sr-passenger-contract-terminated]", {
                            contractname: p.passengers[i].name,
                            systemname: System.systemNameForID(p.passengers[i].destination),
                            time: clock.clockStringForTime(clock.seconds + p.passengers[i].eta)
                        });
                        var subj = expandDescription("[sr-passenger-contract-terminated-subject]");
                        w.$createEmail({
                            sender: sndr,
                            subject: subj,
                            date: global.clock.seconds,
                            message: msg,
                            expiryDays: worldScripts.GalCopAdminServices._defaultExpiryDays
                        });
                    }
                    var bb = worldScripts.BulletinBoardSystem;
                    var cobb = worldScripts.ContractsOnBB;
                    if (bb && cobb) {
                        for (var j = 0; j < bb._data.length; j++) {
                            var itm = bb._data[j];
                            if (itm != null && itm.accepted === true) {
                                // is this item the one we're dealing with?
                                if (itm.destination === p.passengers[i].destination && itm.payment === p.passengers[i].fee && itm.source === p.passengers[i].start) {
                                    bb.$removeBBMission(itm.ID);
                                    break;
                                }
                            }
                        }
                    }
                    p.removePassenger(p.passengers[i].name);
                    player.decreasePassengerReputation();
                }
                if (count > 1) {
                    player.addMessageToArrivalReport(expandDescription("[passengers-lost]", {
                        names: list
                    }));
                } else {
                    player.addMessageToArrivalReport(expandDescription("[passenger-lost]", {
                        name: list
                    }));
                }
            }
        }
        if (this._debug) log(this.name, "auto-ejecting...");
        p.abandonShip();
        if (sc) {
            sc._repairing = false;
            sc._adding = false;
        }
    }
}
//-------------------------------------------------------------------------------------------------------------
this.escapePodSequenceOver = function () {
    if (this._simulator) return;
    this._doRepurchase = true;
    // stop hull repairs from showing up
    if (missionVariables.BattleDamage_status != "OK") missionVariables.BattleDamage_status = "OK";
    if (system.isInterstellarSpace) {
        this._interstellarRecovery = true;
        this._cargoRecovery.length = 0; // no cargo recovery in interstellar space
        this._cargoRecoverySummary = "";
        // only continue if IST is not installed - it will control the recovery chance and end-point
        if (!worldScripts["IST_masterScript"]) {
            if (system.stations.length > 0) {
                // similar logic as for IST
                // naval rescue - near
                if (this._debug === true || Math.random() <= 0.8) {
                    var navalCarriers = system.filteredEntities(this, this.$isNavalCarrier, player.ship, 30000);
                    if (navalCarriers.length > 0) {
                        player.setEscapePodDestination(navalCarriers[0]);
                        return;
                    }
                }
                // naval rescue - further away
                if (this._debug === true || Math.random() <= 0.4) {
                    navalCarriers = system.filteredEntities(this, this.$isNavalCarrier, player.ship, 100000);
                    if (navalCarriers.length > 0) {
                        player.setEscapePodDestination(navalCarriers[0]);
                        return;
                    }
                }
                // other station types (eg Erehwon)
                if (this._debug === true || Math.random() <= 0.4) {
                    var nonNavalCarriers = system.filteredEntities(this, this.$isNotNavalCarrier, player.ship, 200000);
                    if (nonNavalCarriers.length > 0) player.setEscapePodDestination(nonNavalCarriers[0]);
                }
            }
        }
    }
}
//-------------------------------------------------------------------------------------------------------------
this.playerBoughtEquipment = function (equipmentKey) {
    if (equipmentKey === "EQ_REPURCHASE_INSURANCE") this._insuranceDate = clock.adjustedSeconds;
}
//-------------------------------------------------------------------------------------------------------------
this.missionScreenOpportunity = function () {
    // check if our insurance has lapsed
    if (this._insuranceDate != 0 && (clock.adjustedSeconds - this._insuranceDate) > (86400 * 60) && this._doRepurchase === false) {
        player.ship.removeEquipment("EQ_REPURCHASE_INSURANCE");
        this._insuranceDate = 0;
        mission.runScreen({
            screenID: "oolite-insurance-expiry-map",
            title: expandDescription("[sr_repurchase_ins]"),
            overlay: {
                name: "shiprepurchase_logo.png",
                height: 546
            },
            message: expandDescription("[insurance_lapsed]"),
            exitScreen: "GUI_SCREEN_STATUS"
        });
        return;
    }
    if (this._passengerList && this._passengerList.length > 0) {
        if (this._passengerList.length > 1) {
            // more than 1 passenger
            var list = "";
            for (var i = 0; i < this._passengerList.length; i++) {
                list += (list === "" ? "" : (i === this._passengerList.length - 1 ? " and " : ", ")) + this._passengerList[i];
                player.decreasePassengerReputation();
            }
            mission.runScreen({
                screenID: "oolite-passengers-stranded-map",
                title: "Passengers",
                message: expandDescription("[passengers-stranded]", {
                    names: list,
                    location: system.name
                }),
                exitScreen: "GUI_SCREEN_STATUS"
            });
        } else {
            // just 1 passenger
            player.decreasePassengerReputation();
            mission.runScreen({
                screenID: "oolite-passenger-stranded-map",
                title: expandDescription("[sr_passengers]"),
                message: expandDescription("[passenger-stranded]", {
                    name: this._passengerList[0],
                    location: system.name
                }),
                exitScreen: "GUI_SCREEN_STATUS"
            });
        }
        this._passengerList.length = 0;
        return;
    }
    // have we been rescued in interstellar space?
    if (this._interstellarRecovery === true) {
        this._interstellarRecovery = false;
        // turn on the interstellar mode
        this._interstellarMode = true;
        // turn off the standard repurchase screen
        this._doRepurchase = false;
        var p = player.ship;
        var hud = p.hud;
        var eq = p.equipment;
        // populate the portable list
        this._portable.length = 0;
        for (var i = 0; i < eq.length; i++) {
            var item = eq[i];
            if (item.isPortableBetweenShips === true) this._portable.push(item.equipmentKey);
        }
        this.$rememberPassengers();
        // switch to the "loaner"
        player.replaceShip(this._interstellarFreeShip.key);
        // portable equipment is not automatically transferred if the shipkey changes
        this.$addPortable();
        p.serviceLevel = 75;
        p.hud = hud;
        if (p.dockedStation.isPolice) {
            mission.runScreen({
                screenID: "oolite-naval-recovery-map",
                title: expandDescription("[sr_naval_command]"),
                message: expandDescription("[interstellar_rescue_navy]", {
                    shiptype: this._interstellarFreeShip.name
                }),
                exitScreen: "GUI_SCREEN_STATUS"
            });
        } else {
            // a non-police/navy station in interstellar space
            mission.runScreen({
                screenID: "oolite-interstellar-recovery-map",
                title: expandDescription("[sr_station_control]"),
                message: expandDescription("[interstellar_rescue_normal]", {
                    shiptype: this._interstellarFreeShip.name
                }),
                exitScreen: "GUI_SCREEN_STATUS"
            });
        }
        return;
    }
    // are we doing the repurchase process now?
    if (this._doRepurchase === true) {
        this._doRepurchase = false;
        this.$showRepurchaseScreen();
    }
}
//-------------------------------------------------------------------------------------------------------------
this.shipLaunchedFromStation = function (station) {
    if (this.$simulatorRunning()) {
        this._simulator = true;
        return;
    }
    this._simulator = false;
    // what's our repurchase percentage? make an initial setting here but we'll recheck before an auto-eject
    var p = player.ship;
    this._repurchasePercent = 0.1;
    if (p.equipmentStatus("EQ_ESCAPE_POD") == "EQUIPMENT_OK") this._repurchasePercent = parseFloat(EquipmentInfo.infoForKey("EQ_ESCAPE_POD").scriptInfo["repurchase_percent"]);
    if (p.equipmentStatus("EQ_ESCAPE_POD_PLUS") == "EQUIPMENT_OK") this._repurchasePercent = parseFloat(EquipmentInfo.infoForKey("EQ_ESCAPE_POD_PLUS").scriptInfo["repurchase_percent"]);;
    if (worldScripts.ShipConfiguration_Armour) {
        this._scTimer = new Timer(this, this.$getSCArmour, 0.5, 0);
    }
}
//-------------------------------------------------------------------------------------------------------------
this.$getSCArmour = function $getSCArmour() {
    var p = player.ship;
    this._holdSCAArmourFrontType = p.script._frontArmourType;
    this._holdSCAArmourAftType = p.script._aftArmourType;
}
//-------------------------------------------------------------------------------------------------------------
this.shipExitedWitchspace = function () {
    // check if we've been recovered in interstellar space, and if so, turn on the repurchase screen
    if (this._interstellarMode === true) {
        // in case we have a misjump in interstellar space
        if (system.isInterstellarSpace === false) this._doRepurchase = true;
        return;
    }
    // store ship and equipment (but not cargo) in case we need it later
    // but only if we're not already in interstellar recovery mode
    var shipinfo = worldScripts["Ship_Storage_Helper.js"].storeCurrentShip();
    var data = JSON.parse(shipinfo);
    data[8].length = 0; // remove any cargo
    data[10].length = 0; // remove any passengers
    this._storedShip = JSON.stringify(data);
}
//-------------------------------------------------------------------------------------------------------------
this.$showRepurchaseScreen = function $showRepurchaseScreen() {
    // at this point we should have a new ship + equipment (damaged or otherwise) to work with
    // calculate cost of replacement ship + equipment
    var p = player.ship;
    var col1 = 22;
    var col2 = 32 - col1;
    p.hudHidden = true;
    // store the current list of player roles
    this._roles = [];
    for (var i = 0; i < p.roleWeights.length; i++) {
        this._roles.push(p.roleWeights[i]);
    }
    // check for an interstellar mode recovery
    if (system.isInterstellarSpace === false && this._interstellarMode === true && this._storedShip != "") {
        var lmss = worldScripts.LMSS_Core;
        if (lmss) lmss._switching = true;
        // restore the players original ship
        worldScripts["Ship_Storage_Helper.js"].restoreStoredShip(this._storedShip);
        this._storedShip = "";
        if (lmss) lmss._switching = false;
    }
    // first, repair everything, so the ship price will be accurate
    var eq = p.equipment;
    this._portable.length = 0;
    for (var i = 0; i < eq.length; i++) {
        var item = eq[i];
        var sts = "";
        var count = 0;
        var m_sts = p.equipmentStatus(item.equipmentKey, true);
        if (m_sts["EQUIPMENT_DAMAGED"] > 0) {
            sts = "EQUIPMENT_DAMAGED";
            count = m_sts["EQUIPMENT_DAMAGED"];
        }
        if (sts === "EQUIPMENT_DAMAGED") {
            var autoRepair = this._autoRepairMinTL[item.equipmentKey];
            if (!autoRepair || parseInt(autoRepair) <= p.dockedStation.equivalentTechLevel) {
                for (var j = 1; j <= count; j++) {
                    p.setEquipmentStatus(item.equipmentKey, "EQUIPMENT_OK");
                }
            }
        }
        if (item.isPortableBetweenShips === true) this._portable.push(item.equipmentKey);
    }
    // repair any ship config armour
    if (worldScripts.ShipConfiguration_Armour) {
        if (this._holdSCAArmourFrontType != "") {
            if (p.equipmentStatus(this._holdSCAArmourFrontType) != "EQUIPMENT_OK") p.awardEquipment(this._holdSCAArmourFrontType);
            p.script._armourFront = 100;
        }
        if (this._holdSCAArmourAftType != "") {
            if (p.equipmentStatus(this._holdSCAArmourAftType) != "EQUIPMENT_OK") p.awardEquipment(this._holdSCAArmourAftType);
            p.script._armourAft = 100;
        }
    }
    // battle damage OXP
    if (worldScripts["Battle Damage"]) {
        p.awardEquipment("EQ_HULL_REPAIR");
        missionVariables.BattleDamage_status = "OK";
    }
    // get the cost
    this._fullShipCost = p.price;
    if (this._overrideBasePrice[p.name]) {
        this._baseShipCost = this._overrideBasePrice[p.name];
        // if we needed to override the price, we should update the full ship/equipment price as well
        var sd = Ship.shipDataForKey(p.dataKey);
        if (sd._oo_shipyard) {
            var base = sd._oo_shipyard.price;
            this._fullShipCost = (this._fullShipCost - base) + this._baseShipCost;
        }
    } else {
        var sd = Ship.shipDataForKey(p.dataKey);
        if (sd._oo_shipyard) {
            this._baseShipCost = sd._oo_shipyard.price;
        } else {
            sd = this.$getShipDataEntryWithShipyard(p.name);
            if (sd) {
                this._baseShipCost = sd._oo_shipyard.price;
            } else {
                // this should never happen, but, you know...
                log(this.name, "!!ERROR: Unable to find valid shipyard price value for shipkey " + p.dataKey);
                this._baseShipCost = p.price;
            }
        }
    }
    var ins = false;
    if (this._repurchasePercent < 0.1) {
        if (p.equipmentStatus("EQ_REPURCHASE_INSURANCE") === "EQUIPMENT_OK") ins = true;
    }
    var repurchase = this._fullShipCost * (this._repurchasePercent * this.$calculatePremium());
    if (ins && player.score >= this._jamesonPoint) repurchase = 1000;
    if (player.score < this._jamesonPoint) repurchase = 200;
    var curChoices = {};
    var text = expandDescription("[sr_current_credits]", { amount: formatCredits(player.credits, true, true) }) + "\n\n";
    if (this._interstellarMode === false) {
        text += expandDescription("[sr_ship_destroyed]") + " ";
    } else {
        text += expandDescription("[sr_ship_destroyed_int]") + " ";
    }
    text += expandDescription("[sr_repurchase_options]");
    if (this._cargoRecoverySummary != "") {
        text += "\n\n" + expandDescription("[sr_recover_cargo]", { amount: this._cargoRecoverySummary });
    }
    var label = expandDescription("[sr_current_ship]");
    if (this._interstellarMode === true) label = expandDescription("[sr_original_ship]");
    curChoices["01_CURRENT"] = {
        text: this.$padTextRight(expandDescription("[sr_ship_with_equipment]", { label: label, shipclass: p.shipClassName }), col1) +
            this.$padTextLeft((repurchase === 0 ? expandDescription("[sr_free]") : formatCredits(repurchase, true, true)), col2),
        unselectable: (player.credits >= repurchase ? false : true)
    };
    this._freePick1 = null;
    this._freePick2 = null;
    var types = Object.keys(this._freeShipTrader);
    for (var i = 0; i < types.length; i++) {
        if (parseInt(types[i]) < this._baseShipCost && this._freePick1 == null && this._freeShipTrader[types[i]].name != p.shipClassName) {
            this._freePick1 = this._freeShipTrader[types[i]];
        }
    }
    // if we didn't get a hit, just choose the lowest value one (Adder)
    if (this._freePick1 == null) {
        this._freePick1 = this._freeShipTrader[types[types.length - 1]];
    } else {
        var types = Object.keys(this._freeShipHunter);
        for (var i = 0; i < types.length; i++) {
            if (parseInt(types[i]) < this._baseShipCost && this._freePick2 == null && this._freeShipHunter[types[i]].name != p.shipClassName) {
                this._freePick2 = this._freeShipHunter[types[i]];
            }
        }
        // if they're the same, just show first one
        if (this._freePick2 != null && this._freePick2.name == this._freePick1.name) this._freePick2 = null;
    }
    // if the player is already in an adder, we have to show a free Adder as a replacement
    // so don't show a "ship only" repurchase option if we're going to offer a free one anyway
    if (p.shipClassName != "Adder") {
        var baseRepurchase = this._baseShipCost * (this._repurchasePercent * this.$calculatePremium());
        if (ins && player.score >= this._jamesonPoint) baseRepurchase = 500;
        if (player.score < this._jamesonPoint) baseRepurchase = 100;
        curChoices["01_CURRENT_BASE"] = {
            text: this.$padTextRight(expandDescription("[sr_stock_ship]", { label: label.toLowerCase(), shipclass: p.shipClassName }), col1) +
                this.$padTextLeft((baseRepurchase === 0 ? expandDescription("[sr_free]") : formatCredits(baseRepurchase, true, true)), col2),
            unselectable: (player.credits >= baseRepurchase ? false : true)
        }
    }
    // a stock stepdown is always free
    curChoices["04_BASE_SHIP"] = {
        text: this.$padTextRight(expandDescription("[sr_stock]", { name: this._freePick1.name }), col1) +
            this.$padTextLeft(expandDescription("[sr_free]"), col2),
        unselectable: false
    };
    if (this._freePick2 != null) {
        curChoices["04_BASE_SHIP2"] = {
            text: this.$padTextRight(expandDescription("[sr_stock]", { name: this._freePick2.name }), col1) +
                this.$padTextLeft(expandDescription("[sr_free]"), col2),
            unselectable: false
        }
    }
    var opts = {
        screenID: "oolite-shiprepurchase-details-map",
        title: expandDescription("[sr_repurchase_title]"),
        overlay: {
            name: "shiprepurchase_logo.png",
            height: 546
        },
        allowInterrupt: false,
        exitScreen: "GUI_SCREEN_STATUS",
        choices: curChoices,
        initialChoicesKey: "01_CURRENT",
        message: text
    };
    mission.runScreen(opts, this.$screenHandler, this);
    this._interstellarMode = false;
}
//-------------------------------------------------------------------------------------------------------------
this.$screenHandler = function $screenHandler(choice) {
    var p = player.ship;
    var hud = p.hud;
    p.hudHidden = false;
    var ins = false;
    if (this._repurchasePercent < 0.1) {
        if (p.equipmentStatus("EQ_REPURCHASE_INSURANCE") === "EQUIPMENT_OK") ins = true;
    }
    if (choice === "01_CURRENT") {
        // nothing to do for this option: current ship and equipment should already be present.
        var repurchase = (this._fullShipCost * (this._repurchasePercent * this.$calculatePremium()));
        if (ins && player.score >= this._jamesonPoint) {
            repurchase = 1000;
            p.removeEquipment("EQ_REPURCHASE_INSURANCE");
        }
        if (player.score < this._jamesonPoint) repurchase = 200;
        player.credits -= repurchase;
        p.serviceLevel = 95; // replacement ship shouldn't need to be serviced any time soon.
        player.consoleMessage(expandDescription("[sr_deducted]", { cost: formatCredits(repurchase, true, true) }));
    } else {
        // make a note of any passengers who will be getting dumped here.
        this.$rememberPassengers();
        // remove any secondary lasers
        if (worldScripts.LMSS_Core) {
            var lmss = worldScripts.LMSS_Core;
            if (lmss._forwardAltKey && lmss._forwardAltKey != "EQ_WEAPON_NONE") lmss._forwardAltKey = "EQ_WEAPON_NONE";
            if (lmss._aftAltKey && lmss._aftAltKey != "EQ_WEAPON_NONE") lmss._aftAltKey = "EQ_WEAPON_NONE";
            if (lmss._portAltKey && lmss._portAltKey != "EQ_WEAPON_NONE") lmss._portAltKey = "EQ_WEAPON_NONE";
            if (lmss._starboardAltKey && lmss._starboardAltKey != "EQ_WEAPON_NONE") lmss._starboardAltKey = "EQ_WEAPON_NONE";
        }
        // do we need to clean up any other 3rd party packs that have data stored in mission or local variables?
        // note: some base models come with an escape pod - should we remove it after changing the player ship?
        var cost = this._baseShipCost;
        if (choice === "01_CURRENT_BASE") {
            player.replaceShip(p.dataKey, p.entityPersonality);
            var deduct = (cost * (this._repurchasePercent * this.$calculatePremium()));
            if (ins && player.score >= this._jamesonPoint) {
                deduct = 500;
                p.removeEquipment("EQ_REPURCHASE_INSURANCE");
            }
            if (player.score < this._jamesonPoint) deduct = 100;
            player.credits -= deduct;
            p.serviceLevel = 95; // replacement ship shouldn't need to be serviced any time soon.
            player.consoleMessage(expandDescription("[sr_deducted]", { cost: formatCredits(deduct, true, true) }));
        }
        if (choice === "04_BASE_SHIP") {
            player.replaceShip(this._freePick1.key);
            p.serviceLevel = 95; // replacement ship shouldn't need to be serviced any time soon.
            // portable equipment is not automatically transferred if the shipkey changes
            this.$addPortable();
        }
        if (choice === "04_BASE_SHIP2") {
            player.replaceShip(this._freePick2.key);
            p.serviceLevel = 95; // replacement ship shouldn't need to be serviced any time soon.
            // portable equipment is not automatically transferred if the shipkey changes
            this.$addPortable();
        }
        p.hud = hud;
    }
    // restore any recovered cargo
    this.$restoreCargo();
    // make sure the player has a complimentary fuel load
    p.fuel = 7;
    // add the date to our array here, so that it's only amended after the replacement is bought
    if (player.score >= this._jamesonPoint) this._lastEjectDates.push(this._ejectDate);
    // restore the previous list of roles
    for (var i = 0; i < p.roleWeights.length; i++) {
        if (this._roles.length - 1 >= i) p.setPlayerRole(this._roles[i], i);
    }
}
//-------------------------------------------------------------------------------------------------------------
this.$addPortable = function $addPortable() {
    if (this._portable.length > 0) {
        var p = player.ship;
        for (var i = 0; i < this._portable.length; i++) {
            p.awardEquipment(this._portable[i]);
        }
    }
}
//-------------------------------------------------------------------------------------------------------------
this.$restoreCargo = function $restoreCargo() {
    if (this._cargoRecovery.length > 0) {
        var p = player.ship;
        var m = p.manifest;
        for (var i = 0; i < this._cargoRecovery.length; i++) {
            m[this._cargoRecovery[i].commodity] += this._cargoRecovery[i].quantity;
        }
    }
}
//-------------------------------------------------------------------------------------------------------------
this.$calculatePremium = function $calculatePremium() {
    if (this._applyPenalty == false) {
        return 1;
    } else {
        // how many times has the player ejected in period?
        var period = 365;
        // get a period based on the player score.
        var keys = Object.keys(this._periodLevels);
        for (var i = 0; i < keys.length; i++) {
            if (player.score >= parseInt(keys[i])) {
                period = parseInt(this._periodLevels[keys[i]]);
            }
        }
        var mth = clock.adjustedSeconds - (86400 * period);
        var count = 0;
        if (this._lastEjectDates.length > 0) {
            for (var i = 0; i > this._lastEjectDates.length; i++) {
                if (this._lastEjectDates[i] > mth) count += 1;
            }
        }
        // return a (1.n) value as a multiplier
        var result = 1 + ((count * 2) / 10);
        return result;
    }
}
//-------------------------------------------------------------------------------------------------------------
// appends space to currentText to the specified length in 'em'
this.$padTextRight = function $padTextRight(currentText, desiredLength, leftSwitch) {
    if (currentText == null) currentText = "";
    var hairSpace = String.fromCharCode(31);
    var ellip = "…";
    var currentLength = defaultFont.measureString(currentText.replace(/%%/g, "%"));
    var hairSpaceLength = defaultFont.measureString(hairSpace);
    // calculate number needed to fill remaining length
    var padsNeeded = Math.floor((desiredLength - currentLength) / hairSpaceLength);
    if (padsNeeded < 1) {
        // text is too long for column, so start pulling characters off
        var tmp = currentText;
        do {
            tmp = tmp.substring(0, tmp.length - 2) + ellip;
            if (tmp === ellip) break;
        } while (defaultFont.measureString(tmp.replace(/%%/g, "%")) > desiredLength);
        currentLength = defaultFont.measureString(tmp.replace(/%%/g, "%"));
        padsNeeded = Math.floor((desiredLength - currentLength) / hairSpaceLength);
        currentText = tmp;
    }
    // quick way of generating a repeated string of that number
    if (!leftSwitch || leftSwitch === false) {
        return currentText + new Array(padsNeeded).join(hairSpace);
    } else {
        return new Array(padsNeeded).join(hairSpace) + currentText;
    }
}
//-------------------------------------------------------------------------------------------------------------
// appends space to currentText to the specified length in 'em'
this.$padTextLeft = function $padTextLeft(currentText, desiredLength) {
    return this.$padTextRight(currentText, desiredLength, true);
}
//-------------------------------------------------------------------------------------------------------------
// routine to check the combat simulator worldscript, to see if it's running or not
this.$simulatorRunning = function $simulatorRunning() {
    var w = worldScripts["Combat Simulator"];
    if (w && w.$checkFight && w.$checkFight.isRunning) return true;
    return false;
}
//-------------------------------------------------------------------------------------------------------------
this.$isNavalCarrier = function $isNavalCarrier(e) {
    return e.isShip && e.isPolice && e.isStation;
}
//-------------------------------------------------------------------------------------------------------------
this.$isNotNavalCarrier = function $isNotNavalCarrier(e) {
    return e.isShip && !e.isPolice && e.isStation && !e.hasRole("generationship") && (e.allegiance == null || e.allegiance != "thargoid");
}
//-------------------------------------------------------------------------------------------------------------
// gets a list of all the current passenger names
this.$rememberPassengers = function $rememberPassengers() {
    var p = player.ship;
    if (p.passengers.length > 0) {
        this._passengerList = [];
        for (var i = 0; i < p.passengers.length; i++) {
            this._passengerList.push(p.passengers[i].name);
        }
    }
}
//-------------------------------------------------------------------------------------------------------------
// finds a shipdata entry that has a corresponding shipyard value, so we can get an accurate base ship cost
this.$getShipDataEntryWithShipyard = function $getShipDataEntryWithShipyard(shipName) {
    var keys = Ship.keysForRole("player");
    for (var i = 0; i < keys.length; i++) {
        var sd = Ship.shipDataForKey(keys[i]);
        if (sd.name === shipName) {
            // did we find a match that has a shipyard entry?
            if (sd._oo_shipyard) return sd;
        }
    }
    return null;
} |