| Config/script.js | "use strict";
this.name = "NavBeaconsMFD";
this.author = "phkb";
this.copyright = "2023 phkb";
this.description = "Lists all nav beacons in the system, along with distances to them.";
this.license = "CC BY-NC-SA 4.0";
this._hairSpace = String.fromCharCode(31);
this._hairSpaceLength = defaultFont.measureString(this._hairSpace);
this._ellip = "…";
this._bullet = "•";
this._bulletSpace = "";
this._timer = null;
this._distTimer = null;
this._beaconShips = [];
this._final = [];
this._finalID = [];
this._current = 0;
this._rightCol = defaultFont.measureString("9999 km") + 0.1;
this._leftCol = 14.2 - this._rightCol;
this._shipsOnly = false;
this._unitSetting = 1;
//distance unit to show in mfd.
this._distUnits = ["OU", "km", "m", "CZ"];
//basis in meters for one distUnit.
//for kiloometers, set to 1000
//for OUs, set to 905520, which is roughly the distance of planet and sun in Lave in an unmodified game.
this._unitFactor = [905520, 1000, 1, 2.08641];
this._unitRounding = [4, 0, 0, 0];
this._libSettings = {
	Name: this.name, Display: "Settings", Alias: "Navigation Beacons MFD", Alive: "_libSettings",
    Bool: {
        B0: { Name:"_shipsOnly", Def: false, Desc:"Ship/Station beacons only"},
        Info: "0 = Display beacons from ships and stations only"
    },
	SInt: {
		S0: { Name: "_unitSetting", Def: 1, Min: 0, Max: 3, Desc: "Display units" },
		Info: "0 = OU, 1 = km, 2 = m, 3 = CZ (Cavezzi)"
	},
};
//-------------------------------------------------------------------------------------------------------------
this.startUp = function() {
	if (missionVariables.NavigationBeaconsMFD_Units) {
		this._unitSetting = parseInt(missionVariables.NavigationBeaconsMFD_Units);
	}
    if (missionVariables.NavigationBeaconsMFD_ShipsOnly) {
        this._shipsOnly = (missionVariables.NavigationBeaconsMFD_ShipsOnly == "yes" ? true : false);
    }
}
//-------------------------------------------------------------------------------------------------------------
this.startUpComplete = function () {
    var measure = defaultFont.measureString;
	// register our settings, if Lib_Config is present
	if (worldScripts.Lib_Config) worldScripts.Lib_Config._registerSet(this._libSettings);
    this._bulletWidth = defaultFont.measureString(this._bullet);
    while (measure(this._bulletSpace) < this._bulletWidth) {
        this._bulletSpace += this._hairSpace;
    }
}
//-------------------------------------------------------------------------------------------------------------
this.playerWillSaveGame = function () {
	missionVariables.NavigationBeaconsMFD_Units = this._unitSetting;
    missionVariables.NavigationBeaconsMFD_ShipsOnly = (this._shipsOnly == true ? "yes" : "no");
}
//-------------------------------------------------------------------------------------------------------------
this.shipWillLaunchFromStation = function() {
    // recalc the left/right col widths, based on unit setting
    this._rightCol = defaultFont.measureString("9999" + (this._unitSetting > 1 ? "99" : "") + " " + this._unitFactor[this._unitSetting]) + 0.1;
    this._leftCol = 14.2 - this._rightCol;
}
//-------------------------------------------------------------------------------------------------------------
this.shipExitedWitchspace = this.shipLaunchedFromStation = function() {
    if (!player.ship.hasEquipmentProviding("EQ_ADVANCED_COMPASS")) return;
    if (player.ship.equipmentStatus("EQ_NAVBEACONS_MFD") != "EQUIPMENT_OK") return;
    if (!player.ship.isInSpace) return;
    // do a quick update to put "scanning" on the display
    this._final.length = 0;
    this._beaconShips.length = 0;
    this.$updateMFD(true);
    // first run after 2 seconds, rerun every 5 seconds
    this._timer = new Timer(this, this.$collateInfo.bind(this), 2, 5); 
}
//-------------------------------------------------------------------------------------------------------------
this.shipWillEnterWitchspace = function() {
    this.$stopTimers();
    if (player.ship.equipmentStatus("EQ_NAVBEACONS_MFD") == "EQUIPMENT_OK")
        player.ship.setMultiFunctionText("NavigationBeaconsMFD", "NAVIGATION BEACONS:\n\n\nInitialising...\n", false);
}
//-------------------------------------------------------------------------------------------------------------
this.shipDockedWithStation = this.shipDied = function() {
    this.$stopTimers()
    // switch the display to "Scanning" now, so when we next launch we don't get an old display showing up first before the timer clears it
    this.$updateMFD(true); 
}
//-------------------------------------------------------------------------------------------------------------
this.equipmentDamaged = function(equipKey) {
    if (equipKey == "EQ_NAVBEACONS_MFD" || equipKey == "EQ_ADVANCED_COMPASS") {
        this.$updateMFD();
        this.$stopTimers();
    }
}
//-------------------------------------------------------------------------------------------------------------
this.equipmentRepaired = function(equipKey) {
    var p = player.ship;
    if (player.ship.equipmentStatus("EQ_NAVBEACONS_MFD") != "EQUIPMENT_DAMAGED" && p.hasEquipmentProviding("EQ_ADVANCED_COMPASS")) {
        this.shipExitedWitchspace();
    } else {
        this.$updateMFD();
    }
}
//-------------------------------------------------------------------------------------------------------------
this.compassTargetChanged = function(whom, mode) {
    this.$updateMFD();
}
//-------------------------------------------------------------------------------------------------------------
this.$stopTimers = function() {
    if (this._timer && this._timer.isRunning) this._timer.stop();
    if (this._distTimer && this._distTimer.isRunning) this._distTimer.stop();    
}
//-------------------------------------------------------------------------------------------------------------
this.$collateInfo = function $collateInfo() {
    this._beaconShips = this.$findShipsWithBeacons();
    var i = this._beaconShips.length;
    while (i--) {
        this._beaconShips[i]._nb_ID = i;
    }
    if (this._distTimer && this._distTimer.isRunning) return; 
    this._distTimer = new Timer(this, this.$calcDistances.bind(this), 0.25, 1.5);
}
//-------------------------------------------------------------------------------------------------------------
this.$calcDistances = function $calcDistances() {
    var that = $calcDistances;
    var measure = (that.measure = that.measure || defaultFont.measureString);
    var _p = (that._p = that._p || player.ship);
    this._final.length = 0;
    this._finalID.length = 0;
    if (!_p || !_p.isValid || !_p.position) return;
    var i = this._beaconShips.length;
    if (i > 9) i = 9;
    while (i--) {
        var shp = this._beaconShips[i];
        var check = ~~((_p.position.distanceTo(shp) - shp.collisionRadius - _p.collisionRadius) + 0.5);
        var dist = check > 0 ? check : 0;
        var nm = (shp.beaconLabel != "" && shp.beaconLabel != null ? shp.beaconLabel : shp.displayName);
        this._finalID.unshift(shp._nb_ID);
        var distOut = " " + (dist / this._unitFactor[this._unitSetting]).toFixed(this._unitRounding[this._unitSetting]) + " " + this._distUnits[this._unitSetting] + " ";
        var width = measure(distOut) + 0.5;
        this._final.unshift(this.$padText(this._bullet + nm, 15 - width) + distOut);
    }
    this.$updateMFD();
}
//-------------------------------------------------------------------------------------------------------------
this.$findShipsWithBeacons = function() {
	function _beacons(entity) {
		return !(entity.isPlayer) && 
            (this._shipsOnly == false || entity.isShip) && 
            (entity.beaconCode != "" && entity.beaconCode != null) && 
            (entity.displayName != "Jump Marker") &&
            (entity.AI != "EscortDeck_AI.plist") &&
            (entity.isVisualEffect == false || entity.dataKey == "almanacCompass_planet" || entity.dataKey == "almanacCompass_moon");
	}
	return system.filteredEntities(this, _beacons, player.ship);
}
//-------------------------------------------------------------------------------------------------------------
this.$updateMFD = function(init) {
    var p = player.ship
    if (!p || !p.isValid) return;
    var output = "NAVIGATION BEACONS:\n";
    if (p.equipmentStatus("EQ_NAVBEACONS_MFD") == "EQUIPMENT_DAMAGED") {
        output += "\n\nEquipment damaged.\n";
    } else if (!p.hasEquipmentProviding("EQ_ADVANCED_COMPASS")) {
        output += "\n\nNo data. ASC not available.";
    } else {
        if (init == true) {
            output += "\n\nScanning...\n";
        } else {
            var max = this._final.length;
            if (max == 0) {
                output += "\n\nNo beacons in range\n";
            } else {
                if (max > 9) max = 9
                var ct = p.compassTarget;
                if (ct) {
                    //var ctn = (ct.beaconLabel != "" && ct.beaconLabel != null ? ct.beaconLabel : ct.displayName)
                    var ctn = ct._nb_ID;
                    for (var i = 0; i < max; i++) {
                        var txt = this._final[i];
                        if (ctn == this._finalID[i]) {
                            txt = this._bullet + txt.replace(this._bullet, "");
                        } else {
                            txt = txt.replace(this._bullet, this._bulletSpace);
                        }
                        output += txt + "\n";
                    }
                }
            }
        }
    }
    p.setMultiFunctionText("NavigationBeaconsMFD", output, false);
}
//-------------------------------------------------------------------------------------------------------------
// appends space to currentText to the specified length in 'em'
this.$padText = function $padText(currentText, desiredLength, leftSwitch, centreSwitch) {
    var that = $padText;
    var hairSpace = (that.hairSpace = that.hairSpace || this._hairSpace);
    var ellip = (that.ellip = that.ellip || this._ellip);
    var hairSpaceLength = (that.hairSpaceLength = that.hairSpaceLength || this._hairSpaceLength);
    var measure = (that.measure = that.measure || defaultFont.measureString);
    if (currentText == null) currentText = "";
    var currentLength = measure(currentText.replace(/%%/g, "%"));
    // calculate number needed to fill remaining length
    var padsNeeded = ~~((desiredLength - currentLength) / hairSpaceLength);
    if (padsNeeded < 1) {
        // text is too long for column, so start pulling characters off
        var tmp = currentText;
        do {
            tmp = tmp.substring(0, tmp.length - 2) + ellip;
            if (tmp === ellip) break;
        } while (measure(tmp.replace(/%%/g, "%")) > desiredLength);
        currentLength = measure(tmp.replace(/%%/g, "%"));
        padsNeeded = ~~((desiredLength - currentLength) / hairSpaceLength);
        currentText = tmp;
    }
    // quick way of generating a repeated string of that number
    if (!leftSwitch || leftSwitch === false) {
        if (!centreSwitch || centreSwitch === false) {
            return currentText + new Array(padsNeeded).join(hairSpace);
        } else {
            return currentText + new Array(parseInt(padsNeeded / 2)).join(hairSpace);
        }
    } else {
        if (!centreSwitch || centreSwitch === false) {
            return new Array(padsNeeded).join(hairSpace) + currentText;
        } else {
            return new Array(parseInt(padsNeeded / 2)).join(hairSpace) + currentText;
        }
    }
}
 |