| Config/script.js | "use strict";
this.name = "combat_MFD";
this.author = "Norby, Zireael";
this.copyright = "2014 Norby, Zireael";
this.description = "Makes MFDs more useful by displaying some info";
this.licence = "CC BY-NC-SA 4.0";
this.version = "1.17";
this.$DW = 28; //default width of centered textfields in setCustomHUDDial
this.$ExactNumbers = false; //energy and shield values for debug
this.$combat_MFD = null; //store the final displayed string with player and target data
this.$Data = {}; //storage of calculated values for external usage
this.$DataArray = [ //data_source key names for drawCustomText and drawCustomBar HUD dials
    "combatAftSh",    //aft shield numeric value (including capacitors)
    "combatAftShBar",    //aft shield bar including capacitors
    "combatAftShP",    //aft shield percent value (over 100% if upgraded)
    "combatAlt",    //altitude over the nearest planet, moon or sun in km
    "combatAltR",    //altitude over a planet, moon or sun in radius
    "combatCargoBar",    //how filled the cargo bay of player ship
    "combatCredits",    //credits of player
    "combatCTemp",    //cabin temp
    "combatDmgEq",    //name of the lastly damaged equipment
    "combatDEqNum",    //number of the damaged equipments
    "combatEnergy",    //energy of player ship in numeric form
    "combatEnergyP",    //energy of player ship in percent
    "combatFwSh",    //forward shield numeric value (including capacitors)
    "combatFwShBar",    //forward shield bar including capacitors
    "combatFwShP",    //forward shield percent value (over 100% if upgraded)
    "combatFuel",    //remaining fuel in the main tank in ly (min. 0.0, max. 7.0)
    "combatFuelReq",    //required fuel of a declared hyperjump
    "combatFuelRes",    //reserve fuel in extra tanks (integer)
    "combatLegal",    //legal status of player (word)
    "combatLegalNum",    //legal status of player (number)
    "combatNewTelT",    //name of the newest target detected by Telescope
    "combatOneShot",    //letters of One-shot indicator (BCEGHPRU)
    "combatSCTDist",    //distance of space compass target
    "combatSEE",    //letters of SEE indicator about the player ship (CMBSAHE)
    "combatSEEBar",    //total durability bar of player ship based on the SEE indicator
    "combatSEEMax",    //maximum number of ship durability: max of shield+armour+energy
    "combatSEENum",    //number of ship durability: shield+armour+energy
    "combatSLBar",    //service level bar of player ship
    "combatSpeed",    //actual speed of player ship in m/s
    "combatTDist",    //distance of the current target
    "combatTDmg",    //damaged/weak/derelict status of the current target
    "combatTEnergy",    //energy of the current target, use for debug only
    "combatTLaser",    //laser weapon of the current target
    "combatTLegal",    //legal status of the current target, if derelict then Towbar status
    "combatTLRange",    //range of the target's laser weapon
    "combatTMissile",    //missile type fired by the target (Hard, etc.)
    "combatTMissNum",    //number of missiles fired by the target so far
    "combatTName",    //full name of the current target
    "combatTRadius",    //shield radius of the current target
    "combatTSpeed"    //speed of the current target
];
this.$DataShow = {}; //allow or deny display of the mentioned values in CustomHUDDial
this.$DmgEqs = []; //store damaged eq keys
this.$InfoTimer = null; //store pointer to timer
this.$InfoTimer2 = null; //store pointer to timer2
this.$TelescopeLine = ""; //the 5. line in this MFD is filled by Telescope_1.9.oxz
this.$WasOk = false; //store the previous state of Equipment
this.$lastTarget = null;
this._altimeterDecimals = 3;
this.startUp = function () {
    var h = worldScripts.hudselector;
    if (h && h.$HUDSelectorAddMFD) h.$HUDSelectorAddMFD(this.name);
    for (var i = 0; i < this.$DataArray.length; i++) {
        this.$Data[this.$DataArray[i]] = 0; //fill up Data object with key names
        this.$DataShow[this.$DataArray[i]] = true; //allow display all data by default
    }
    if (player.ship && player.ship.isValid)
        this.$PlayerPrevPos = player.ship.position;
    else this.$PlayerPrevPos = [0, 0, 0];
    this.$Target = null;
    this.$TargetPrevPos = [0, 0, 0];
    this.$Aws = worldScripts.andromeda;
    this.$Cws = worldScripts["Automatic ECM System"];
    this.$Dws = worldScripts.duplex_fuel_tank;
    this.$Ews = worldScripts["extra_tanks_script.js"];
    this.$Hws = worldScripts.hardships;
    this.$Iws = worldScripts["IronHide Armour Script"];
    this.$Sca = worldScripts.ShipConfiguration_Armour;
    this.$Ows = worldScripts.towbar;
    this.$Rws = worldScripts.torustosun;
    this.$Sws = worldScripts.shieldequalizercapacitors;
    this.$Scs = worldScripts["Shield Cycler"];
    this.$Tws = worldScripts.telescope;
    this.$Uws = worldScripts["Turret Toggler"];
    this.$InfoTimer = null; //store pointer to timer
    this.$InfoTimer2 = null; //store pointer to timer2
    this.$DmgEqs = []; //store damaged eq keys
    var eq = player.ship.equipment;
    var i = -1;
    while (eq[++i]) { //skip noname eqs awarded by NumericHUD
        if (eq[i].name.length > 0
            && player.ship.equipmentStatus(eq[i].equipmentKey) == "EQUIPMENT_DAMAGED")
            this.$DmgEqs.push(eq[i].equipmentKey);
    }
}
this.equipmentDamaged = function (equipment) {
    this.$DmgEqs.push(equipment);
    //    log("combat_MFD", "DmgEqs: "+this.$DmgEqs); //debug
}
this.shipAttackedWithMissile = function (missile, whom) {
    if (whom && whom.script) {
        if (whom.script.$combat_MFD_missiles && whom.script.$combat_MFD_missiles > 0)
            whom.script.$combat_MFD_missiles++;
        else whom.script.$combat_MFD_missiles = 1;
        if (missile.dataKey == "missile")
            whom.script.$combat_MFD_normalmissile = true;
        else if (missile.dataKey == "ecm-proof-missile")
            whom.script.$combat_MFD_hardmissile = true;
    }
}
this.shipBeingAttacked = this.shipBeingAttackedUnsuccessfully = function (whom) {
    if (whom && whom.script) whom.script.$combat_MFD_attacked = true; //flag for display Laser type
}
this.shipTargetAcquired = function (target) {
    this.$showCombatInfo(); //update MFD
}
this.shipTargetLost = function (losttarget) {
    this.$showCombatInfo(); //update MFD
}
this.shipWillDockWithStation = function () {
    this.$TelescopeLine = "";
    if (this.$InfoTimer) {
        this.$InfoTimer.stop();
        delete this.$InfoTimer;
    }
    var p = player.ship;
    if (p.setCustomHUDDial) //need Oolite v1.81
        for (var key in this.$Data) p.setCustomHUDDial(key, ""); //clear custom dials
}
this.shipWillEnterWitchspace = function () {
    this.$TelescopeLine = "";
}
this.shipWillLaunchFromStation = function () {
    //the timer interval is bound to the multipliers in speed calculations so must change together
    if (!this.$InfoTimer) {
        this.$InfoTimer = new Timer(this, this.$showCombatInfo.bind(this), 0, 0.25);
        if (this.$Sws) { //turn of irritating messages and sounds which is unneccesary with this MFD
            this.$Sws.togglemessages = "OFF";
            this.$Sws.togglesoundfx2 = "OFF";
        }
    }
    //log("CombatMFD",player.ship.equipmentStatus("EQ_COMBATMFD")+" "+this.$InfoTimer);//debug
    this.$InfoTimer2Done = false;
    if (this.$ExactNumbers) {//debug
        var s = system.addShips("pirate", 1,
            system.mainStation.position.add(player.ship.heading.multiply(3000)), 100);//debug target
        if (s && s[0]) {
            //s[0].forwardWeapon = null;//setAI("nullAI.plist");
            s[0].bounty = 1;
            s[0].target = player.ship;
            //s[0].displayName = s[0].name + " Target";
        }
    }
}
this.$showCombatInfo = function $showCombatInfo () {
    var that = $showCombatInfo;
    var _p = (that._p = that._p || player.ship);
    var _decimals = (that._decimals = that._decimals || this._altimeterDecimals);
    //var p = player.ship;
    //    log("combat_MFD", "EQ_COMBATMFD: "+p.equipmentStatus("EQ_COMBATMFD")); //debug
    if (!_p || !_p.isValid) return;
    if (this.$WasOk && _p.equipmentStatus("EQ_COMBATMFD") == "EQUIPMENT_DAMAGED") {
        this.$WasOk = false;
        _p.removeEquipment("EQ_DTADER"); //remove text legends in NumericHUD
        _p.removeEquipment("EQ_DTADAM");
        _p.removeEquipment("EQ_DTAWEA");
        _p.setMultiFunctionText(this.name, "Combat MFD is damaged", false);
    }
    var pp = _p.position;
    var target = "\n\n\n\n"; //gather data of player's current target, skip the lines if no target
    var ceq = false;
    //always clear combatT dials for sure (no eq, no target or not applicable due to other reason)
    this.$setData("combatTDist", "");    //distance of the current target
    this.$setData("combatTDmg", "");    //damaged/weak/derelict status of the current target
    this.$setData("combatTEnergy", "");    //energy of the current target, use for debug only
    this.$setData("combatTLaser", "");    //laser weapon of the current target
    this.$setData("combatTLegal", "");    //legal status of the current target, if derelict then Towbar status
    this.$setData("combatTLRange", "");    //range of the target's laser weapon
    this.$setData("combatTMissile", "");    //missile type fired by the target (Hard, etc.)
    this.$setData("combatTMissNum", "");    //number of missiles fired by the target so far
    this.$setData("combatTName", "");    //full name of the current target
    this.$setData("combatTRadius", "");    //shield radius of the current target
    this.$setData("combatTSpeed", "");    //speed of the current target
    if (_p.equipmentStatus("EQ_COMBATMFD") == "EQUIPMENT_OK") {
        var ceq = true;
        this.$WasOk = true;
        var t = _p.target;
        if (t != this.$lastTarget) {
            _p.removeEquipment("EQ_DTADER"); //remove text legends in NumericHUD
            _p.removeEquipment("EQ_DTADAM");
            _p.removeEquipment("EQ_DTAWEA");
            this.$lastTarget = t;
        }
        if (t && t.isValid) {
            var tdn = t.displayName;//must read name before handle telescopemarker
            var st = "";
            if (t.isWormhole) {
                tdn = "Wormhole";
                if (_p.equipmentStatus("EQ_WORMHOLE_SCANNER") == "EQUIPMENT_OK") {
                    if (this.$InfoTimer2Done && this.$LastWormhole == t) {
                        //log("CombatMFD",t.expiryTime+" "+clock.seconds+" "+t.arrivalTime);//debug
                        if (t.destination) tdn += " to " + System.systemNameForID(t.destination);
                        if (t.expiryTime) tdn = this.$AlignedText(tdn, "expire in " +
                            Math.round(t.expiryTime - clock.seconds) + " s");
                        if (t.arrivalTime) {
                            st = "Travel Time: " + (Math.round(
                                (t.arrivalTime - clock.seconds) / 360) / 10) + " h";
                            this.$setData("combatTLegal", st);
                        }
                    } else { //5s delay before show Wormhole info
                        tdn += " scanning...";
                        if (this.$LastWormhole != t) {
                            this.$LastWormhole = t;
                            this.$InfoTimer2Done = false;
                            if (this.$InfoTimer2) {
                                this.$InfoTimer2.stop();
                                delete this.$InfoTimer2;
                            }
                            this.$InfoTimer2 = new Timer(this, this.$showWormholeInfo.bind(this), 6);
                        }
                    }
                }
            }
            //telescope far target over scanner range
            var t2 = null;
            if (this.$Tws && t.dataKey == "telescopemarker") {//change the pointer to the real target
                t2 = this.$Tws.$TelescopeList[this.$Tws.$TelescopeListi - 1];
                //this.$Tws.$TelescopeTarget is not good: not refreshed when
                //the target is changed automatically in weapons offline mode
                if (t2 && t2.isValid) t = t2;
                var k = tdn.indexOf("km "); //chop the x km from the begin of marker name
                if (k > -1 && k < 10) tdn = tdn.substr(k + 3);
            }
            this.$setData("combatTName", tdn);
            //target speed
            var tsp = 0;
            var ts = "0";
            if (t.maxSpeed > 0) {
                if (t.speed > t.maxSpeed * t.injectorSpeedFactor || (t.speed == 0 && t.velocity.magnitude() != 0)) { // only do the calc if the speed value is greater than the maxspeed setting
                    if (this.$Target == t) { //need 2 pos. for speed, use velocity for Q-Charger
                        tsp = Math.round(t.velocity.magnitude() / 10) * 10; //stable but can not show FTL drive
                        if (tsp > 31 * t.maxSpeed) { //support for FarPlanets
                            var psp = Math.round(0.1 * t.position.distanceTo(this.$TargetPrevPos)) * 10;
                            if (psp > 1.1 * tsp) tsp = psp;//instable but need to follow position changes
                        }
                    } else {
                        this.$Target = t; //first time save position
                        tsp = Math.round(t.velocity.magnitude() / 10) * 10;
                    }
                    if (tsp > 10000000) tsp = Math.round(tsp / 1000000) + " M";
                    else if (tsp > 1000000) tsp = Math.round(tsp / 1000) + " k";
                    else tsp = Math.round(tsp) + " ";
                    ts = tsp;
                    tsp += "m/s";
                } else {
                    // otherwise, we can just rely on the standard speed value
                    ts = t.speed.toFixed(0) + " ";
                    tsp = ts + "m/s";
                }
                //tsp = "Speed: " + tsp;
            } else tsp = ""; //nothing for stationary objects, zero for others
            this.$setData("combatTSpeed", ts, this.$DW);
            this.$TargetPrevPos = t.position;
            //target distance
            var ra = Math.round(t.collisionRadius);
            var di = Math.max(0, Math.round(t.position.distanceTo(pp) - ra - _p.collisionRadius));
            if (di >= 1000000000) di = Math.floor(di / 1000000000) + "Mkm";
            else if (di >= 1000) di = Math.floor(di / 1000) + "km";
            else di += "m";
            this.$setData("combatTDist", di, this.$DW);
            if (ra > 100000) ra = Math.round(ra / 1000) + "km";
            else ra += "m";
            //target radius
            var sr = "";
            if (!t.isShip || t.isPlanet || t.isSun || t.isRock) {
                sr = "Radius: " + ra;
                this.$setData("combatTRadius", sr);
                //                this.$setData("combatTDmg", "");
            } else {    //facing shield
                if (!t2) { //within scanner range only
                    sr = "Shield Radius: ";
                    if (this.$AftShieldFacing(t)) sr = sr + ra + "    Aft";
                    else sr = sr + ra + "   Front";
                    this.$setData("combatTRadius", sr);
                }
                var d = false;
                if (t.isDerelict) {
                    d = "Derelict";
                    //                    log(this.name, "Awarding DTADER, target is derelict");
                    _p.awardEquipment("EQ_DTADER"); //show text legend in NumericHUD
                } else {
                    if (!t2 && t.energy && t.maxEnergy) {
                        if (t.energy < 0.2 * t.maxEnergy) {
                            d = "Weak!";
                            _p.awardEquipment("EQ_DTAWEA");
                        } else if (t.energy < 0.4 * t.maxEnergy) {
                            d = "Damaged!";
                            _p.awardEquipment("EQ_DTADAM");
                        }
                    }
                }
                if (d) {
                    sr = this.$AlignedText(sr, d);
                    this.$setData("combatTDmg", d);
                } //else this.$setData("combatTDmg", "");
            }
            //target laser
            var laser = "";
            if (t.script && t.script.$combat_MFD_attacked && t.currentWeapon) {
                var cw = t.currentWeapon;
                var wr = t.weaponRange;
                if (t.currentWeapon.equipmentKey == "EQ_WEAPON_NONE") {
                    // in the instance where weapons have been placed on subentities (rather than on the main entity)
                    // find the first subentity with a current weapon
                    if (t.subEntities && t.subEntities.length > 0) {
                        for (var si = 0; si < t.subEntities.length; si++) {
                            if (t.subEntities[si].currentWeapon && t.subEntities[si].currentWeapon.equipmentKey != "EQ_WEAPON_NONE") {
                                cw = t.subEntities[si].currentWeapon;
                                wr = t.subEntities[si].weaponRange;
                                break;
                            }
                        }
                    }
                }
                laser = "Laser: " + cw.name;
                this.$setData("combatTLaser", laser);
                var rng = "Range: " + Math.round(wr / 100) / 10 + "km";
                this.$setData("combatTLRange", rng);
                laser = this.$AlignedText(laser, rng);
            } else if (t.forwardWeapon) {
                laser = "Laser: Unknown";
                this.$setData("combatTLaser", laser);
                //                this.$setData("combatTLRange", "");
            }
            //target status
            if (!t2 && _p.equipmentStatus("EQ_SCANNER_SHOW_MISSILE_TARGET") == "EQUIPMENT_OK"
                && (!t.isWormhole && t.isShip && t.isPiloted || t.isDerelict)) {
                st = "Status:";
                if (t.isDerelict) {
                    if (this.$Ows && t.script) { //Towbar status
                        if (t.script.$TowbarUsableShip) st += " Usable!";
                        else if (t.script.$TowbarMinedShip) st += " Mined";
                        if (t.script.$TowbarEmptyShip) st += " Empty";
                        else st += " Trap or Treasure";
                    }
                } else if (t.bounty > 50) st += " Fugitive"; //legal status
                else if (t.bounty > 0) st += " Offender";
                else st += " Clean";
                this.$setData("combatTLegal", st);
                if (this.$ExactNumbers) st += " " + Math.round(t.energy);//for debug
                this.$setData("combatTEnergy", Math.round(t.energy), this.$DW);
                //                this.$setData("combatTMissNum", 2); this.$setData("combatTMissile", "Normal");//debug
                //Missiles fired
                if (t.script) {
                    var m = t.script.$combat_MFD_missiles;
                    if (m > 0) {
                        this.$setData("combatTMissNum", m);
                        var s = "";
                        if (m > 1) s = "s";
                        if (t.script.$combat_MFD_hardmissile) {
                            m += " Hard";
                            this.$setData("combatTMissile", "Hard");
                        } else if (t.script.$combat_MFD_normalmissile) {
                            this.$setData("combatTMissile", "Normal");
                        } else {
                            m += " Unknown type";
                            this.$setData("combatTMissile", "Unknown");
                        }
                        st = this.$AlignedText(st, " Missile" + s + " fired: " + m);
                    }
                }
            }
            //concatenate target info
            target = tdn + "\n" + this.$AlignedText("Distance: " + di + ", Mass: " + (t.mass / 1000).toFixed(1) + "t", tsp) +
                "\n" + sr + "\n" + laser + "\n" + st;
        }
        else {
            _p.removeEquipment("EQ_DTADER"); //remove text legends in NumericHUD
            _p.removeEquipment("EQ_DTADAM");
            _p.removeEquipment("EQ_DTAWEA");
            this.$Target = null;
        }
    }
    //player energy
    this.$setData("combatEnergy", Math.round(_p.energy), this.$DW);
    var ep = "Energy: " + 10 * Math.round(10 * _p.energy / _p.maxEnergy) + "%";
    this.$setData("combatEnergyP", ep, this.$DW);
    var eb = Math.floor(_p.maxEnergy / 64);//how many energy banks shown in hud
    var e = Math.ceil(_p.energy / (_p.maxEnergy / eb));
    var en = "";
    var seemax = _p.maxEnergy;
    var ihv = 0;
    if (this.$Iws) { //IronHide armour
        var ihs = missionVariables.ironHide_strength;
        seemax += ihs;
        var ihp = missionVariables.ironHide_percentage;
        if (ihs > 0 && ihp > 0) ihv = ihs * (ihp / 100);
        //        log("combat_MFD", "H: "+ihs+" "+ihp+" "+ihv); //debug
    }
    var asmax = _p.maxAftShield;
    var fsmax = _p.maxForwardShield;
    seemax += Math.max(asmax, fsmax);
    if (this.$Sws && //Shield capacitor
        (_p.equipmentStatus("EQ_FORWARD_SHIELD_CAPACITOR") != "EQUIPMENT_UNAVAILABLE"
            || _p.equipmentStatus("EQ_AFT_SHIELD_CAPACITOR") != "EQUIPMENT_UNAVAILABLE")) {
        var max = 64;
        if (this.$Sws.secforwardshieldcapacitormax) {
            max = Math.max(max, this.$Sws.secforwardshieldcapacitormax);
            fsmax += this.$Sws.secforwardshieldcapacitormax;
        } else if (_p.equipmentStatus("EQ_FORWARD_SHIELD_CAPACITOR") == "EQUIPMENT_OK")
            fsmax += 64;
        if (this.$Sws.secaftshieldcapacitormax) {
            max = Math.max(max, this.$Sws.secaftshieldcapacitormax);
            asmax += this.$Sws.secaftshieldcapacitormax;
        } else if (_p.equipmentStatus("EQ_AFT_SHIELD_CAPACITOR") == "EQUIPMENT_OK")
            asmax += 64;
        seemax += max;
        en += this.$SEELetters("C", max,
            this.$Sws.secforwardshieldcapacitor,
            this.$Sws.secaftshieldcapacitor,
            "EQ_FORWARD_SHIELD_CAPACITOR",
            "EQ_AFT_SHIELD_CAPACITOR",
            "EQ_BIGSHCAP", "EQ_BIGSHCAP2", "EQ_BIGSHCAP3");
    }
    if (this.$Hws) { //armours in HardShips OXP
        var hmax = Math.max(this.$Hws.$HardShips_AMax[0][0], //fw
            this.$Hws.$HardShips_AMax[0][1]); //aft
        seemax += hmax;
    }
    if (this.$Sca) { // armour in ShipConfig
        var scmax = Math.max(_p.script._frontArmourStrength, _p.script._aftArmourStrength);
        // lowest max is 100, highest is 400. convert of factors of 128
        scmax *= 1.28;
        seemax += scmax;
    }
    if (e < 2 && _p.maxEnergy > 96) { //not exactly below 64, for example in Fer-De-Lance below 75
        en = "LAST BANK!";
    } else { //"SEE" indicator: letters mean shields, armour and energy for each double banks
        if (_p.equipmentStatus("EQ_NAVAL_SHIELD_BOOSTER") != "EQUIPMENT_UNAVAILABLE")
            en += this.$SEELetters("M", 128,
                Math.max(0, _p.forwardShield - 256),
                Math.max(0, _p.aftShield - 256),
                "EQ_NAVAL_SHIELD_BOOSTER");
        if (_p.equipmentStatus("EQ_SHIELD_BOOSTER") != "EQUIPMENT_UNAVAILABLE")
            en += this.$SEELetters("B", 128,
                Math.min(128, Math.max(0, _p.forwardShield - 128)),
                Math.min(128, Math.max(0, _p.aftShield - 128)),
                "EQ_SHIELD_BOOSTER");
        en += this.$SEELetters("S", 128,
            Math.min(128, _p.forwardShield),
            Math.min(128, _p.aftShield),
            "EQ_BREAKABLE_SHIELD_FORE_SMALL",
            "EQ_BREAKABLE_SHIELD_AFT_SMALL",
            "EQ_BREAKABLE_SHIELD_FORE_MEDIUM",
            "EQ_BREAKABLE_SHIELD_AFT_MEDIUM",
            "EQ_BREAKABLE_SHIELD_FORE_LARGE",
            "EQ_BREAKABLE_SHIELD_AFT_LARGE");
        if (this.$Hws) { //armours in HardShip OXP
            en += this.$SEELetters("A", hmax,
                this.$Hws.$HardShips_Armour[0][0], //fw armour value
                this.$Hws.$HardShips_Armour[0][1]);//aft
            //IronHide handled by HardShips
            var ihs = this.$Hws.$HardShips_AMax[0][7];
            var ihv = this.$Hws.$HardShips_Armour[0][7];
        }
        if (ihv > 0) en += this.$SEELetters("H", ihs, ihv, ihv);
        if (this.$Sca) { // armour in ShipConfig
            en += this.$SEELetters("A", scmax, ((_p.script._armourFront / 100) * _p.script._frontArmourStrength) * 1.28, ((_p.script._armourAft / 100) * _p.script._aftArmourStrength) * 1.28);
        }
        en += this.$SEELetters("E", _p.maxEnergy, _p.energy, _p.energy,
            "EQ_BREAKABLE_ENERGY_UNIT_SMALL",
            "EQ_BREAKABLE_ENERGY_UNIT_MEDIUM",
            "EQ_BREAKABLE_ENERGY_UNIT_LARGE");
    }
    //"SEE" indicator is done at this point in en variable
    this.$setData("combatSEE", en);
    //count enery+armour+shield banks
    e = _p.energy + Math.min(_p.forwardShield, _p.aftShield);
    if (this.$Sws) e += Math.min(this.$Sws.secforwardshieldcapacitor,
        this.$Sws.secaftshieldcapacitor);
    if (this.$Hws) e += Math.min(this.$Hws.$HardShips_Armour[0][0], //fw
        Math.min(this.$Hws.$HardShips_Armour[0][1],//aft
            Math.min(this.$Hws.$HardShips_Armour[0][2],//port
                Math.min(this.$Hws.$HardShips_Armour[0][3],//sb
                    Math.min(this.$Hws.$HardShips_Armour[0][4],//top
                        this.$Hws.$HardShips_Armour[0][5])))));//bottom
    e += ihv; //ironhide value, calculated above
    en += " (" + Math.round(e / 64) + ")"; //in banks
    this.$setData("combatSEEMax", seemax, this.$DW);//maximum of exact SEE value
    this.$setData("combatSEENum", Math.round(e), this.$DW);//exact value
    this.$setData("combatSEEBar", e / seemax);//0-1
    //player shields
    var fs = Math.round(_p.forwardShield);
    if (this.$Sws) fs += Math.round(this.$Sws.secforwardshieldcapacitor);
    this.$setData("combatFwSh", fs, this.$DW);
    this.$setData("combatFwShBar", fs / fsmax);
    var as = Math.round(_p.aftShield);
    if (this.$Sws) as += Math.round(this.$Sws.secaftshieldcapacitor);
    this.$setData("combatAftSh", as, this.$DW);
    this.$setData("combatAftShBar", as / asmax);
    var fp = this.$ForwardPercent(fs);
    var ap = this.$AftPercent(as);
    var sh = "Shields: " + fp + "%/" + ap + "%";
    var ws_sc = this.$Scs;
    if (ws_sc && (ws_sc.hasOwnProperty("_sc_get_sc_versions") && (ws_sc._sc_get_sc_versions(null)).version > 0) && !ws_sc.$sc_disabled && ws_sc.$sc_settings.version > 0) 
        sh += " " + ws_sc.$sc_const.mode_names[ws_sc.$sc_settings.current_configuration];
    this.$setData("combatFwShP", fp + "%", this.$DW);
    this.$setData("combatAftShP", ap + "%", this.$DW);
    if (this.$ExactNumbers) { //for debug
        en += ": " + Math.round(_p.energy);
        sh += " (" + fs + "/" + as + ")";
    }
    //Auto ECM and Turret Toggler indicators
    var one = "";
    if (this.$Cws && missionVariables.autoECM == 1
        && _p.equipmentStatus("EQ_AUTOECM") == "EQUIPMENT_OK") one += " ECM ";
    if (this.$Uws && this.$Uws._tt_turret_status == "active"
        && _p.equipmentStatus("EQ_UNI_TURRET") == "EQUIPMENT_OK") one += " Turret ";
    if (one.length > 0) one = " " + one + " ";
    //"One-shot" indicator for Energy Bomb and one-shot equipments in HardShips OXP
    if (_p.equipmentStatus("EQ_ENERGY_BOMB") == "EQUIPMENT_OK") one += "B";
    if (this.$Hws) {
        if (_p.equipmentStatus("EQ_ADDITIONAL_CORE_HULL") == "EQUIPMENT_OK") one += "C";
        if (_p.equipmentStatus("EQ_EEG") == "EQUIPMENT_OK") one += "E";
    }
    if (_p.equipmentStatus("EQ_GAL_DRIVE") == "EQUIPMENT_OK") one += "G";
    if (this.$Hws && _p.equipmentStatus("EQ_HTCAT") == "EQUIPMENT_OK") one += "H";
    if (_p.equipmentStatus("EQ_ESCAPE_POD") == "EQUIPMENT_OK") one += "P";
    if (this.$Hws && _p.equipmentStatus("EQ_REG") == "EQUIPMENT_OK") one += "R";
    if (_p.equipmentStatus("EQ_EEU") == "EQUIPMENT_OK") one += "U";
    sh = this.$AlignedText(sh, one);
    this.$setData("combatOneShot", one);
    //player legal status
    var ps = "";
    var psw = "Clean";
    if (_p.bounty > 50) {
        psw = "Fugitive";
        ps += psw + " (" + _p.bounty + ")";
        if (_p.bounty < 100) ps += "  ";
    } else if (_p.bounty > 0) {
        psw = "Offender";
        ps += psw + " (" + _p.bounty + ") ";
        if (_p.bounty < 10) ps += "  ";
    } else ps += "Status: " + psw + " ";
    this.$setData("combatLegal", psw);
    var b = Math.round(_p.bounty);
    if (b == 0) b = "";
    this.$setData("combatLegalNum", b, this.$DW);
    //player fuel
    var extrafuel = "";
    var ef = 0;
    if (this.$Aws && _p.dataKey == "andromeda-player"
        && this.$Aws.$AndromedaFuelReserve > 0)
        ef += this.$Aws.$AndromedaFuelReserve;
    if (this.$Dws) { //DuplexFuelTank.oxz
        if (_p.equipmentStatus("EQ_DUPLEX_FUEL_TANK_STATE_1") == "EQUIPMENT_OK") ef += 1;
        else if (_p.equipmentStatus("EQ_DUPLEX_FUEL_TANK_STATE_2") == "EQUIPMENT_OK") ef += 2;
        else if (_p.equipmentStatus("EQ_DUPLEX_FUEL_TANK_STATE_3") == "EQUIPMENT_OK") ef += 3;
    }
    if (this.$Ews) { //ExtraFuelTanks-v1.4.1.oxz
        if (_p.equipmentStatus("EQ_RESERVE_TANK") == "EQUIPMENT_OK") ef += 1;
        else if (_p.equipmentStatus("EQ_AUX_TANK") == "EQUIPMENT_OK") ef += 3;
    }
    if (_p.equipmentStatus("EQ_FUELTANK_MINE") == "EQUIPMENT_OK") { //Fuel_Tank_2.2.oxz
        //        log(this.name, _p.missiles); //debug
        for (var i = 0; i < _p.missiles.length; i++) {
            if (_p.missiles[i].equipmentKey == "EQ_FUELTANK_MINE")
                ef += 3;
        }
    }
    if (ef > 0) extrafuel = "+" + Math.round(ef); //display total reserve fuel in addition
    var f = (_p.fuel).toFixed(1);
    this.$setData("combatFuel", f);
    this.$setData("combatFuelRes", extrafuel);
    ps += " Fuel: " + f + extrafuel + "ly";
    var fr = ""; //fuel required for hyperjump
    if (_p.targetSystem)
        fr = (System.infoForSystem(galaxyNumber, system.info.systemID)
            .distanceToSystem(System.infoForSystem(galaxyNumber, _p.targetSystem))
        ).toFixed(1);
    this.$setData("combatFuelReq", fr);
    //torus multiplier and player speed
    var tm = "";
    var sp = 0;
    if (_p.speed > _p.maxSpeed * _p.injectorSpeedFactor || _p.script._ficcEngaged) {
        sp = Math.round(_p.velocity.magnitude() / 10) * 10; //stable but can not show FTL drive nor Torus multiplier
        if (this.$Rws && this.$Rws.$TorusToSunBonus > 1) {
            tm = " " + Math.round(this.$Rws.$TorusToSunBonus) + "x";
            sp = Math.round(this.$Rws.$TorusToSunBonus * 3.2 * _p.maxSpeed) * 10;
            if (this.$Rws.$TimeFw > 1) { //time forwarding active
                tm += Math.round(this.$Rws.$TimeFw);
                sp *= this.$Rws.$TimeFw;
            }
        } else {
            if (sp > 31 * _p.maxSpeed) { //support for FarPlanets and any other position changer drives
                var psp = Math.round(0.1 * pp.distanceTo(this.$PlayerPrevPos)) * 10;
                if (psp > 1.1 * sp) sp = psp;//unstable but need to follow position changes
            }
        }
        if (sp > 10000000) sp = Math.round(sp / 1000000) + " M";
        else if (sp > 1000000) sp = Math.round(sp / 1000) + " k";
    } else {
        sp = _p.speed.toFixed(0);
    }
    this.$setData("combatSpeed", sp, this.$DW);
    if (tm.length > 0) sp += "m/s"; else sp += " m/s";
    this.$PlayerPrevPos = pp;
    //damaged equipment if any
    var dm = "";
    var dmg = "";
    var dmgeqs = [];
    //    log("combat_MFD", "DmgEqs: "+this.$DmgEqs); //debug
    //    _p.setEquipmentStatus("EQ_ECM","EQUIPMENT_DAMAGED");//debug
    for (var i = 0; i < this.$DmgEqs.length; i++) //remove fixed ones from the damaged list
        if (_p.equipmentStatus(this.$DmgEqs[i]) == "EQUIPMENT_DAMAGED")
            dmgeqs.push(this.$DmgEqs[i]);
    this.$DmgEqs = dmgeqs;
    //    log("combat_MFD", "DmgEqs: "+this.$DmgEqs); //debug
    var b = ""; //hide 0 length value when no damaged eq
    if (dmgeqs && dmgeqs.length > 0) {  //show the name of the lastly damaged equipment
        b = dmgeqs.length;
        dmg = EquipmentInfo.infoForKey(dmgeqs[dmgeqs.length - 1]).name;
        if (dmgeqs.length > 1) dm = "(+" + (dmgeqs.length - 1) + ")";//+how many other eq damaged
    }
    this.$setData("combatDmgEq", dmg);
    this.$setData("combatDEqNum", b, this.$DW);
    if (dmg.length > 0) dmg = this.$AlignedText("Damaged: " + dmg, dm);
    if (ceq) { //if CombatMFD equipment is ok then show the result in MFD
        this.$combat_MFD = this.$AlignedText(ep, en) + "\n" + //1.line
            sh + "\n" + //2.line
            this.$AlignedText(ps, sp + tm) + "\n" + //3.line
            dmg + "\n" + //4.line
            target + "\n" +  //5.-9.lines
            this.$TelescopeLine; //10.line
        _p.setMultiFunctionText(this.name, this.$combat_MFD, false);
    }
    //extra data sources (not in MFD)
    //altimeter
    var c = system.sun; //closest planetary object
    if (c) var d = _p.position.distanceTo(c.position);
    else var d = 100000000000000000000.0; //10^20m for sure
    var l = system.planets;
    var len = l.length;
    for (var i = 0; i < len; i++) { //find the smallest distance
        var s = pp.distanceTo(l[i].position) - l[i].radius;
        if (d > s) {
            d = s;
            c = l[i];
        }
    }
    var alt = ""; //clear if no planetary object
    var altr = "";
    if (c) {
        alt = pp.distanceTo(c) - c.radius - _p.collisionRadius;
        altr = Math.round(100 * alt / c.radius) / 100; //how many radius far
        if (alt >= 1000000000) alt = Math.floor(alt / 1000000000) + "Mkm";
        else if (alt >= 1000) alt = Math.floor(alt / 1000) + "km";
        else alt = alt.toFixed(_decimals) + "m";
    }
    this.$setData("combatAlt", alt, this.$DW);
    this.$setData("combatAltR", altr, this.$DW);
    //credits of player
    this.$setData("combatCredits", formatCredits(player.credits, true, true), this.$DW);
    //cabin temp
    var ct = Math.max(Math.round(100 * _p.temperature), 23); //prevent flickering below 23
    this.$setData("combatCTemp", ct);
    //the newest target detected by Telescope
    this.$setData("combatNewTelT", this.$TelescopeLine);
    //distance of space compass target
    var di = "";
    var c = _p.compassTarget;
    if (c && c.isValid && c.displayName != "Jump Marker") di = Math.max(0, Math.round(pp.distanceTo(c) - c.collisionRadius - _p.collisionRadius));
    if (di >= 1000000000) di = Math.floor(di / 1000000000) + "Mkm";
    else if (di >= 1000) di = Math.floor(di / 1000) + "km";
    else if (c && c.isValid && c.displayName != "Jump Marker") di += "m";
    this.$setData("combatSCTDist", di, this.$DW);
    //how filled the cargo bay of player ship
    this.$setData("combatCargoBar", _p.cargoSpaceUsed / _p.cargoSpaceCapacity);
    //service level bar of player ship
    this.$setData("combatSLBar", (_p.serviceLevel - 75) / 25);
}
this.$ForwardPercent = function (current) {
    var max = 128;//player.ship.maxForwardShield;
    var result = 10 * Math.round(10 * current / max);
    return result;
}
this.$AftPercent = function (current) {
    var max = 128;//player.ship.maxAftShield;
    var result = 10 * Math.round(10 * current / max);
    return result;
}
this.$showWormholeInfo = function () { //6s delay before show Wormhole info
    this.$InfoTimer2Done = true;
    if (this.$InfoTimer2) {
        this.$InfoTimer2.stop();
        delete this.$InfoTimer2;
    }
}
this.$AlignedText = function (left, right) { //insert as many spaces between as fill the line
    var width = 14.2;
    var space = " ";
    left += space;
    var t = left + right;
    while (defaultFont.measureString(t) < width) {
        left += space;
        t = left + right;
    }
    //fine tune with hair spaces - from spara's marketObserver
    var hairSpace = String.fromCharCode(31);
    while (defaultFont.measureString(t + hairSpace) < width) {
        left += hairSpace;
        t = left + right;
    }
    return (t);
}
this.$CenteredText = function (txt, width) { //insert spaces before text to align to center in width
    var t = "" + txt; //force to srting, else txt.length is undefined for numbers
    var w = width / 2;
    //    log("combat_MFD", "CT1 "+txt+" "+t.length+" "+w); //debug
    while (t && t.length < w) { //for fixed with font in setCustomHUDDial
        t = " " + t;
    }
    return (t);
}
this.$RightText = function (txt, width) { //insert spaces before text to align to right in width
    var t = "" + txt; //force to srting, else txt.length is undefined for numbers
    while (t && t.length < width) { //for fixed with font in setCustomHUDDial
        t = " " + t;
    }
    return (t);
}
this.$SEELetters = function (letter, max, fw, aft, eq1, eq2, eq3, eq4, eq5, eq6) {
    var s = 0; //smaller shield side
    var l = 0; //larger shield side
    var p = player.ship;
    if (eq1 && p.equipmentStatus(eq1) == "EQUIPMENT_DAMAGED" ||
        eq2 && p.equipmentStatus(eq2) == "EQUIPMENT_DAMAGED" ||
        eq3 && p.equipmentStatus(eq3) == "EQUIPMENT_DAMAGED" ||
        eq4 && p.equipmentStatus(eq4) == "EQUIPMENT_DAMAGED" ||
        eq5 && p.equipmentStatus(eq5) == "EQUIPMENT_DAMAGED" ||
        eq6 && p.equipmentStatus(eq6) == "EQUIPMENT_DAMAGED")
        letter = "_"; //damaged
    s = Math.round(Math.min(fw, aft) / 128);
    l = Math.round(Math.max(fw, aft) / 128);
    var r = "";
    var t = Math.round(max / 128); //total letters
    for (var i = 0; i < t; i++)
        if (letter == "_") r += letter; //damaged equipment
        else if (i < t - l) r += "-"; //empty bank
        else if (i < t - s) r += letter.toLowerCase(); //one side of shield is up only
        else r += letter; //both forward and aft shield is up
        //log("combat_MFD", letter+": "+max+" "+fw+" "+aft+" "+i+". "+(t-s)+" "+(t-l)+" "+t+" "+r); //debug
    return (r);
}
this.$AftShieldFacing = function (ship) { //determine which shield of the target facing to player ship
    if (ship.vectorForward.angleTo(ship.position.subtract(player.ship.position)) <= Math.PI / 2)
        return (true);
    return (false);
}
this.$combatKeyIsExists = function (key) {
    if (this.$DataArray.indexOf(key) > -1) return true;
    else return false;
}
this.$disableKey = function (combatKey) {
    this.$DataShow[combatKey] = false;
}
this.$enableKey = function (combatKey) {
    this.$DataShow[combatKey] = true;
}
this.$setData = function (key, v, align) { //align=0:left, >0:center in this width, <0:right
    this.$Data[key] = v; //save for external access
    var p = player.ship;
    if (p.setCustomHUDDial && this.$DataShow[key]) {
        if (align > 0) v = this.$CenteredText(v, align); //centered in an align wide box
        else if (align < 0) v = this.$RightText(v, -align); //right in a -align wide box
        p.setCustomHUDDial(key, v);
    }
}
 |