| Scripts/separatedlasers.js | "use strict";
this.name	= "separatedlasers";
this.author	= "Norby";
this.copyright	= "2016 Norby";
this.description= "Lasers will not heat up faster if there is at least 30m space between multiple mounts";
this.licence	= "CC BY-NC-SA 4.0";
//customizable properties
this.$Log = false; //log separated laser distances of ships when spawned
//worldscript events
this.startUpComplete = this.playerBoughtNewShip = function() {
        this._Replace_Lasers(player.ship, this, false); //check and replace lasers to separated if possible
}
this.shipSpawned = function(ship) {
        this._Replace_Lasers(ship, this, false); //check and replace NPC lasers to separated if possible
}
this.playerBoughtEquipment = function( equipment ) {
        if( equipment.indexOf("EQ_WEAPON_") > -1 ) { //core lasers, laser cannons and other lasers in the future
                //check and replace new laser to separated
                //and replace back to basic laser if bought to not separated side
                this._Replace_Lasers(player.ship, this, true);
        }
}
//internal methods
this._Replace_Lasers = function(ship, w, refund) {  //check and replace lasers to separated if possible
    if( !ship || !ship.isValid ) return 0;
    if( !w ) var w = worldScripts.separatedlasers;
    var a = w._Separated_Lasers(ship, w);
    if( a ) {
        if( w.$Log ) log(w.name, ship.name+" Separated laser distances: fw:"+a[1]
                        +" aft:"+a[2]+" port:"+a[3]+" sb:"+a[4]);
//      log(w.name, equipmentKey+" "+datakey+" "+ship.forwardWeapon+" "
//              +ship.aftWeapon+" "+ship.portWeapon+" "+ship.starboardWeapon); //debug
        var eq, eq1, len, m, multi = false, p = player.ship;
        if( !ship.script ) ship.setScript("oolite-default-ship-script.js"); //for sure
        var m = ship.script._cachedMultipleLasers; //read cached variable
        if( !m ) {
                var shipdata = Ship.shipDataForKey(ship.dataKey);
                if( shipdata && shipdata["weapon_mount_mode"] == "multiply" ) m = 1;
                else m = -1; //not multiple
                ship.script._cachedMultipleLasers = m; //store into a cache variable
        }
        if( m == 1 ) multi = true; //a must for separation
        if( ship.scriptInfo ) {
                var si = parseInt( ship.scriptInfo.separated_lasers );
                //separation is denied if shipdata contain script_info={separatedlasers=-1;};
                //separation need an empty side mount if script_info contain separatedlasers=1;
                if( si == -1 || si == 1 && ship.portWeapon && ship.starboardWeapon
                         //and need both side mounts stay without lasers if separatedlasers=2;
                        || si == 2 && ( ship.portWeapon || ship.starboardWeapon ) ) {
                        multi = false; //deny separation
                }
        }
        //replace back to single laser if bought a separated one to not separated side
        //then replace single lasers to separated versions where there is enough space and money
        //use the laser match to the number of mounts but max.5
        //Dual, Triple, Quad or Broad Separated Pulse, Beam or Military Lasers,
        //Sniper Guns and some Laser Cannons
        if( ship.forwardWeapon ) {
            eq = ship.forwardWeapon.equipmentKey;
            if( multi ) len = ship.weaponPositionForward.length; else len = 1;
            //replace back to basic laser if bought to not separated side
            m = w._getMultiLevel(eq);
            if( m > 1 && ( Math.min(5, len) != m || !a[1] || a[1] < w._space(eq) ) ) {
                eq1 = eq.substr(0, eq.length-2);
                //replace to single weapon without _2, _3, _4 or _5 at the end
                if( !EquipmentInfo.infoForKey( eq1 ) ) eq1 = eq; //for sure
                if( refund && ship == p ) w._refund( eq, eq1, len, true ); //no refund during flight
                ship.forwardWeapon = eq1;
                eq = eq1;
                m = 1;
            }
            //replace single lasers to separated versions where there is enough space and money
            if( m < 2 && len > 1 && a[1] && a[1] >= w._space(eq) ) {
                eq1 = eq+"_"+Math.min(5, len); //max 5 (broadside)
                if( EquipmentInfo.infoForKey( eq1 ) ) { //not all weapons are separable
                    if( refund && ship == p ) { 
                        //no refund for NPCs and during flight (Sapper->Sniper Gun)
                        if( w._refund( eq, eq1, len, true ) )
                            ship.forwardWeapon = eq1;
                    } else ship.forwardWeapon = eq1;
                }
            }
        }
        if( ship.aftWeapon ) {
            eq = ship.aftWeapon.equipmentKey;
            if( multi ) len = ship.weaponPositionAft.length; else len = 1;
            //replace back to basic laser if bought to not separated side
            m = w._getMultiLevel(eq);
            if( m > 1 && ( Math.min(5, len) != m || !a[2] || a[2] < w._space(eq) ) ) {
                eq1 = eq.substr(0, eq.length-2);
                //replace to single weapon without _2, _3, _4 or _5 at the end
                if( !EquipmentInfo.infoForKey( eq1 ) ) eq1 = eq; //for sure
                if( refund && ship == p ) w._refund( eq, eq1, len, true ); //no refund during flight
                ship.aftWeapon = eq1;
                eq = eq1;
                m = 1;
            }
            //replace single lasers to separated versions where there is enough space and money
            if( m < 2 && len > 1 && a[2] && a[2] >= w._space(eq) ) {
                eq1 = eq+"_"+Math.min(5, len); //max 5 (broadside)
                if( EquipmentInfo.infoForKey( eq1 ) ) { //not all weapons are separable
                    if( refund && ship == p ) {
                        //no refund for NPCs and during flight (Sapper->Sniper Gun)
                        if( w._refund( eq, eq1, len, true ) )
                            ship.aftWeapon = eq1;
                    } else ship.aftWeapon = eq1;
                }
            }
        }
        if( ship.portWeapon ) {
            eq = ship.portWeapon.equipmentKey;
            if( multi ) len = ship.weaponPositionPort.length; else len = 1;
            //replace back to basic laser if bought to not separated side
            m = w._getMultiLevel(eq);
            if( m > 1 && ( Math.min(5, len) != m || !a[3] || a[3] < w._space(eq) ) ) {
                eq1 = eq.substr(0, eq.length-2);
                //replace to single weapon without _2, _3, _4 or _5 at the end
                if( !EquipmentInfo.infoForKey( eq1 ) ) eq1 = eq; //for sure
                if( refund && ship == p ) w._refund( eq, eq1, len, true ); //no refund during flight
                ship.portWeapon = eq1;
                eq = eq1;
                m = 1;
            }
            //replace single lasers to separated versions where there is enough space and money
            if( m < 2 && len > 1 && a[3] && a[3] >= w._space(eq) ) {
                eq1 = eq+"_"+Math.min(5, len); //max 5 (broadside)
                if( EquipmentInfo.infoForKey( eq1 ) ) { //not all weapons are separable
                    if( refund && ship == p ) {
                        //no refund for NPCs and during flight (Sapper->Sniper Gun)
                        if( w._refund( eq, eq1, len, true ) )
                            ship.portWeapon = eq1;
                    } else ship.portWeapon = eq1;
                }
            }
        }
        if( ship.starboardWeapon ) {
            eq = ship.starboardWeapon.equipmentKey;
            if( multi ) len = ship.weaponPositionStarboard.length; else len = 1;
            //replace back to basic laser if bought to not separated side
            m = w._getMultiLevel(eq);
            if( m > 1 && ( Math.min(5, len) != m || !a[4] || a[4] < w._space(eq) ) ) {
                eq1 = eq.substr(0, eq.length-2);
                //replace to single weapon without _2, _3, _4 or _5 at the end
                if( !EquipmentInfo.infoForKey( eq1 ) ) eq1 = eq; //for sure
                if( refund && ship == p ) w._refund( eq, eq1, len, true ); //no refund during flight
                ship.starboardWeapon = eq1;
                eq = eq1;
                m = 1;
            }
            //replace single lasers to separated versions where there is enough space and money
            if( m < 2 && len > 1 && a[4] && a[4] >= w._space(eq) ) {
                eq1 = eq+"_"+Math.min(5, len); //max 5 (broadside)
                if( EquipmentInfo.infoForKey( eq1 ) ) { //not all weapons are separable
                    if( refund && ship == p ) {
                        //no refund for NPCs and during flight (Sapper->Sniper Gun)
                        if( w._refund( eq, eq1, len, true ) )
                            ship.starboardWeapon = eq1;
                    } else ship.starboardWeapon = eq1;
                }
            }
        }
    }
}
this._getMultiLevel = function(equipment) { //get the multiplication level of a given weapon
        var m = 0;
        var end = equipment.substr(-2, 2); //the last two characters of equipmentKey
        if( end == "_2" && equipment != "EQ_WEAPON_CANNON_2" //except single Ingram 3838C8 Mini Cannon
            && equipment != "EQ_WEAPON_GATLING_LASER_2" ) m = 2; //and Gatling 2
        if( end == "_3" && equipment != "EQ_WEAPON_CANNON_3" //except single Hassoni C38 Dual Cannon
            && equipment != "EQ_WEAPON_GATLING_LASER_3" ) m = 3; //and Gatling 3
        if( end == "_4" && equipment != "EQ_WEAPON_CANNON_4" //except single Lance & Ferman Military Cannon
            && equipment != "EQ_WEAPON_GATLING_LASER_4" ) m = 4; //and Gatling 4
        if( end == "_5" && equipment != "EQ_WEAPON_CANNON_5" //except single LF38 Twin Military Cannon
            && equipment != "EQ_WEAPON_GATLING_LASER_5" ) m = 5; //and Gatling 5
        return( m );
}
this._Separated_Lasers = function(ship, w) {  
        //return false if no separated mount, else return array of sides
        if( !w ) var w = worldScripts.separatedlasers;
        var a = [];
        a[1] = w._is_Separated_Mount(ship, 1, w); //forward
        a[2] = w._is_Separated_Mount(ship, 2, w); //aft
            a[3] = w._is_Separated_Mount(ship, 3, w); //port
        a[4] = w._is_Separated_Mount(ship, 4, w); //starboard
//      log(w.name, ship.name+" Laser distances: fw:"+a[1]+" aft:"+a[2]+" port:"+a[3]+" sb:"+a[4]);
        if( !a[1] && !a[2] && !a[3] && !a[4] ) return false;
        else return a; //array of min. distances at sides where lasers are separated, false at other sides
}
this._is_Separated_Mount = function(ship, side, w) { //return false if this side is not separated
        if( !w ) var w = worldScripts.separatedlasers;
        var d = w._mount_Distance(ship, side, w);
        if( d < 30 ) return false;
        else return d; //return min. distance if more than the minimally allowed for separated lasers
}
this._mount_Distance = function(ship, side, w) { //return 0 if only one mount, min. dist. if more.
        if( !w ) var w = worldScripts.separatedlasers;
        side = Math.floor(side);
        var array;
        if( !side || side <= 1 || side > 4 ) array = ship.weaponPositionForward;
        else if( side == 2 ) array = ship.weaponPositionAft;
        else if( side == 3 ) array = ship.weaponPositionPort;
        else array = ship.weaponPositionStarboard;
        var min = w._min_Distance(array);
        return min;
}
this._min_Distance = function(array) { //calculate min. dist. in the given array of Vector3Ds
        var min = 0;
        if( array && array.length > 1 ) for(var i = 0; i < array.length; i++) {
                for(var j = i + 1; j < array.length; j++) { //check against all remaining items
                        var d = 0;
                        if( array[i].distanceTo && array[j].distanceTo )
                                d = array[i].distanceTo(array[j]);
                        if( d > 0 && min == 0 ) min = d;
                        else if( min > 0 && d < min ) min = d;
//                      log(this.name, "d:"+d+"min:"+min+" a["+i+"]: "+array[i]+" a["+j+"]:"+array[j]);
                }
        }
//      log(this.name, " array: "+array+" min.d:"+min);
        return min;
}
this._refund = function(eq, eq1, length, msg) { 
        //refund weapon price, length is the size of multiple weapons array
        var f = 1;
        if( player.ship && player.ship.dockedStation ) {
                var e = player.ship.dockedStation.equipmentPriceFactor;
                if( e > 0 ) f = e;
        }
        if( 0 < oolite.compareVersion("1.83") ) length = 1; //no multiple weapons before Oolite 1.83
        var refund = f * length * (EquipmentInfo.infoForKey( eq ).price
                - EquipmentInfo.infoForKey( eq1 ).price)/10;
        if( refund < 0 && player.credits < -1 * refund ) {
                var m = EquipmentInfo.infoForKey( eq ).name 
                        + " is not the best in this mount, but you have not "
                        + (-1*refund) + " credits to buy " 
                        + EquipmentInfo.infoForKey( eq1 ).name+".";
                if(msg) player.consoleMessage(m, 10);
                log(this.name, "Replace "+eq+" to "+eq1+" skipped, need "
                        +refund+"cr but you have "+player.credits+"cr only.");
                return( false );
        }
        player.credits += refund;
        var r = ", "+refund+" credits refunded.";
        if( refund < 0 ) r = " for " + (-1*refund) + " credits.";
        var m = EquipmentInfo.infoForKey( eq ).name 
                + " is not the best in this mount, replaced to "
                + EquipmentInfo.infoForKey( eq1 ).name + r;
        if(msg) player.consoleMessage(m, 10);
        log(this.name, "Replace "+eq+" to "+eq1+", refund: "+refund+" cr");
        return( true );
}
this._space = function(eq) { //how much space need between weapon positions to be separated
        if( eq.indexOf("EQ_WEAPON_CANNON_5") > -1 //Twin Military Cannon
                || eq.indexOf("EQ_WEAPON_CANNON_10") > -1 //Plasma Ray
                || eq.indexOf("EQ_WEAPON_CANNON_12") > -1 //Twin Artillery Cannon
                || eq.indexOf("EQ_WEAPON_CANNON_EA") > -1 //Eco Artillery
                || eq.indexOf("EQ_WEAPON_ECO_REACTOR") > -1 //Eco Reactor
                || eq.indexOf("EQ_WEAPON_CANNON_SNIPER_GUN") > -1 ) //Sniper Gun
                return(50); //need 50m
        if( eq.indexOf("EQ_WEAPON_CANNON_HEAVY_SNIPER_GUN") > -1 ) //Heavy Sniper Gun
                return(100); //need 100m
        return(30); //all others need 30m
}
 |