Scripts/escortdeck.js |
"use strict";
this.name = "escortdeck";
this.version = "1.12"
this.author = "Norby";
this.copyright = "2015 Norbert Nagy";
this.description = "Buy and hold escort ships on external deck";
this.licence = "CC BY-NC-SA 4.0";
this.$debug = false; // debug verbose log
this.$logging = true; // verbose log
this.$Derelicts = false; //add derelict ships near the nav buoy of the starting station
this.$EscortDeckAutoLaunch = false; // automatically launch/callback when leaving/entering green condition
this.$EscortDeckBigTraderLaunch = false; //launch Anaconda from Carriers automatically or not
this.$EscortDeckCMaxMass = 800000; //escorts must below this mass on XL deck of Carriers
this.$EscortDeckExtremeMass = 10*this.$EscortDeckCMaxMass; //so much so don't display message
this.$EscortDeckJumpDist = 50; //escorts jump this m farther from the main ship at launch
this.$EscortDeckMassEffect = true; //slower steering and acceleration with more mass on deck
this.$EscortDeckMassFactor = 1; // fraction of steering maneuverability to retain
this.$EscortDeckMinMass = 30000; //which ships can tow the deck (30t not small in ShipVersion)
this.$EscortDeckLandingDist = 1500; //escorts will be added to the deck within this m if possible
this.$EscortDeckSpinModel = true;//spinning model in the interface screen
//this.$EscortDeckLargeMass = 130000; //max. escort mass, border of large ships in ShipVersion OXP
//this.$EscortDeckWMass = 2 * this.$EscortDeckLargeMass; //which ships can tow wide deck (2*large)
this.$EscortDeckLargeMass = 205000; //max. escort mass, border of large ships in ShipVersion OXP
this.$EscortDeckWMass = 1.5 * this.$EscortDeckLargeMass; //which ships can tow wide deck (2*large)
this.$EscortDeckXLMass = 400000; //which can tow XL deck (400t mean xl ships in Telescope OXP)
this.$EscortDeckBasicUnusableProb = 0.9; //probability of damage (0-1) to base systems (not equipment) of a derelict, which would render it unusable
//internal properties, should not touch
this.$EscortDeckEqs = []; //buyable escort ship sizes and masses
this.$EscortDeckEqs["EQ_ESCORTDECK_adder"] = [28.02, 14.9, 51, 16000];
this.$EscortDeckEqs["EQ_ESCORTDECK_asp"] = [64.73, 23.52, 83.69, 75882];
this.$EscortDeckEqs["EQ_ESCORTDECK_gecko"] = [61.48, 14.64, 51, 24670];
this.$EscortDeckEqs["EQ_ESCORTDECK_sidewinder"] = [63.23, 14.43, 44, 25088];
this.$EscortDeckEqs["EQ_ESCORTDECK_viper"] = [50, 18.12, 65.199, 34000];
this.$EscortDeckPad = { //landing pad max. x,y,z sizes and max. mass
escort:[70,29,135,this.$EscortDeckLargeMass], //for Asp SG, Arachnid, S8
// escort:[70,45,135,this.$EscortDeckLargeMass], //for Asp SG, Arachnid, S8
mini:[39,15,65,30000], //Adder, Ophidian
wide:[95,29,135,this.$EscortDeckLargeMass], //wide ship: Krait SG, Tiger Ray
// wide:[95,45,135,this.$EscortDeckLargeMass], //wide ship: Krait SG, Tiger Ray
xl:[70,29,95,60000], //sizes like heavy escorts but smaller mass, no Asp
// xl:[70,45,95,60000], //sizes like heavy escorts but smaller mass, no Asp
//Carrier specific pads
center:[65,29,95,130000], //small center pad for Asp, Fer-De-Lance, S8
main:[95,64,170,this.$EscortDeckCMaxMass], //Anaconda, D.T.T. Atlas, Krait SG
side:[70,29,95,this.$EscortDeckLargeMass], //heavy escorts: Asp, Arachnid, S8
small:[65,19,66,40000], //aft side pad for Gecko, Ophidian, Sonoran, Viper
top:[140,35,135,this.$EscortDeckCMaxMass] } //Cobra Mark III, King Cobra, Krait SG
this.$EscortDeckLPos = [[-36,6,-25], [36,6,-25], //landing pad positions: top left-right
[-36,-6,-25], [36,-6,-25], //bottom left-right
[-43,0,-25], [43,0,-25]]; //center left-right (Adder only)
// [34,0,-43], [-34,0,-43], //aft left, aft right (facing backward) - disabled
this.$EscortDeckLVec = [[0,1,0], [0,1,0], //vectors from landing pad to ship center: up
[0,-1,0], [0,-1,0], //down
[-1,0,0], [1,0,0]]; //left, right
// [0,0,-1], [0,0,-1], //back - disabled
this.$EscortDeckWPos = [[-48,6,-25], [48,6,-25], //Wide deck landing pad positions: top left-right
[-48,-6,-25], [48,-6,-25], //bottom left-right
[-54,0,-25], [54,0,-25]]; //center left-right (Adder only)
// [34,0,-43], [-34,0,-43], //aft left, aft right (facing backward)
this.$EscortDeckWVec = [[0,1,0], [0,1,0], //vectors from landing pad to ship center: up
[0,-1,0], [0,-1,0], //down
[-1,0,0], [1,0,0]]; //left, right
// [0,0,-1], [0,0,-1], //back
this.$EscortDeckXLPos = [[-8,0,-15], [8,0,-15], //XL deck landing pad positions: left, right
[-8,0,-44], [8,0,-44], //left, right
[-8,0,-73], [8,0,-73], //left, right
[-8,0,-102], [8,0,-102]]; //left, right
this.$EscortDeckXLVec = [[-1,0,0], [1,0,0], //XL deck vectors from landing pad to ship center
[-1,0,0], [1,0,0], //left, right
[-1,0,0], [1,0,0], //left, right
[-1,0,0], [1,0,0]]; //left, right
this.$EscortDeckCPos = [[0,15,215], [0,15,135], //Carrier pad positions: top front and center
[0,15,15], [-67,16,15], [67,16,15], //aft center, left, right
[-52,15,46], [52,15,46], //main deck left, right
[-30,0,233], [30,0,233]]; //mini front deck left, right
this.$EscortDeckCSize = [this.$EscortDeckPad.top, //Carrier max. sizes and mass, top front pad
this.$EscortDeckPad.center, //center: Asp, Fer-De-Lance, S8
this.$EscortDeckPad.top, //top aft for Cobra Mark III and King Cobra
this.$EscortDeckPad.small, this.$EscortDeckPad.small, //aft sides
this.$EscortDeckPad.main, this.$EscortDeckPad.main, //left-right main deck
this.$EscortDeckPad.mini, this.$EscortDeckPad.mini]; //front pads
this.$EscortDeckCVec = [[0,1,0], [0,1,0], //Carrier vectors from landing pad to ship center: up
[0,1,0], //up
[0,1,0], [0,1,0], //up
[0,0,1], [0,0,1], //forward
[0,0,0], [0,0,0]]; //center
this.$EscortDeckCXPos = [[0,15,215], [0,15,115], //Carrier XL deck positions: top front, center
[-48,16,15], [48,16,15], //aft left, right
[-11,0,151], [11,0,151], //left, right
[-11,0,81], [11,0,81], //left, right
[-30,0,233], [30,0,233]]; //front left, right
this.$EscortDeckCXSize = [this.$EscortDeckPad.top, this.$EscortDeckPad.top, //top front and center
this.$EscortDeckPad.wide, this.$EscortDeckPad.wide, //top aft wide pads
this.$EscortDeckPad.side, this.$EscortDeckPad.side, //4 heavy escorts
this.$EscortDeckPad.side, this.$EscortDeckPad.side, //on main decks
this.$EscortDeckPad.mini, this.$EscortDeckPad.mini]; //front pads
this.$EscortDeckCXVec = [[0,1,0], [0,1,0], //Carrier XL deck vectors from landing pad to ship center: up
[0,1,0], [0,1,0], //up
[-1,0,0], [1,0,0], //left, right
[-1,0,0], [1,0,0], //left, right
[0,0,0], [0,0,0]]; //center
/* Carrier deck positions for 10 heavy escorts, removed to be less powerful
this.$EscortDeckCXXPos = [[0,14,220], [0,14,23], //landing pad positions: top front-aft
[-11,0,170], [11,0,170], //left, right
[-11,0,135], [11,0,135], //left, right
[-11,0,100], [11,0,100], //left, right
[-11,0,65], [11,0,65]]; //left, right
this.$EscortDeckCXXSize = [this.$EscortDeckPad.top, this.$EscortDeckPad.top, //top front and aft
this.$EscortDeckPad.escort, this.$EscortDeckPad.escort, //8 heavy escorts
this.$EscortDeckPad.escort, this.$EscortDeckPad.escort, //on main decks
this.$EscortDeckPad.escort, this.$EscortDeckPad.escort,
this.$EscortDeckPad.escort, this.$EscortDeckPad.escort];
this.$EscortDeckCXXVec = [[0,1,0], [0,1,0], //vectors from landing pad to ship center: up
[-1,0,0], [1,0,0], //left, right
[-1,0,0], [1,0,0], //left, right
[-1,0,0], [1,0,0], //left, right
[-1,0,0], [1,0,0]]; //left, right
*/
this.$EscortDeckVersion = null; //version of EscortDeck installed on the ship
this.$EscortDeckCWS = null; //carriers worldscript if Carriers OXP is installed
this.$EscortDeckDockingTunnel = false; //player in the tunnel
this.$EscortDeckEWS = null; //escortdeck_escort worldscript
this.$EscortDeckFCB = null; //FrameCallBack updating escort ship positions
this.$EscortDeckFCBDelta = 0; //delta counter in FrameCallBack
this.$EscortDeckFirstMSO = true; //first missionScreenOpportunity after docked
this.$EscortDeckGWS = null; //Gallery worldscript
this.$EscortDeckI = false; //start on the first page within EscortDeck Interface
this.$EscortDeckLaunch = false; //launch or call back via primeable equipment
this.$EscortDeckLastTooLargeTarget = null; //prevent repeated too large messages
this.$EscortDeckLocked = []; //which positions will not launch with the other escorts
this.$EscortDeckMaxs = null; //store maxThrust, maxPitch, maxRoll and maxYaw
this.$EscortDeckNPCEqDamage = false; // NPC Equipment Damage OPC is installed
this.$EscortDeckPadPos = []; //landing pad positions
this.$EscortDeckPadSize = []; //landing pad sizes
this.$EscortDeckPadVec = []; //vectors from landing pad to ship center
this.$EscortDeckPlayableDataKeys = null; //playable ships
this.$EscortDeckPWS = null; //escortpack worldscript if escortpack OXP is installed
this.$EscortDeckSelectedPad = -1; //the pad selected via primeable equipment
this.$EscortDeckShip = []; //store the ship objects of escorts
this.$EscortDeckShipData = []; //store the data of escort ships
this.$EscortDeckShipPos = []; //positions of escort ships on board
this.$EscortDeckSitInto = false; //sit into the selected escort when launch it from a Carrier
this.$EscortDeckSitIntoWithMode = false; //allow sit into with mode key when deck is primed
this.$EscortDeckSound = null; //sound of escort landing
this.$EscortDeckSpawnedShips = []; //stack for restoring ship data in FCB after shipSpawned
this.$EscortDeckSGWS = null; //snipergun worldscript if Sniper Gun OXP is installed
this.$EscortDeckTWS = null; //telecope worldscript if telecope OXP is installed
this.$EscortDeckToWS = null; //towbar worldscript if towbar OXP is installed
this.$EscortDeckV = null; //visualeffect of the escort deck of the player ship
this.$EscortDeckXL = false; //the current deck is xl or not
this.$EscortDeckZV = 0; //the z offset of deck position from the center of the player ship
// configuration settings for use in Lib_Config
this.$newUsableProb = 1 - this.$EscortDeckBasicUnusableProb;
this.$newAutoLaunch = false;
this._compConfig = {
Name:this.name, Alias:"EscortDeck", Display:"Config", Alive:"_compConfig", Notify:"$onChange", Reset:true,
Bool: {
B0: {Name:"$newAutoLaunch", Def:true, Desc:"Automatic Launch"},
Info:"Automatic launch/callback on Red condition transition (0:no, 1:yes)"
},
SInt: {
S0:{Name:"$newUsableProb", Min:0, Max:1, Float:true, Def:0.1, Desc:"Usable derelict probability"},
Info:"0 - Value between 0 (all unusable) and 1 (all usable)"
},
};
//
//equipment events
//
//-----------------------------------------------------------------------------------------------//
this.activated = function _escortdeck_activated() { //launch or call back one or all escorts
var w = worldScripts.escortdeck;
w.$EscortDeck_EQActivated(w);
}
//-----------------------------------------------------------------------------------------------//
this.mode = function _escortdeck_mode() { //select the next pad on escort deck
var w = worldScripts.escortdeck;
w.$EscortDeck_EQMode(w);
}
//
//worldscript events
//
//-----------------------------------------------------------------------------------------------//
this.startUp = function _escortdeck_startUp() {
var hh = worldScripts.HyperspaceHangar;
if (!player.ship.addCollisionException) { //before v1.81 need more room
for (var i = 0; i < this.$EscortDeckLPos.length; i++)
this.$EscortDeckLPos[i][2] -= 30;
for (var i = 0; i < this.$EscortDeckWPos.length; i++)
this.$EscortDeckWPos[i][2] -= 30;
for (var i = 0; i < this.$EscortDeckXLPos.length; i++)
this.$EscortDeckXLPos[i][2] -= 20;
}
this.$EscortDeck_SetPads(player.ship, this);
this.$EscortDeckSound = new SoundSource;
this.$EscortDeckSound.sound = "escortdeck.ogg";
this.$EscortDeckSound.loop = false;
this.$EscortDeckSound.repeatCount = 1;
this.$EscortDeckSound2 = new SoundSource;
if (worldScripts["BGS-M"]) this.$EscortDeckSound2.sound = "bgs-c_sell.ogg";
else this.$EscortDeckSound2.sound = "sell.ogg"; //Oolite default sell sound
this.$EscortDeckSound2.loop = false;
this.$EscortDeckSound2.repeatCount = 1;
this.$EscortDeckSound3 = new SoundSource;
this.$EscortDeckSound3.sound = "escortdeckhitderelict.ogg";
this.$EscortDeckSound3.loop = false;
this.$EscortDeckSound3.repeatCount = 1;
this.$EscortDeckPlayableDataKeys = Ship.keysForRole( "player" ); //playable ships
if (worldScripts.escortdeck_escort) this.$EscortDeckEWS = worldScripts.escortdeck_escort;
if (worldScripts.carriers) this.$EscortDeckCWS = worldScripts.carriers;
if (worldScripts.gallery) this.$EscortDeckGWS = worldScripts.gallery;
if (worldScripts.escortpack) this.$EscortDeckPWS = worldScripts.escortpack;
if (worldScripts.snipergun) this.$EscortDeckSGWS = worldScripts.snipergun;
if (worldScripts.telescope) this.$EscortDeckTWS = worldScripts.telescope;
if (worldScripts.NPC_Equipment_Damage) this.$EscortDeckNPCEqDamage = true;
var h = worldScripts.hudselector;
if( h && h.$HUDSelectorAddMFD ) h.$HUDSelectorAddMFD(this.name);
var s = missionVariables.$EscortDeckShipData;
if( s && s.length > 0 ) this.$EscortDeckShipData = JSON.parse(s);
var s = missionVariables.$EscortDeckLocked;
if( s && s.length > 0 ) this.$EscortDeckLocked = JSON.parse(s);
var t = worldScripts.towbar;
if (t) {
this.$EscortDeckToWS = t; //save Towbar worldscript
//inject code to do not lock on derelicts of escort deck
eval("t.$EscortDeckToWS_IsDerelictOrig = "+t.$Towbar_IsDerelict);
eval("t.$Towbar_IsDerelict = function(entity) { \
var w = worldScripts.escortdeck; \
if ((!w.$EscortDeckXL || \
w.$EscortDeckCWS && player.ship && player.ship.isValid \
&& player.ship.dataKey.indexOf('carrier') > -1 ) && \
worldScripts.towbar.$EscortDeckToWS_IsDerelictOrig(entity) \
&& ( !w.$EscortDeckShip \
|| w.$EscortDeckShip.indexOf(entity) == -1 )) \
return true; \
return false; \
}");
eval("t.$EscortDeckToWS_shipCollidedOrig = "+t.shipCollided);
eval("t.shipCollided = function(otherShip) { \
var w = worldScripts.escortdeck; \
if( (!w.$EscortDeckXL || \
w.$EscortDeckCWS && player.ship && player.ship.isValid && \
player.ship.dataKey.indexOf('carrier') > -1 ) && \
!w.$EscortDeckShip || w.$EscortDeckShip.indexOf(otherShip) == -1 ) \
worldScripts.towbar.$EscortDeckToWS_shipCollidedOrig(otherShip); \
}");
if (hh) {
//makes Towbar not salvage usable derelicts
eval("t.$EscortDeckToWS_missionScreenOpportunityOrig = "+t.missionScreenOpportunity);
eval("t.missionScreenOpportunity = function _Towbar_missionScreenOpportunity_escortdeck() { \
var w = worldScripts.towbar; \
if (w.$TowbarShip && w.$TowbarShip.script && w.$TowbarShip.script.$EscortDeckUsable != 1) \
w.$EscortDeckToWS_missionScreenOpportunityOrig(); \
}");
//makes Towbar's Tug Drone not salvage usable derelicts
eval("t.$Towbar_TugDrone_ProcessingOrig = "+t.$Towbar_TugDrone_Processing);
eval("t.$Towbar_TugDrone_Processing = "+worldScripts.escortdeck.$EscortDeck_TugDrone_Processing)
}
}
}
//-----------------------------------------------------------------------------------------------//
this.startUpComplete = function _escortdeck_startUpComplete() {
// register our settings, if Lib_Config is present
if (worldScripts.Lib_Config) worldScripts.Lib_Config._registerSet(this._compConfig);
if (missionVariables.$EscortDeck_unusable_derelic_prob) {
this.$EscortDeckBasicUnusableProb = parseFloat(missionVariables.$EscortDeck_unusable_derelic_prob)
} else {
if (worldScripts.NPC_Equipment_Damage) this.$EscortDeckBasicUnusableProb -= 0.1
}
this.$newUsableProb = 1 - this.$EscortDeckBasicUnusableProb;
log(this.name, "Base unusable derelict probability: "+this.$EscortDeckBasicUnusableProb)
this.$EscortDeck_SaveMaxs( player.ship, this );
//call after towbar and ccl startups finished
this.$EscortDeck_SalvageBadEscorts(player.ship);
this.$EscortDeckI = false; //start on the first page
this.$EscortDeck_Interface();
this.$EscortDeckVersion = this.$EscortDeck_Version();
}
//-----------------------------------------------------------------------------------------------//
this.alertConditionChanged = function _escortdeck_alertConditionChanged(newCondition, oldCondition) {
if (!this.$EscortDeckVersion) return;
if( !player.ship || !player.ship.isValid || !this.$EscortDeckAutoLaunch) return;
if( newCondition == 3 && player.alertHostiles ) {
this.$EscortDeck_ControlAll(true, false, this); //launch all escorts
} else if( newCondition == 1 ) { //call back escorts in green alert
this.$EscortDeck_ControlAll(false, false, this);
}
}
//-----------------------------------------------------------------------------------------------//
this.guiScreenChanged = function _escortdeck_guiScreenChanged(to, from) {
if( to == "GUI_SCREEN_INTERFACES" ) this.$EscortDeck_Interface(); //update displayed ship names
}
//-----------------------------------------------------------------------------------------------//
this.missionScreenOpportunity = function _escortdeck_missionScreenOpportunity() {
var that = _escortdeck_missionScreenOpportunity;
var hh = (that.hh = that.hh || worldScripts.HyperspaceHangar);
var t = this.$EscortDeckToWS; //use Towbar salvage if available
if (t && t.$TowbarShip && t.$TowbarShip.isValid
&& !t.$Towbar_IsHeavyShip(t.$TowbarShip)) { //avoid heavy ships in aegis with autodock
if (t.$TowbarShip.script && t.$TowbarShip.script.$EscortDeckUsable > 0 && hh) {
var displayName = t.$TowbarShip.displayName;
log("escortdeck", "Towed ship "+displayName+" is usable, storing in Hyperspace Hangar");
// towed ship is usable and HH is installed, transfer it to HH
this.$EscortDeck_HHStoreShip(t.$TowbarShip)
player.consoleMessage("Usable ship "+displayName+" stored into Hyperspace Hangar", 10);
player.commsMessage("Usable ship "+displayName+" stored into Hyperspace Hangar", 10);
t.$TowbarShip.remove(true);
t.$TowbarShip = null;
} else {
log("escortdeck", t.$TowbarShip.displayName+" is not usable, salvaging it!");
t.$Towbar_Payout(t.$TowbarShip); //call payout in towbar oxp
}
}
if( player.ship.docked && this.$EscortDeckFirstMSO
&& this.$EscortDeckShip && this.$EscortDeckShip.length > 0 ) {
this.$EscortDeckFirstMSO = false; //do only once
for(var i = 0; i < this.$EscortDeckPadPos.length; i++ ) { //salvage unusables
var s = this.$EscortDeckShip[i]; //the ship on this pad
if( s && s.isValid ) {
if( !s.script || !( s.script.$EscortDeckUsable > 0 ) ) { //unusable
this.$EscortDeck_Salvage(i, player.ship); //salvage
if(this.$EscortDeckToWS) {
this.$EscortDeckFirstMSO = true; //do one at a time
i = this.$EscortDeckPadPos.length;
}
}
} else { //inside stations check saved ship data
var d = this.$EscortDeckShipData[i];
if( d && d[1] && ( !d[14] || !(d[14].usable > 0) ) ) {//unusable
this.$EscortDeck_Salvage(i, player.ship);
if(this.$EscortDeckToWS) {
this.$EscortDeckFirstMSO = true; //do one at a time
i = this.$EscortDeckPadPos.length;
}
}
}
}
}
if( t && t.$TowbarRunScreens.length > 0 ) {
var rs = t.$TowbarRunScreens.shift(); //get and remove the oldest element
if(this.$debug) log(this.name, "MSO "+t.$TowbarRunScreens.length+" "+guiScreen+" "+rs );
t.$Towbar_RunScreen(rs.place, rs.msg, rs.model); //show sell salvage screen
}
}
//-----------------------------------------------------------------------------------------------//
this.playerBoughtEquipment = this.equipmentAdded = function _escortdeck_playerBoughtEquipment(equipment) {
var p = player.ship;
var w = this; //w is used below
if( equipment.indexOf("EQ_ESCORTDECK_" ) == 0 ) {
p.removeEquipment(equipment);
var k = equipment.substring(14); //extract datakey from the end of the eq key
var s = this.$EscortDeck_SpawnOne( "[escortdeck-"+k+"]", this, false );
if(this.$debug) log(this.name, k+" "+s); //debug
if( !s || !s[0] || !s[0].isValid ) { //possible bug
this.$EscortDeck_RollBack(equipment, "You can't buy "+k+".");
return;
} else s = s[0];
var c = EquipmentInfo.infoForKey(equipment).price/10; //escort price
if( p && p.dockedStation ) { //Rock Hermits ask more
var e = p.dockedStation.equipmentPriceFactor;
if( e > 0 ) c = e * c;
}
var en = EquipmentInfo.infoForKey(equipment).name; //escort name
w.$EscortDeck_AwardEQs(s);
var d = this.$EscortDeck_MakeShipData(s);
if(d[14]) d[14].usable = 1; //usable
var added = false;
var reason = this.$EscortDeck_TooLarge(s, p, this);
if( !reason && !this.$EscortDeckXL && this.$EscortDeck_isLarge(p, this)
&& this.$EscortDeck_isMini(s) ) { //Adder or smaller, try side decks first
for( var i = 0; !added && i < w.$EscortDeckPadPos.length; i++ ) {
if( w.$EscortDeck_isMiniPad(i, p, w) ) {
if( !this.$EscortDeckShipData[i] ) {
this.$EscortDeckShipData[i] = d;
added = i+1; //=pad+1
}
}
}
}
var len = this.$EscortDeckPadPos.length;
if( !reason && !added ) for(var i = 0; i < len; i++ ) {
if( !this.$EscortDeckShipData[i]
&& !w.$EscortDeck_TooLarge2(s, p, w, i) ) {
this.$EscortDeckShipData[i] = d;
added = i+1;
i=10;
}
}
s.remove(true);
if( added ) player.consoleMessage("You paid "+c+" credits for "+en+" placed to landing pad "+added, 10);
else {
if(!reason) reason = "no room on your deck";
this.$EscortDeck_RollBack( equipment, "You can't buy "+en+", "+reason );
}
}
if( equipment == "EQ_ESCORTDECK" ) {
this.$EscortDeckXL = false;
if( w && w.$EscortDeckCWS && p.dataKey.indexOf("carrier") > -1
&& p.equipmentStatus("EQ_ESCORTDECKXL") == "EQUIPMENT_OK" ) {
//salvage pad 8, exchange pad 2-3 and shift pad 9-10 to 8-9
w.$EscortDeck_Salvage(7, p); //salvage a ship to reduce from 10 to 9
var d = [], s = [];
d[0] = w.$EscortDeckShipData[0]; s[0] = w.$EscortDeckShip[0];
d[1] = w.$EscortDeckShipData[2]; s[1] = w.$EscortDeckShip[2];
d[2] = w.$EscortDeckShipData[1]; s[2] = w.$EscortDeckShip[1];
d[3] = w.$EscortDeckShipData[3]; s[3] = w.$EscortDeckShip[3];
d[4] = w.$EscortDeckShipData[4]; s[4] = w.$EscortDeckShip[4];
d[5] = w.$EscortDeckShipData[5]; s[5] = w.$EscortDeckShip[5];
d[6] = w.$EscortDeckShipData[6]; s[6] = w.$EscortDeckShip[6];
d[7] = w.$EscortDeckShipData[8]; s[7] = w.$EscortDeckShip[8];
d[8] = w.$EscortDeckShipData[9]; s[8] = w.$EscortDeckShip[9];
w.$EscortDeckShipData = d;
w.$EscortDeckShip = s;
}
p.removeEquipment("EQ_ESCORTDECKXL"); //replace
this.$EscortDeck_SalvageBadEscorts(p);
}
if( equipment == "EQ_ESCORTDECKXL" ) {
this.$EscortDeckXL = true;
if( w && w.$EscortDeckCWS && p.dataKey.indexOf("carrier") > -1
&& p.equipmentStatus("EQ_ESCORTDECK") == "EQUIPMENT_OK" ) {
//exchange pad 2-3 and shift pad 8-9 to 9-10
var d = [], s = [];
d[0] = w.$EscortDeckShipData[0]; s[0] = w.$EscortDeckShip[0];
d[1] = w.$EscortDeckShipData[2]; s[1] = w.$EscortDeckShip[2];
d[2] = w.$EscortDeckShipData[1]; s[2] = w.$EscortDeckShip[1];
d[3] = w.$EscortDeckShipData[3]; s[3] = w.$EscortDeckShip[3];
d[4] = w.$EscortDeckShipData[4]; s[4] = w.$EscortDeckShip[4];
d[5] = w.$EscortDeckShipData[5]; s[5] = w.$EscortDeckShip[5];
d[6] = w.$EscortDeckShipData[6]; s[6] = w.$EscortDeckShip[6];
d[7] = null; s[7] = null;
d[8] = w.$EscortDeckShipData[7]; s[8] = w.$EscortDeckShip[7];
d[9] = w.$EscortDeckShipData[8]; s[9] = w.$EscortDeckShip[8];
w.$EscortDeckShipData = d;
w.$EscortDeckShip = s;
}
p.removeEquipment("EQ_ESCORTDECK"); //replace
this.$EscortDeck_SalvageBadEscorts(p);
}
if( equipment == "EQ_FUEL" ) {
var d = this.$EscortDeckShipData;
var f = 0;
var s = "";
var len = this.$EscortDeckPadPos.length;
if(d) for(var i = 0; i < len; i++ ) {
if( d[i] && d[i][9] < 7 ) {
if( f > 0 ) s = "s"; //more than one escort ship need refuel
f += 7 - d[i][9];
}
}
if( f > 0 ) {
var c = f * 3;//3cr/ly
if( player.credits > c ) {
for(var i = 0; i < len; i++ ) {
if( d[i] && d[i][9] < 7 ) d[i][9] = 7;
}
player.credits -= c;
player.consoleMessage("Your escort"+s+" got "+(Math.round(f*10)/10)+" ly fuel for "+(Math.round(c*10)/10)+" credits", 10);
}
}
}
}
//-----------------------------------------------------------------------------------------------//
this.playerBoughtNewShip = function _escortdeck_playerBoughtNewShip(ship) {
this.$EscortDeck_SaveMaxs( player.ship, this );
if( !this.$EscortDeckCWS || !this.$EscortDeckCWS.$CarriersPlayerInEscort ) {
this.$EscortDeck_SalvageBadEscorts(player.ship);
//setup new pads after SalvageBadEscorts only!
this.$EscortDeck_SetPads( player.ship, this );
}
}
//-----------------------------------------------------------------------------------------------//
this.playerStartedJumpCountdown = function _escortdeck_playerStartedJumpCountdown(type, seconds) {
if (!this.$EscortDeckVersion) return;
//fix for Granite ships to the too close high mass do not prevent the jump
if( !this.$EscortDeckCWS || player.ship.dataKey.indexOf("carrier") == -1 ) {
for( var i = 0; i < this.$EscortDeckPadPos.length; i++ )
if( this.$EscortDeckShipPos[i] )
this.$EscortDeckShipPos[i].z -= 40; //move backward on non-Carriers
} else { //on Carriers move escort up
for( var i = 0; i < this.$EscortDeckPadPos.length; i++ )
if( this.$EscortDeckShipPos[i] )
this.$EscortDeckShipPos[i].y += 100;
}
}
//-----------------------------------------------------------------------------------------------//
this.playerWillSaveGame = function _escortdeck_playerWillSaveGame(message) {
missionVariables.$EscortDeckShipData = JSON.stringify(this.$EscortDeckShipData);
missionVariables.$EscortDeckLocked = JSON.stringify(this.$EscortDeckLocked);
missionVariables.$EscortDeck_unusable_derelic_prob = this.$EscortDeckBasicUnusableProb
}
//-----------------------------------------------------------------------------------------------//
this.shipKilledOther = function _escortdeck_shipKilledOther(whom, damageType) {
var _npcShip = this.ship; // set if called from an escort's ship script, unidefined if called as a world script for the playership
var _ship = (_npcShip ? _npcShip : player.ship);
if (!_npcShip && !this.$EscortDeckVersion) return;
log("escortdeck", _ship.displayName+" killed "+whom.displayName+" with "+damageType);
if (_npcShip && this.$EscortDeckUsable === 1)
_ship.commsMessage("Mama, "+whom.displayName+" is no more!", player.ship);
}
//-----------------------------------------------------------------------------------------------//
this.shipAttackedOther = function _escortdeck_shipAttackedOther(other) { //player hits other
// this function is both a world script event handler for the playership and a ship event handler for the escorts
var that = _escortdeck_shipAttackedOther;
var wc = (that.wc = that.wc || worldScripts.escortdeck);
var tb = (that.tb = that.tb || worldScripts.towbar);
var weak_lasers = (that.weak_lasers = that.weak_lasers || ["EQ_WEAPON_EBEAM_LASER","EQ_WEAPON_BEAM_LASER"]);
var npcShip = this.ship; // set if called from an escort's ship script, unidefined if called as a world script for the playership
var _ship = (npcShip ? npcShip : player.ship);
var current_weapon = _ship.currentWeapon;
var weapon_info = current_weapon.weaponInfo;
var hit_energy = (weapon_info && weapon_info.damage ?
weapon_info.damage :
(weak_lasers.indexOf(current_weapon.equipmentKey) >= 0 ? 14 : 23)
);
var fname = "shipAttackedOther";
var debug = this.$debug;
var logging = this.$logging;
if (!npcShip && !this.$EscortDeckVersion) return;
if (!other || !other.isValid || other.isPlayer) return; //for sure
if (npcShip && logging) log("escortdeck", _ship.displayName+" attacked "+other.displayName+" with "+other.energy.toFixed(1)+" energy at "+_ship.position.distanceTo(other).toFixed(1));
if( !other.script ) other.script = "oolite-default-ship-script.js"; //for sure
if( other.script ) other.script.$EscortDeck_Target = 1; //flag for escort AI
if (!other.isThargoid && !other.isDerelict && other.energy > 0 && other.energy < 2 * hit_energy) {
//the next Military Laser hit can destroy the target
if (logging) log("escortdeck", _ship.displayName+": forcing target '"+other.displayName+"' to abandon ship");
other.fireMissile();//last resort
if( other.equipmentStatus("EQ_ESCAPE_POD") === "EQUIPMENT_OK"
&& other.abandonShip()) {//eject to make more derelict ship but only if has Escape Pod
var dn;
if( worldScripts["detectors"] ) {
dn = other.script.$Detectors_Origname;//short name with version
} else {
dn = other.displayName;
other.displayName = "Derelict " + dn;
}
if (!npcShip) {
player.consoleMessage(dn+" ejected!");
log("escortdeck", _ship.displayName+" turned "+other.displayName+" derelict");
if( other.script && !other.script.$AlreadyScored ) {
other.script.$AlreadyScored = true;
player.score++;
}
} else if (npcShip && this.$EscortDeckUsable === 1) {
_ship.commsMessage("Mama, look, I got "+other.displayName+" !", player.ship);
log("escortdeck", "Escort "+_ship.displayName+" turned "+other.displayName+" derelict");
}
} else log("escortdeck", _ship.displayName+" "+other.displayName+" has no functional escape pod");
}
if( other.isDerelict && !other.isThargoid ) {
if (!npcShip) this.$EscortDeckSound3.play(); //audio feedback if derelict
if( other.script ) {
if( !other.script.$TowbarDerelictAttack ) {
other.script.$TowbarDerelictAttack = 1;
} else {
if (++other.script.$TowbarDerelictAttack >= tb.$TowbarMaxReduct)
log("escortdeck", _ship.displayName+", Laser Reductor disabled for "+other.displayName+", used too many times:"+other.script.$TowbarDerelictAttack);
}
}
if (_ship.equipmentStatus("EQ_LASERREDUCTOR") === "EQUIPMENT_OK" && tb.$TowbarLaserReductorOn &&
(!other.script || other.script.$TowbarDerelictAttack <= tb.$TowbarMaxReduct) ) { //prevent invulnerability
// there are too many special cases for weapons and weapons positions (like shipdata's weapon_energy field)
// so let's err on the conservative side...
if (debug) log("escortdeck", _ship.displayName+" giving "+2*hit_energy+" back to derelict "+other.displayName+", "+other.script.$TowbarDerelictAttack+" attacks");
other.energy += 2 * hit_energy;
}
if (npcShip) return;
var reason = wc.$EscortDeck_TooLarge(_ship, other, this); //fit into the deck limits?
if (other.script && !(other.script.$EscortDeckUsable > 0) && !(other.script.$EscortDeckUsable < 0) ) {
if (wc.$isUsable(other, fname) ) { //fit into the deck limits and lucky
log(this.name, _ship.displayName+": "+fname+": '"+other.displayName+"' is usable");
other.script.$EscortDeckUsable = 1;
other.script.$TowbarUsableShip = true;
other.script.$TowbarShipHealth = 1;
wc.$EscortDeck_AwardEQs(other);
} else {
log(this.name, _ship.displayName+": "+fname+": '"+other.displayName+"' is NOT usable");
other.script.$EscortDeckUsable = -1; //unusable as escort
other.script.$TowbarUsableShip = false;
}
}
}
if (!npcShip) this.$EscortDeck_Bounty( other );
}
//-----------------------------------------------------------------------------------------------//
this.shipCollided = function _escortdeck_shipCollided(otherShip) {
var _fname = "shipCollided";
var p = player.ship;
if (!this.$EscortDeckVersion) return;
if( !p || !p.isValid ) return;
var w = this;
var i = w.$EscortDeckShip.indexOf(otherShip);
if( i > -1 ) {
if( w.$EscortDeckShipPos[i] ) { //move escort until avoid collision
if( !w.$EscortDeckCWS || p.dataKey.indexOf("carrier") == -1 ) {
w.$EscortDeckShipPos[i].z -= 2; //move backward on non-Carriers
} else { //on Carriers move escort up until avoid collision
w.$EscortDeckShipPos[i].y += 5;
// if( w.$EscortDeckXL ) {
// if( i < 4 ) w.$EscortDeckShipPos[i].y += 5; //top pads up
// else w.$EscortDeckShipPos[i].y -= 5; //others move down
// else if( Math.round(i/2) != i/2 )
// w.$EscortDeckShipPos[i].x += 5; //move right
// else w.$EscortDeckShipPos[i].x -= 5; //move left
// } else {
// if( i < 5 ) w.$EscortDeckShipPos[i].y += 5; //on top move up
// else w.$EscortDeckShipPos[i].y -= 5; //others move down
// }
}
}
if( otherShip.AIState == "LANDING" ) {
var r = w.$EscortDeck_PutShip(false, otherShip, i, p, true, true);
if( !r ) otherShip.AIState == "DECK"; //landed
else {
otherShip.AIState == "FOLLOW"; //no room?
if(w.$debug) log(w.name, "Can't add "+t.displayName +" to Escort Deck pad "+i+", "+r); //debug
}
}
} else {
if (this.$debug) log(this.name, _fname+": bumped '"+otherShip.dislayName+"'");
w.$EscortDeck_AddEscort( otherShip, w.$EscortDeckShip, p, w, true );
}
}
//-----------------------------------------------------------------------------------------------//
this.shipDied = function _escortdeck_shipDied() {
var p = player.ship;
//var e = system.addVisualEffect("escortdeckxl", p.position);e.orientation = p.orientation;
//this.$EscortDeckV.remove();
}
//-----------------------------------------------------------------------------------------------//
this.shipDockedWithStation = function _escortdeck_shipDockedWithStation(station) {
this.$EscortDeck_RestoreMaxs(player.ship, this);
this.$EscortDeckDockingTunnel = false;
this.$EscortDeckI = false; //start on the first page
this.$EscortDeck_Interface();
}
//-----------------------------------------------------------------------------------------------//
this.shipExitedWitchspace = function _escortdeck_shipExitedWitchspace() {
if (!this.$EscortDeckVersion) return;
this.$EscortDeck_Show(player.ship);
this.$EscortDeckDockingTunnel = false;
}
//-----------------------------------------------------------------------------------------------//
this.shipLaunchedFromStation = function _escortdeck_shipLaunchedFromStation() {
if (!this.$EscortDeckVersion) return;
this.$EscortDeck_SaveMaxs( player.ship, this );
this.$EscortDeckDockingTunnel = false;
if( this.$EscortDeckCWS && this.$EscortDeckCWS.$CarriersPlayerInEscort ) return;
if (this.$debug) {
log(this.name, "Launching, PadSize:"+JSON.stringify(this.$$EscortDeckPadSize));
this.$printDeckLoad();
}
this.$EscortDeck_Show(player.ship);
}
//-----------------------------------------------------------------------------------------------//
this.shipTargetAcquired = function _escortdeck_shipTargetAcquired(t) { //make some derelicts usable
var _fname = "shipTargetAcquired";
if( !t || !t.isValid ) return;
this.$EscortDeckLastTooLargeTarget = null;
var p = player.ship;
var pad = this.$EscortDeckShip.indexOf(t);
// if( pad > -1 ) this.$EscortDeckSelectedPad = pad; - do not use, cause disturbing changes in selected pad
if( !t.script ) t.script = "oolite-default-ship-script.js"; //for sure
if( t.script.$TowbarMinedShip ) t.script.$EscortDeckUsable = -1; //unusable as escort
if ( t.isDerelict && !(t.script.$EscortDeckUsable > 0) && !(t.script.$EscortDeckUsable < 0) ) {
//determine usability once only
if (this.$isUsable(t, _fname)) { //limits and lucky
log(this.name, "'"+t.displayName+"' is usable");
t.displayName = t.displayName.replace("Derelict", "Usable");
t.script.$EscortDeckUsable = 1;
t.script.$TowbarUsableShip = true;
t.script.$TowbarShipHealth = 1;
this.$EscortDeck_AwardEQs(t);
} else {
log(this.name, "'"+t.displayName+"' is NOT usable");
t.script.$EscortDeckUsable = -1; //unusable
t.script.$TowbarUsableShip = false;
if(this.$debug) log(this.name, t.displayName+" is set to unusable: "+t);
}
}
if( t.script && pad == -1 && ( t.isDerelict || t.script.$EscortDeckUsable > 0 )
&& (!this.$EscortDeck_isLarge(t, this) //exclude ships over 130t except for Carriers
|| this.$EscortDeckCWS && p.dataKey.indexOf("carrier") > -1
&& t.mass < this.$EscortDeckCMaxMass ) ) { //must below this mass on Carriers
var shipmass = t.mass/1000;
//remove extra density from HardShips and Granite ships
if( t.scriptInfo && t.scriptInfo.hardarmour > 1 )
shipmass = shipmass / 2;
var reason = this.$EscortDeck_TooLarge(t, p, this); //fit into the deck limits?
if( t.script.$EscortDeckUsable > 0 ) {
t.script.$TowbarUsableShip = true; //show Usable! flag in CombatMFD
var f = "Found an usable "+t.name+" ("+Math.ceil(shipmass)+"t) ";
var g = null;
if( p.equipmentStatus("EQ_ESCORTDECK") != "EQUIPMENT_OK"
&& p.equipmentStatus("EQ_ESCORTDECKXL") != "EQUIPMENT_OK" )
g = "You need an Escort Deck equipment to use as your escort";
else if( reason ) f = t.name+" is "+reason+" for your Escort Deck";
else if( t.position.distanceTo(p.position) > this.$EscortDeckLandingDist
+ t.collisionRadius )
g = "Go near to pick up to the Escort Deck";
if(this.$debug) log(this.name, f+" "+g); //debug
player.consoleMessage(f, 10);
if(g) player.consoleMessage(g, 10);
} else if( t.script.$EscortDeckUsable < 0 ) {
t.script.$TowbarUsableShip = false;
player.consoleMessage("Salvage "+t.displayName+" ("+Math.ceil(shipmass)+"t)", 4.5);
}
}
if( pad == -1 && t.script && t.script.$EscortDeckUsable > 0 )
p.awardEquipment("EQ_DTAUSA"); //show Usable label on NumericHUD
else p.removeEquipment("EQ_DTAUSA"); //remove Usable label on NumericHUD
}
//-----------------------------------------------------------------------------------------------//
this.shipTargetLost = function _escortdeck_shipTargetLost() {
if (!this.$EscortDeckVersion) return;
player.ship.removeEquipment("EQ_DTAUSA"); //remove Usable label on NumericHUD
}
//-----------------------------------------------------------------------------------------------//
this.shipWillDockWithStation = function _escortdeck_shipWillDockWithStation(station) {
if (!this.$EscortDeckVersion) return;
if (this.$debug) {
log(this.name, "Will Dock");
this.$printDeckLoad();
}
this.$EscortDeckDockingTunnel = true;
if( station.dataKey != "carriers-vdock"
&& station.dataKey.indexOf("carrier-player") == -1 )
this.$EscortDeck_VClear(this);
else this.$EscortDeckFirstMSO = false; //do not salvage in vdock
player.ship.removeEquipment("EQ_DTAUSA"); //remove Usable label on NumericHUD
}
//-----------------------------------------------------------------------------------------------//
this.shipWillEnterWitchspace = function _escortdeck_shipWillEnterWitchspace(cause) {
if (!this.$EscortDeckVersion) return;
this.$EscortDeckDockingTunnel = true;
this.$EscortDeck_VClear(this);
player.ship.removeEquipment("EQ_DTAUSA"); //remove Usable label on NumericHUD
}
//-----------------------------------------------------------------------------------------------//
this.shipWillLaunchFromStation = function _escortdeck_shipWillLaunchFromStation() {
this.$EscortDeckVersion = this.$EscortDeck_Version();
if (!this.$EscortDeckVersion) return;
this.$EscortDeckDockingTunnel = true;
// this.$EscortDeck_Show(player.ship); - lose escorts here, must call in shipLaunchedFromStation
var ps = player.ship; //show mfd as early as other mfds but avoid no escorts message
if( ps.setMultiFunctionText ) ps.setMultiFunctionText(this.name, "", false);
if( this.$EscortDeckCWS && this.$EscortDeckCWS.$CarriersPlayerInEscort )
return;
this.$EscortDeckFirstMSO = true;
if( this.$Derelicts && system && !system.isInterstellarSpace ) { //derelict targets
var m = system.mainStation;
var r = 300; //spread ship in this radius
var ships = system.addShips("escort", 16, //random escorts over the nav buoy
m.position.add(m.vectorForward.multiply( 11000 )), 3*r);
if( this.$EscortDeckSGWS ) {
var a = system.addShips("[asp-sg]", 2,
m.position.add(m.vectorForward.multiply( 1800 )), r);
ships.push(a[0]); ships.push(a[1]);
var a = system.addShips("[cobra3-sg]", 2,
m.position.add(m.vectorForward.multiply( 2100 )), r);
ships.push(a[0]); ships.push(a[1]);
var a = system.addShips("[ferdelance-sg]", 2,
m.position.add(m.vectorForward.multiply( 2400 )), r);
ships.push(a[0]); ships.push(a[1]);
var a = system.addShips("[krait-sg]", 2,
m.position.add(m.vectorForward.multiply( 2700 )), r);
ships.push(a[0]); ships.push(a[1]);
}
var a = system.addShips("[escortdeck-adder]", 4,
m.position.add(m.vectorForward.multiply( 3000 )), r);
ships.push(a[0]); ships.push(a[1]); ships.push(a[2]); ships.push(a[3]);
/* var a = system.addShips("[escortdeck-asp]", 4,
m.position.add(m.vectorForward.multiply( 3500 )), r);
ships.push(a[0]); ships.push(a[1]); ships.push(a[2]); ships.push(a[3]);
var a = system.addShips("[escortdeck-gecko]", 4,
m.position.add(m.vectorForward.multiply( 4000 )), r);
ships.push(a[0]); ships.push(a[1]); ships.push(a[2]); ships.push(a[3]);
var a = system.addShips("[escortdeck-sidewinder]", 4,
m.position.add(m.vectorForward.multiply( 4500 )), r);
ships.push(a[0]); ships.push(a[1]); ships.push(a[2]); ships.push(a[3]);
var a = system.addShips("[escortdeck-viper]", 4,
m.position.add(m.vectorForward.multiply( 5000 )), r);
ships.push(a[0]); ships.push(a[1]); ships.push(a[2]); ships.push(a[3]);
var a = system.addShips("[cobramk1]", 4,
m.position.add(m.vectorForward.multiply( 6000 )), r);
ships.push(a[0]); ships.push(a[1]); ships.push(a[2]); ships.push(a[3]);
var a = system.addShips("[ferdelance]", 4,
m.position.add(m.vectorForward.multiply( 7000 )), r);
ships.push(a[0]); ships.push(a[1]); ships.push(a[2]); ships.push(a[3]);
var a = system.addShips("[moray]", 4,
m.position.add(m.vectorForward.multiply( 8000 )), r);
ships.push(a[0]); ships.push(a[1]); ships.push(a[2]); ships.push(a[3]);
var a = system.addShips("[cobra3-player]", 4,
m.position.add(m.vectorForward.multiply( 6500 )), r);
ships.push(a[0]); ships.push(a[1]); ships.push(a[2]); ships.push(a[3]);
var a = system.addShips("[krait]", 4,
m.position.add(m.vectorForward.multiply( 7500 )), r);
ships.push(a[0]); ships.push(a[1]); ships.push(a[2]); ships.push(a[3]);
*/
if( this.$EscortDeckPWS ) {
var a = system.addShips("[escortdeck-ophidian]", 4,
m.position.add(m.vectorForward.multiply( 8000 )), r);
ships.push(a[0]); ships.push(a[1]); ships.push(a[2]); ships.push(a[3]);
/* var a = system.addShips("[escortdeck-ghavial]", 2,
m.position.add(m.vectorForward.multiply( 8000 )), r);
ships.push(a[0]); ships.push(a[1]);
var a = system.addShips("[escortdeck-wolf]", 2,
m.position.add(m.vectorForward.multiply( 8500 )), r);
ships.push(a[0]); ships.push(a[1]);
var a = system.addShips("[escortdeck-sniper-wolf]", 2,
m.position.add(m.vectorForward.multiply( 8500 )), r);
ships.push(a[0]); ships.push(a[1]);
*/ var a = system.addShips("[escortdeck-arachnid]", 2,
m.position.add(m.vectorForward.multiply( 9000 )), r);
ships.push(a[0]); ships.push(a[1]);
var a = system.addShips("[escortdeck-sniper-arachnid]", 2,
m.position.add(m.vectorForward.multiply( 9000 )), r);
ships.push(a[0]); ships.push(a[1]);
var a = system.addShips("[escortdeck-s8]", 2,
m.position.add(m.vectorForward.multiply( 9500 )), r);
ships.push(a[0]); ships.push(a[1]);
var a = system.addShips("[escortdeck-sniper-s8]", 2,
m.position.add(m.vectorForward.multiply( 9500 )), r);
ships.push(a[0]); ships.push(a[1]);
}
for(var i=0; i<ships.length; i++) {
if( ships[i] ) {
ships[i].orientation = ps.orientation;
ships[i].switchAI("nullAI.plist"); //need to deactivate escortdeck AI
if( worldScripts["shipversion"] ) {
worldScripts["shipversion"].shipSpawned(ships[i]);
}
ships[i].awardEquipment("EQ_ESCAPE_POD");
if(i == 0) { //make first ship to weak offender without injectors, need a few shot only to eject it
ships[i].removeEquipment("EQ_FUEL_INJECTION");
ships[i].bounty=33;
ships[i].energy=33;
} else ships[i].abandonShip(); //other ships derelict
} else if( this.$debug ) log(this.name, "ships["+i+"] is undefined ");
}
if( this.$debug ) log(this.name, "test derelict ships: "+ships);
}
}
//
// Internal functions
//
//-----------------------------------------------------------------------------------------------//
this.$EscortDeck_Version = function _escortdeck_EscortDeck_Version() {
var that = _escortdeck_EscortDeck_Version;
var eq_list = (that.eq_list = that.eq_list || [ "EQ_ESCORTDECK", "EQ_ESCORTDECKXL" ]);
var ship = player.ship;
var i = eq_list.length;
while (i--)
if (ship.equipmentStatus(eq_list[i]) === "EQUIPMENT_OK")
return eq_list[i];
return null;
}
//-----------------------------------------------------------------------------------------------//
this.$onChange = function _escortdeck_onChange() {
if (this.$newUsableProb != (1-this.$EscortDeckBasicUnusableProb) && this.$newUsableProb >= 0 && this.$newUsableProb <= 1) {
this.$EscortDeckBasicUnusableProb = (1-this.$newUsableProb);
log(this.name, "Unusable derelict probability changed to "+this.$EscortDeckBasicUnusableProb);
}
if (this.$newAutoLaunch != this.$EscortDeckAutoLaunch) {
this.$EscortDeckAutoLaunch = this.$newAutoLaunch;
log(this.name, "AutoLaunch changed to "+this.$EscortDeckAutoLaunch);
}
log(this.name,"Config changed, newUsableProb:"+this.$newUsableProb+", newAutoLaunch:"+this.$newAutoLaunch+", $EscortDeckBasicUnusableProb:"+this.$EscortDeckBasicUnusableProb+", $EscortDeckAutoLaunch:"+this.$EscortDeckAutoLaunch);
}
//-----------------------------------------------------------------------------------------------//
this.$damageRatio = function _escortdeck_damageRatio(s) {
var fname = "damageRatio";
var shipEquipments = s.equipment;
var i = shipEquipments.length;
var ok = 0;
var damaged = 0;
var eqpKey, eqpStatus, damaged_ratio;
while (i--) {
eqpKey = shipEquipments[i].equipmentKey;
eqpStatus = s.equipmentStatus(eqpKey);
if (this.$debug) log(this.name, fname+": '"+s.displayName+"' has "+eqpKey+": "+eqpStatus);
if (eqpStatus === "EQUIPMENT_OK") {
ok++;
} else if (eqpStatus === "EQUIPMENT_DAMAGED") {
damaged++;
}
}
if (ok + damaged === 0) {
damaged_ratio = 0;
} else if (ok === 0 && damaged > 0) {
damaged_ratio = 1;
} else {
damaged_ratio = damaged / (damaged + ok);
}
if (this.$debug) log(this.name, fname+": '"+s.displayName+"' has "+damaged+" damaged of "+(damaged+ok)+" equipments, ratio "+damaged_ratio+", $EscortDeckUsable: "+s.script.$EscortDeckUsable);
return damaged_ratio
}
//-----------------------------------------------------------------------------------------------//
this.$isUsable = function _escortdeck_isUsable(s, from_fname) {
var fname = "isUsable";
var unusable_prob, rnd;
if (s.script && s.script.$EscortDeckUsable && s.script.$EscortDeckUsable === -1) return false;
var damaged_ratio = this.$damageRatio(s);
if (this.$EscortDeckNPCEqDamage) {
// NPC Equipment Damage OPC is installed, and it adds _a lot_ of equipment damage
damaged_ratio = damaged_ratio / 6.7;
}
if (this.$debug) log(this.name, fname+"("+from_fname+"): '"+s.displayName+"', adjusted damage_ratio: "+damaged_ratio+", $EscortDeckUsable: "+s.script.$EscortDeckUsable);
// probability of damaged to basic systems + equipment damage
// (it's not a probem being larger than 1)
unusable_prob = this.$EscortDeckBasicUnusableProb + damaged_ratio;
rnd = Math.random();
if (this.$debug) log(this.name, fname+": unusable probability: "+unusable_prob+", random: "+rnd);
return (rnd > unusable_prob);
}
//-----------------------------------------------------------------------------------------------//
this.$EscortDeck_HHStoreShip = function _escortdeck_HHStoreShip(ship) {
var that = _escortdeck_HHStoreShip;
var ws = (that.ws = that.ws || worldScripts.escortdeck);
if (!ship) return;
var shipdata = $EscortDeck_MakeShipData(ship);
if (shipdata && shipdata[0]) {
ws.$EscortDeck_HHStoreShipData(shipdata);
} else {
log(ws.name, "Trouble creating shipData for "+ship.displayName);
}
}
//-----------------------------------------------------------------------------------------------//
this.$EscortDeck_HHStoreShipData = function _escortdeck_HHStoreShipData(shipdata) {
var that = _escortdeck_HHStoreShipData;
var ws = (that.ws = that.ws || worldScripts.escortdeck);
var hh = (that.hh = that.hh || worldScripts.HyperspaceHangar);
if (shipdata && shipdata[0]) {
var k = clock.absoluteSeconds;
while (hh._shipsStored[k]) k++;
var displayName = shipdata[0][3];
shipdata[0][0] = galaxyNumber;
shipdata[0][1] = system.ID;
hh._shipsStored[k] = JSON.stringify(shipdata);
hh._currentNames[k] = displayName;
if (hh._currentNames["0_EXIT"] === "EMPTY") hh._currentNames["0_EXIT"] = "Exit";
}
}
//-----------------------------------------------------------------------------------------------//
this.$EscortDeck_AddEscort = function _escortdeck_AddEscort( t, e, owner, w, msg ) { //add t into the deck of ship if possible
var _fname = "$EscortDeck_AddEscort";
// if (this.$debug) log(this.name, _fname+": examining "+(t.displayName ? t.displayName : t)+", mass:"+t.mass);
if( t && t.isValid && t.script &&
( t.isDerelict || t.script.$EscortDeckUsable === -1 || t.script.$EscortDeckUsable > 0 )
&& t.mass > 1000 && t.mass < this.$EscortDeckExtremeMass && !t.isVisualEffect && !t.isPlayer
&& !t.isDock && !t.isStation && !t.isCargo && !t.isRock && !t.isThargoid && !t.isWeapon
&& owner && owner.isValid
&& ( owner.equipmentStatus("EQ_ESCORTDECK") == "EQUIPMENT_OK"
|| owner.equipmentStatus("EQ_ESCORTDECKXL") == "EQUIPMENT_OK" )
&& owner.mass >= w.$EscortDeckMinMass
&& owner.position.distanceTo(t.position) < w.$EscortDeckLandingDist + t.collisionRadius ) {
var p = e.indexOf(t);
if( p > -1 && t.AIState != "LANDING" ) return false;//owned escort is on way, do not land
var r = this.$EscortDeck_TooLarge(t, owner, w);
if( r ) { //reason
w.$EscortDeckLastTooLargeTarget = t; //prevent repeated message
if( msg && owner == player.ship ) {
player.consoleMessage("You can't pick up "+t.displayName+" to your Escort Deck, "+r, 10);
}
return false;
}
if (t.script.$EscortDeckUsable != 1 && t.script.$EscortDeckUsable != -1 ) {
// derelict was not examined for usability
if (this.$isUsable(t, _fname)) { //fit into the deck limits and lucky
log(this.name, t.displayName+" is usable");
t.script.$EscortDeckUsable = 1;
t.script.$TowbarUsableShip = true;
t.script.$TowbarShipHealth = 1;
this.$EscortDeck_AwardEQs(t);
} else {
log(this.name, t.displayName+" is NOT usable");
t.script.$EscortDeckUsable = -1; //unusable as escort
t.script.$TowbarUsableShip = false;
if(this.$debug) log(this.name, t.displayName+" is set to unusable: "+t);
}
}
var diff = owner.velocity.subtract( t.velocity ).magnitude();
if( diff > 100 ) {
log(this.name, "Velocity difference ("+(Math.round(diff/10)*10)+"m/s) too big to load into EscortDeck");
if( owner == player.ship && msg && p == -1 ) //not in auto landing
player.consoleMessage(Math.round(diff/10)*10+" m/s speed difference");
return false;
}
//put into the selected pad if fit into
//and regardless of occupied (replace) if this is the current target
var spad = w.$EscortDeckSelectedPad;
if( spad > -1 ) {
if(this.$debug) log(w.name, "Try "+t.displayName+" to selected pad "+(spad+1)); //debug
var f = false; //force replace
if( owner.target == t ) f = true;
r = w.$EscortDeck_PutShip(false, t, spad, owner, msg, f);
if( !r ) return true; //report successfully added
else if(this.$debug) log(w.name, "Can't add "+t.displayName+" to selected pad "+(spad+1)+", "+r); //debug
}
//put owned escorts back to the correct pad
if( p > -1 ) {
if(this.$debug) log(w.name, "Try "+t.displayName+" to old pad "+(p+1)); //debug
r = w.$EscortDeck_PutShip(false, t, p, owner, msg, true);
if( !r ) return true; //report successfully added
else { //if faliled the remove and try other pads as new ships
if(this.$debug) log(w.name, "Can't add "+t.displayName+" back to pad "+(p+1)+", "+r); //debug
e[p] = null;
p = -1;
}
}
if(this.$debug) log(w.name, "Try "+t.displayName+" to empty pads"); //debug
//if Adder or smaller then try Adder pads first
var large = w.$EscortDeck_isLarge(owner, w);
if( large && //side pads need large ship
w.$EscortDeck_AddMini(t, owner, w, msg, false) )
return true; //report successfully added
//non-small escort need large ship
for(var i = 0; i < w.$EscortDeckPadPos.length; i++ ) {
if( !w.$EscortDeck_isMiniPad(i, owner, w) ) {
//find the first empty pad except Adder only pads
// log(w.name, i+". free:"+w.$EscortDeck_isPadFree(i, w)); //debug
r = w.$EscortDeck_PutShip(false, t, i, owner, msg, false);
if( !r ) i = w.$EscortDeckPadPos.length;//exit from the for cycle
}
}
if( e.indexOf(t) > -1 ) return true; //report successfully added
//replace first flying escort if no more empty pad and none selected
if(this.$debug) log(w.name, "Try "+t.displayName+" to replace a flying escort");
for(var i = 0; i < w.$EscortDeckPadPos.length; i++ ) {
if( !w.$EscortDeck_isMiniPad(i, owner, w)
|| large && w.$EscortDeck_isMini(t) ) {
if( !w.$EscortDeckShipPos[i] ) { //on the way
r = w.$EscortDeck_PutShip(false, t, i, owner, msg, true);
if( !r ) return true;
}
}
}
if( e.indexOf(t) > -1 ) return true; //report successfully added
else {
if( msg && owner == player.ship ) {
player.consoleMessage("No room on Escort Deck for "+t.displayName, 10);
}
if(this.$debug) log(w.name, "Can't add "+t.displayName+" to Escort Deck, "+r); //debug
return false;
}
} //else log(this.name, _fname+": ship "+(t.displayName ? t.displayName : t)+" didn't meet basic condition, ignoring");
return false; //can not add escort to the deck
}
//-----------------------------------------------------------------------------------------------//
this.$EscortDeck_AddMini = function _escortdeck_AddMini( t, owner, w, msg, land ) { //add mini escort to a small pad if possible
if( !w.$EscortDeck_isMini(t) ) return false; //need like an Adder or smaller
for( var i = 0; i < w.$EscortDeckPadPos.length; i++ ) {
if( w.$EscortDeck_isMiniPad(i, owner, w) ) {
if( !w.$EscortDeck_PutShip(false, t, i, owner, msg, land ) )
return true;
}
}
return false;
}
//-----------------------------------------------------------------------------------------------//
this.$EscortDeck_AwardEQs = function _escortdeck_AwardEQs(ship) {
var logging = this.$logging;
var debug = this.$debug;
var LN10 = Math.LN10;
var damage_ratio = this.$damageRatio(ship);
function escortdeck_awardEQ(eqKey) {
var eqInfo = EquipmentInfo.infoForKey(eqKey);
var rnd = Math.random();
var threshold = 0.03 * eqInfo.techLevel + 0.05 * Math.log(eqInfo.calculatedPrice/10)/LN10;
if (debug) log("escortdeck", "usable "+ship.displayName+", "+eqKey+": tl:"+eqInfo.techLevel+" ("+0.03 * eqInfo.techLevel+"), price: "+eqInfo.calculatedPrice/10+" ("+0.05 * Math.log(eqInfo.calculatedPrice/10)/LN10+"), threshold: "+threshold+", rnd: "+rnd);
if (rnd > threshold) {
var damage_prob = damage_ratio * eqInfo.damageProbability;
ship.awardEquipment(eqKey);
if (Math.random() < damage_prob) ship.setEquipmentStatus(eqKey, "EQUIPMENT_DAMAGED");
if (logging) log("escortdeck", "awarding "+eqKey+"("+ship.equipmentStatus(eqKey)+") to usable "+ship.displayName);
return true;
}
return false;
}
//award player specific equipments, useful when sitting in from a Carrier
if (ship && ship.isValid) {
escortdeck_awardEQ("EQ_ADVANCED_COMPASS");
if(worldScripts.combat_MFD) escortdeck_awardEQ("EQ_COMBATMFD");
if (worldScripts.MFDFastConfiguration) escortdeck_awardEQ("EQ_MFD_FAST_CONFIG");
if (ship.hasHyperspaceMotor) escortdeck_awardEQ("EQ_ADVANCED_NAVIGATIONAL_ARRAY");
escortdeck_awardEQ("EQ_MULTI_TARGET");
escortdeck_awardEQ("EQ_SCANNER_SHOW_MISSILE_TARGET");
if (worldScripts.targetAutolock) //requires EQ_SCANNER_SHOW_MISSILE_TARGET so add after
escortdeck_awardEQ("EQ_TARGET_AUTOLOCK");
escortdeck_awardEQ("EQ_TARGET_MEMORY");
escortdeck_awardEQ("EQ_INTEGRATED_TARGETING_SYSTEM");//requires Target Memory so add after
if (worldScripts.telescope) escortdeck_awardEQ("EQ_TELESCOPE");
if (worldScripts.sniperlock) escortdeck_awardEQ("EQ_SNIPERLOCK");
if (worldScripts.towbar) escortdeck_awardEQ("EQ_LASERREDUCTOR");
if (worldScripts.traildetector) escortdeck_awardEQ("EQ_TRAIL_DETECTOR");
if (worldScripts.Police_Scanner_Upgrade) escortdeck_awardEQ("EQ_POLICE_SCANNER_UPGRADE");
if (worldScripts.hudselector) escortdeck_awardEQ("EQ_HUDSELECTOR");
if (worldScripts.BarrelRoll) escortdeck_awardEQ("EQ_BARREL_ROLL");
escortdeck_awardEQ("EQ_WORMHOLE_SCANNER");
escortdeck_awardEQ("EQ_HEAT_SHIELD");//protection for sunskimming, requested by QCS
}
}
//-----------------------------------------------------------------------------------------------//
this.$EscortDeck_Bounty = function _escortdeck_Bounty( ship ) {
if( ship.isDerelict && ship.bounty > 0 ) {
player.credits += ship.bounty;
player.consoleMessage("Bounty: "+formatCredits(ship.bounty, true, true));
player.consoleMessage("Total: "+formatCredits(player.credits, true, true));
ship.bounty = 0;
}
}
//-----------------------------------------------------------------------------------------------//
this.$EscortDeck_ControlAll = function _escortdeck_ControlAll( launch, manual, w ) { //launch or call back all escorts
if( launch && w.$EscortDeck_ControlFast(manual) ) return(false);
var c = 0;
var carrier = false;
if( w.$EscortDeckCWS && player.ship.dataKey.indexOf("carrier") > -1 )
carrier = true;
for(var i = 0; i < w.$EscortDeckPadPos.length; i++ ) {
if( !w.$EscortDeckLocked[i] &&
( !launch || !carrier || w.$EscortDeckXL || manual ||
//can prevent auto launch of Anaconda from Carrier
w.$EscortDeckCBigTraderLaunch ||
w.$EscortDeckShip && w.$EscortDeckShip[i]
&& w.$EscortDeckShip[i].mass < w.$EscortDeckXLMass ) ) {
if( w.$EscortDeck_Control1(launch, i, false, manual)) //launch without message
c++; //count usable escorts
}
}
if( c > 0 && ( manual || !launch ) ) { //do not message autolaunch when entering red alert
var e = c+" Escorts ";
if( c == 1 ) e = "1 Escort ";
var m = "called back";
if( launch ) m = "on the way";
player.consoleMessage(e+m, 10);
}
w.$EscortDeck_MFD(w); //update MFD
return(c);
}
//-----------------------------------------------------------------------------------------------//
this.$EscortDeck_ControlFast = function _escortdeck_ControlFast(manual) {
var max = player.ship.maxSpeed; //no launch over max.speed
// if( !manual ) max = max * 0.999; //no autolaunch at max.speed
if( player.ship.speed > max ) {
if( manual ) player.consoleMessage("You travel too fast to launch escorts", 3);
// else player.consoleMessage("No autolaunch of escorts at maximal speed", 3);
return(true);
}
return(false);
}
//-----------------------------------------------------------------------------------------------//
this.$EscortDeck_Control = function _escortdeck_Control( launch, pad, msg, w ) { //launch or call back an escort
w.$EscortDeck_Control1(launch, pad, msg, true);
}
//-----------------------------------------------------------------------------------------------//
this.$EscortDeck_Control1 = function _escortdeck_Control1(launch, pad, msg, manual) { //launch or call back an escort
var that = _escortdeck_Control1;
var w = (that.w = that.w || worldScripts.escortdeck);
if (launch && this.$EscortDeck_ControlFast(manual)) return false;
var s = null;
var p = player.ship;
if (w.$EscortDeckShip) s = w.$EscortDeckShip[pad];
if (s && s.isValid) {
if (!s.script || !(s.script.$EscortDeckUsable > 0)) { //unusable
if (msg) w.$EscortDeck_Release(p, pad, w); //msg mean manual and one ship
return(false);
}
var m = null; //the carrier can't launch
if (launch && !w.$EscortDeckLocked[pad]) {
var seen = [];
s.script.shipAttackedOther = this.shipAttackedOther;
s.script.shipKilledOther = this.shipKilledOther;
s.script.$debugHarderWay = true;
s.script.$debug = false;
s.script.$logging = true;
log(this.name, "Launching "+s.displayName+" from Pad "+pad+", escortdeckUsable "+s.script.$EscortDeckUsable+" script: "+JSON.stringify(s.script, function(key,val) {
if (val != null && typeof val == "object") {
if (seen.indexOf(val) >= 0) {return;}
seen.push(val);
}
return val;
}));
if (p.removeCollisionException) //from v1.81
p.removeCollisionException(s);
w.$EscortDeckShipData[pad] = null; //prevent recreation in dock
w.$EscortDeckShipPos[pad] = null; //do not update position in FCB
//increase steering and acceleration in proportion with the smaller mass on deck
w.$EscortDeck_SetMaxs(p, w);
if (s.AI != "EscortDeck_AI.plist") s.switchAI("EscortDeck_AI.plist");
s.AIState = "GOTO"; //switch back from waypoint AI
s.scannerDisplayColor1 = [0, 0.7, 0]; //green
s.velocity = p.velocity; //prevent collision if player travel at speed
if (w.$EscortDeckPadVec) {//help to leave the owner: jump 20m far
var j = w.$EscortDeckJumpDist;
var x = j * w.$EscortDeckPadVec[pad][0];
var y = j * w.$EscortDeckPadVec[pad][1];
var z = j * w.$EscortDeckPadVec[pad][2];
if (p.dataKey.indexOf("carrier") > -1) {
if (w.$EscortDeck_isMiniPad(pad, p, w)) {
z = j; //Carrier mini pads jump forward
} else if(!w.$EscortDeckXL) { //traders jump sideways
if (pad == 6) {x = j; z = 0;}
else if (pad == 5) {x = -j; z = 0;}
}
}
var o = p.orientation;
var sp = s.position.add(o.vectorRight().multiply(x))
.add(o.vectorUp().multiply(y))
.add(p.heading.multiply(z));
if (sp.magnitude() > 500) s.position = sp;
}
s.script.$EscortDeckOnDeck = false;
m = "on the way";
} else { //call back
if (s.AIState != "DECK") { //isn't in deck already
// if( !w.$EscortDeckShipPos[pad] ) {
if (s.AI != "EscortDeck_AI.plist") s.switchAI("EscortDeck_AI.plist");
s.AIState = "LANDING"; //switch back from waypoint AI
m = "called back";
}
}
if (m && msg) {
player.consoleMessage(s.displayName+" "+m, 5);
w.$EscortDeck_MFD(w); //update MFD
}
if (w.$debug) log(w.name, pad+". launch:"+launch+" "+m); //debug
if (m) return true;
}
return false;
}
//-----------------------------------------------------------------------------------------------//
this.$EscortDeck_EQActivated = function _escortdeck_EQActivated(w) { //launch or call back one or all escorts
var p = player.ship; //called from carriers-escort-eq.js also!
if( !p || !p.isValid ) return;
if( w.$EscortDeckSelectedPad == w.$EscortDeckPadPos.length && w.$EscortDeckToWS ) {
if( w.$EscortDeckToWS.$TowbarShip ) w.$EscortDeckToWS.$Towbar_Release();
else player.consoleMessage("No ship on Towbar");
} else if( w.$EscortDeckSelectedPad == -2 ) { //setup sit into mode
w.$EscortDeckSitInto = !w.$EscortDeckSitInto; //flip-flop
w.$EscortDeck_EQDisplayMode(w);
} else if( w.$EscortDeckSelectedPad == -1 ) { //launch or call back all escorts
w.$EscortDeckLaunch = !w.$EscortDeckLaunch; //flip-flop
w.$EscortDeck_ControlAll(w.$EscortDeckLaunch, true, w);
} else { //launch or call back a selected escort with message
var s = w.$EscortDeckShip[w.$EscortDeckSelectedPad];
if( s && s.isValid ) {
if( w.$EscortDeckShipPos[w.$EscortDeckSelectedPad] ) {
if( w.$EscortDeckLocked[w.$EscortDeckSelectedPad] ) {
w.$EscortDeckLaunch = true; //launch if on deck and locked
w.$EscortDeckLocked[w.$EscortDeckSelectedPad] = false;
} else {
w.$EscortDeckLaunch = false;
w.$EscortDeckLocked[w.$EscortDeckSelectedPad] = true; //lock
}
w.$EscortDeck_MFD(w); //update MFD
} else {
w.$EscortDeckLocked[w.$EscortDeckSelectedPad] = false;
if( s.AIState == "LANDING" ) w.$EscortDeckLaunch = true;
else {
w.$EscortDeckLaunch = false;
if( s.dataKey.indexOf("carrier-player") > -1 ) {
if( p.target == s ) {//target is the player's carrier?
player.consoleMessage("Approach the Carrier "+
"within "+w.$EscortDeckLandingDist+
"m with small speed difference "+
" for landing",10);
} else { player.consoleMessage("Hold ident lock on"+
" your Carrier to keep up"+
" landing clearance",5);
}
}
}
}
if( w.$EscortDeckSitInto //deck is in the "sit into the escort" mode
&& w.$EscortDeckCWS //Carriers OXP is installed
&& p.dataKey.indexOf("carrier-player") > -1 //player in the Carrier
&& s.AIState == "DECK" //selected a landed, usable and playable escort
&& s.missileCapacity > 0 //bugfix against a core bug with 0 max_missiles
&& w.$EscortDeckPlayableDataKeys.indexOf(s.dataKey) > -1 ) {
w.$EscortDeckCWS.$Carriers_SitIntoAnEscort(s); //launch player
} else w.$EscortDeck_Control( w.$EscortDeckLaunch,
w.$EscortDeckSelectedPad, true, w);
} else { //no ship in this pad
if( w.$EscortDeckLocked[w.$EscortDeckSelectedPad] )
w.$EscortDeckLocked[w.$EscortDeckSelectedPad] = false;
else w.$EscortDeckLocked[w.$EscortDeckSelectedPad] = true;
}
}
}
//-----------------------------------------------------------------------------------------------//
this.$EscortDeck_EQDisplayMode = function _escortdeck_EQDisplayMode(w) {
if( w.$EscortDeckSelectedPad == -1 ) { //launch or callback mode
var l = "Launch all non-locked";
if( w.$EscortDeckLaunch ) l = "Call back all flying";
player.consoleMessage(l+" escorts when EscortDeck activated",5);
} else if( w.$EscortDeckSelectedPad == -2 ) { //setup sit into mode
var l = "avoid";
if( w.$EscortDeckSitInto ) l = "allow";
player.consoleMessage("EscortDeck "+l+" to sit into a selected escort",5);
}
}
//-----------------------------------------------------------------------------------------------//
this.$EscortDeck_EQMode = function _escortdeck_EQMode(w, playable) { //select the next pad on escort deck
var p = player.ship; //called from carriers-escort-eq.js also!
if( p && p.isValid ) {
w.$EscortDeckSelectedPad++;
/* if( playable ) { - removed to allow select any ship to drop salvage, etc.
var start = false;
if( w.$EscortDeckSelectedPad == 0 ) start = true; //start from the first pad
var found = -1;
while( found < 0 && w.$EscortDeckSelectedPad < w.$EscortDeckPadPos.length ) {
var s = w.$EscortDeckShip[w.$EscortDeckSelectedPad];
if( s && s.isValid && s.script && s.script.$EscortDeckUsable > 0
&& w.$EscortDeckPlayableDataKeys.indexOf(s.dataKey) > -1 )
found = w.$EscortDeckSelectedPad;
w.$EscortDeckSelectedPad++;
}
w.$EscortDeckSelectedPad = found;
if( start && found == -1 )
player.consoleMessage( "You have not any playable ships on board", 5 );
}
*/
// while( w.$EscortDeckSelectedPad < w.$EscortDeckPadPos.length
// && !w.$EscortDeckShip[w.$EscortDeckSelectedPad] ) //skip empty pads
// w.$EscortDeckSelectedPad++; - no skip to be able to lock to a specific pad
if( !w.$EscortDeckToWS || w.$EscortDeckSelectedPad <= w.$EscortDeckPadPos.length ) {
if( w.$EscortDeckSelectedPad >= w.$EscortDeckPadPos.length ) {
if( w.$EscortDeckSitIntoWithMode //allowing sit into enabled with mode key
&& p.dataKey.indexOf("carrier") > -1 )//and player in a Carrier
w.$EscortDeckSelectedPad = -2; //setup sit into mode
else w.$EscortDeckSelectedPad = -1; //none selected
var ws = w.$EscortDeckTWS; //Telescope worldScript
if( ws ) ws.$TelescopeTargetSet = true;
p.target = null;
if( ws ) ws.$TelescopeTargetSet = false;
}
if( w.$EscortDeckSelectedPad > -1 ) {
var s = w.$EscortDeckShip[w.$EscortDeckSelectedPad];
if( s && s.isValid ) {
var ws = w.$EscortDeckTWS; //Telescope worldScript
if( ws ) {
var mi = ws.$TelescopeList.indexOf( s );
if( mi == -1 ) ws.$Telescope_Scan(); //forced rescan
if( mi > -1 ) {
if( ws.$TelescopeListi != mi + 1 ) {
ws.$TelescopeListi = mi + 1;
ws.$Telescope_Show2( false ); //lock target
}
}
} else p.target = s; //target the selected ship
}
} else w.$EscortDeck_EQDisplayMode(w);
}
w.$EscortDeck_MFD(w); //update MFD
}
}
//-----------------------------------------------------------------------------------------------//
this.$EscortDeck_FCB = function _escortdeck_FCB(delta) { //FrameCallBack updating positions of escort ships
var that = _escortdeck_FCB;
var owner = (that.owner = that.owner || player.ship); //modify this for NPCs
var w = (that.w = that.w || worldScripts.escortdeck);
var wSV = (that.wSV = that.wSV || worldScripts.shipversion); // ShipVersion
var tb = (that.tb = that.tb || w.$EscortDeckToWS); // Towbar
var oldSV = (that.oldSV = that.oldSV || (wSV && wSV.version && wSV.version > "1.24" ? false : true));
if (!owner || !owner.isValid) { //test with PS.explode() in debug console
var v = w.$EscortDeckV;
if (!v || !v.position) return; //exit if player died
if (!w.$EDH) w.$EDH = v.heading.multiply(-4).add(Vector3D.randomDirection());
v.position = v.position.add(w.$EDH.multiply(100*delta)); //deck drift away
v.orientation = v.orientation.rotateX(delta).rotateY(delta).rotateZ(delta/2);
//log(w.name, w.$EscortDeckV.position.x);
return;
}
var zv = w.$EscortDeckZV;
var eship = w.$EscortDeckShip;
var epos = w.$EscortDeckShipPos;
if (owner != player.ship && owner.script && owner.script.$EscortDeckZV) { //NPC
zv = owner.script.$EscortDeckZV;
eship = owner.script.$EscortDeckShip;
epos = owner.script.$EscortDeckShipPos;
} else if (w.$EscortDeckCWS && w.$EscortDeckCWS.$CarriersPlayerInEscort) {
owner = w.$EscortDeckCWS.$CarriersCarrier; //deck is no more on player ship but on Carrier
if (!owner || !owner.isValid) return; //exit if carrier died
}
if (!zv) {
if (!zv) zv = Vector3D(0,0,-50);
zv.x = 0;
var hl = owner.boundingBox.z * 0.5; //half of the ship's length
if (zv.z < hl) zv.z = -hl; //correct z position for carriers
if (zv.y > 0) zv.y = 0; //do not raise over the center line
w.$EscortDeckZV = zv;
}
var d = w.$EscortDeckShipData;
var sh = w.$EscortDeckSpawnedShips.pop();
if ( sh && sh.isValid && oldSV) {
// restore ship data after ShipVersion's shipSpawned has its way with the ship
// ShipVersion 1.25+ looks for ship.script.$ShipVersionIgnore, which we set, and ignores the ship
var pad = eship.indexOf(sh);
if (w.$debug) log(w.name, "FCB pad:"+pad+" d:"+d[pad]);
if (pad > -1 && d[pad]) {
var seen = [];
w.$EscortDeck_RestoreShipData(sh, d[pad], w, owner);
log(w.name, "restored ship: "+JSON.stringify(sh, function(key,val) {
if (val != null && typeof val == "object") {
if (seen.indexOf(val) >= 0) {return;}
seen.push(val);
}
return val;
}));
}
}
// if(w.$debug) log(w.name, "FCB pad:1 d:"+d[1]);
//set the new position of the deck if exist (carriers haven't added deck)
var dp = owner.position.add(owner.heading.multiply(zv.z)); //deck position, used at escorts also
if (zv.y != 0) dp = dp.add(owner.orientation.vectorUp().multiply(zv.y)); //move lower if needed
if (w.$EscortDeckV) { //set the new position of the deck if exist (carriers haven't added deck)
w.$EscortDeckV.position = dp;
w.$EscortDeckV.orientation = owner.orientation;
}
//set the new positions of escorts
for (var pad = 0; pad < w.$EscortDeckPadPos.length; pad++) {
var s = eship[pad];
if (s) {
if(s.isValid) {
var sp = epos[pad]; //on deck and is not a Carrier?
if (sp && s.dataKey.indexOf("carrier") === -1) {
var h = owner.heading;
var o = owner.orientation;
var r = o.vectorRight();
var u = o.vectorUp();
var pos = dp.add(h.multiply(sp.z)).add(r.multiply(sp.x)).add(u.multiply(sp.y));
if (pos.magnitude() > 500) s.position = pos;
if (w.$EscortDeckCWS
&& owner.dataKey.indexOf("carrier") > -1) {
w.$EscortDeck_FCBC(s, owner, pad, o, h, r, u, w);
} else if (w.$EscortDeckXL) {
var left = -0.5; //left side of the xl deck
if (Math.round(pad/2) != pad/2) left = 0.5; //right
var p = -Math.PI;
s.orientation=o.rotate(r,p*0.5001).rotate(h,left*p);
} else s.orientation = o.rotate(r, 0.0001);
//if(pad==6 || pad==7) reverse ori
}
} else { //escort ship destroyed
if (w.$EscortDeckShip) {
if (owner != player.ship && (!w.$EscortDeckCWS
|| owner != w.$EscortDeckCWS.$CarriersCarrier))
owner.script.$EscortDeckShip[pad] = null; //NPC escort
else w.$EscortDeckShip[pad] = null; //player escort
}
}
}
}
w.$EscortDeckFCBDelta += delta;
if (w.$EscortDeckFCBDelta > 0.5) { //update AI and MFD twice in a second
w.$EscortDeckFCBDelta = 0;
//check nearby usable derelict for pickup
var t = owner.target;
// log(w.name, "target:"+t); //debug
if (t && w.$EscortDeckLastTooLargeTarget != t //prevent repeated too large message
&& eship.indexOf(t) === -1 //and must not already owned
&& !(tb && tb.$TowbarShip && t == tb.$TowbarShip) ) //and not being towed
w.$EscortDeck_AddEscort(t, eship, owner, w, true);
//AI helper part, keep in sync with the UPDATE parts of EscortDeck_AI.plist
for (var pad = 0; pad < w.$EscortDeckPadPos.length; pad++) {
var s = eship[pad];
if (s && s.isValid && s.AI === "EscortDeck_AI.plist") { //skip waipoint AIs
var e = s.script;
if (s.AIState === "FOLLOW") e._locatePlayer2(s);
else if (s.AIState === "GOTO") e._findPlayerHostiles2(s);
else if (s.AIState === "ATTACK" || s.AIState == "INJECT"
|| s.AIState === "SNIPER") e._combatCheck2(s);
else if (s.AIState === "FLEE") e._fleeCheck2(s);
else if (s.AIState === "LANDING") e._landing2(s);
}
}
w.$EscortDeck_MFD(w); //update MFD
}
}
//-----------------------------------------------------------------------------------------------//
this.$EscortDeck_FCBC = function _escortdeck_FCBC(s, owner, pad, o, h, r, u, w) { //FrameCallBack rotate ships on Carriers
var left = -0.5001; //left side of the xl deck
if( Math.round(pad/2) != pad/2 ) left = 0.5001; //right side
var p = -Math.PI;
if( w.$EscortDeck_isMiniPad(pad, owner, w) ) {
if( w.$EscortDeckXL ) left = -left;
s.orientation = o.rotate( h, left*p ).rotate( u, left*p*0.6 ).rotate( r, 0.0001 );
} else if( w.$EscortDeckXL && pad > 3 )
s.orientation = o.rotate( u, left*p ).rotate( r, 0.0001 );
else s.orientation = o.rotate( r, 0.0001 ); //fix blinking shaders bug
}
//-----------------------------------------------------------------------------------------------//
this.$EscortDeck_HHGet = function _escortdeck_HHGet(k, pad, w) { //get a ship from Hyperspace Hangar into the given pad
var that = _escortdeck_HHGet;
var w = (that.w = that.w || worldScripts.escortdeck);
var hh = (that.hh = that.hh || worldScripts.HyperspaceHangar);
var d = w.$EscortDeckShipData[pad];
if( !d || !d[1] ) {
log(this.name, "Parsing ship "+k+" "+hh._shipsStored[k]);
w.$EscortDeckShipData[pad] = JSON.parse(hh._shipsStored[k]);
delete hh._shipsStored[k];
delete hh._currentNames[k];
if( Object.keys(hh._currentNames).length < 1 ) //this was the last ship
hh._currentNames["1_EXIT"] = "EMPTY";
}
}
//-----------------------------------------------------------------------------------------------//
this.$EscortDeck_Interface = function _escortdeck_Interface() {
var ps = player.ship; if( !ps || !ps.isValid || !ps.docked ) return;
var n = 0, m = 0, h = this.$EscortDeckShipData, p = this.$EscortDeckPadPos;
if( h ) n = h.length;
if( p ) m = p.length;
var s = "";
if( n > 0 ) {
n = 0;
for(var i = 0; i < m; i++) {
s += " Deck "+(i+1)+": ";
var d = h[i];
if( d && d[0] ) {
n++;
if( d[0][3] ) s += d[0][3]+"\n"; //displayName
else {
if(d[0] && d[0][7]) { //shipClassName
s += d[0][7];
if(d[0][8]) s += d[0][8]; //shipUniqueName
s += "\n";
} else if(d[1]) s += d[1]+"\n"; //dataKey at last resort
}
} else s += "empty\n"
}
n = " ("+n+" of "+m+" ships)";
//s = s.substr( 0, s.length - 2 ); //cut last comma
} else {
n = "";
s = "You have no ships on Escort Deck.";
}
if( ps.equipmentStatus("EQ_ESCORTDECKXL") == "EQUIPMENT_OK" ) n = " XL"+n;
ps.dockedStation.setInterface(this.name,{
title:"Escort Deck"+n,
category:"Ship Systems",
summary:s,
callback:this.$EscortDeck_InterfaceCallBack.bind(this)
});
}
//-----------------------------------------------------------------------------------------------//
this.$EscortDeck_InterfaceCallBack = function _escortdeck_InterfaceCallBack(lastchoice) {
var that = _escortdeck_InterfaceCallBack;
var w = (that.w = that.w || worldScripts.escortdeck);
var hh = (that.hh = that.hh || worldScripts.HyperspaceHangar);
var ps = player.ship; if( !ps || !ps.isValid ) return;
player.ship.hudHidden = true;//to shift down the menu
var d = w.$EscortDeckShipData;
var dkm = null;
if( !w.$EscortDeckI ) w.$EscortDeckSpinModel = false; //rotate only in the subpage
var curChoices = {};
var exitscr = "GUI_SCREEN_INTERFACES";
var ex = "99_EXIT";
var ic = ex;
if( lastchoice === "51_SPIN" ) ic = lastchoice;
var mp = 0;
var msg = "";
var oc = "orangeColor";
var j = 0;
var getHH = false;
var len = w.$EscortDeckPadPos.length;
if( w.$EscortDeckSelectedPad < 0 ) w.$EscortDeckSelectedPad = 0;
if( ps.equipmentStatus("EQ_ESCORTDECK") !== "EQUIPMENT_OK"
&& ps.equipmentStatus("EQ_ESCORTDECKXL") !== "EQUIPMENT_OK" ) {
msg = "You have no Escort Deck";
if( ps.mass > w.$EscortDeckMinMass ) msg += ", buy it in the Equipment shop.";
else msg += ", moreover your ship is too small to hold it."
exitscr = "GUI_SCREEN_EQUIP_SHIP";
} else {
var pad = w.$EscortDeckSelectedPad;
if( w.$EscortDeckI ) { //show submenu when an escort ship is selected
if( d[pad] && d[pad][1] ) {
// curChoices["31_EQUIP"] = {text:"Equip", color:oc };
curChoices["41_RENAME"] = {text:"Rename", color:oc };
if( w.$EscortDeckSpinModel )
curChoices["51_SPIN"] = {text:"Stop rotation", color:oc };
else curChoices["51_SPIN"] = {text:"Rotate", color:oc };
if (d[pad][9] < 7) {
curChoices["61_REFUEL"] = {text:"Refuel", color:oc };
}
if (hh) {
curChoices["71_HH"] = {text:"Put into Hyperspace Hangar", color:oc };
} else curChoices["72_NHH"] = {text:"No Hyperspace Hangar installed",
unselectable:true };
let salvage_price = w.$EscortDeck_SalvagePadPrice(pad);
curChoices["81_SALVAGE"] = {text:"Salvage"+(salvage_price ? " and get about "
+Math.round(salvage_price/1000)*1000+" credits" : ""),
color:"redColor" };
} else if (hh) {
var fitinto=false;
var sID = system.ID;
for( var k in hh._shipsStored ) {
var sh = JSON.parse(hh._shipsStored[k]); //ship in hangar
if (sh[0][1] === sID) {
if( sh && !w.$EscortDeck_TooLarge2b(ps, w, pad, sh)) {
fitinto=true;
var n = sh[0];
if( n ) n = n[3]; else n = sh[1]; //show dataKey if no name
/* if( sh[14] ) {
n += " ("+Math.floor(sh[14].mass/1000)+"t, "
+Math.round(sh[14].x)+"x"
+Math.round(sh[14].y)+"x"
+Math.round(sh[14].z)+"m)";
}*/
curChoices["22_"+k] = "Get "+n+" from Hyperspace Hangar into Deck "+(pad+1);
}
} else { log(this.name, "HyperSpaceHangar ship "+sh[0][3]+" is in system "+sh[0][4]); }
}
if( !fitinto ) {
curChoices["21_PHH"] = {
text:"No ship in Hyperspace Hangar which fit into Deck "
+(pad+1),
unselectable:true };
} else {
getHH = true;
}
} else curChoices["20_NHH"] = { text:"No Hyperspace Hangar installed", unselectable:true };
// curChoices["71_EMPTY_LINE"] = {text:""};
} else { //in the first screen show the list of pads with escort names
for( pad = 0; pad < len && j++ < 20; pad++ ) {
var co = "orangeColor";
var s = d[pad];
var tx = "Deck "+(pad+1)+" ";
var x = 0, y = 0, z = 0, maxmass = 0;
var si = w.$EscortDeckPadSize;
if( si && si[0] ) {
var e = null;
var len = si.length-1;
if( pad < 0 || pad > len ) e = si[len]; //the last line contain the maximums
else e = si[pad];
if( e && e[0] ) {
x = e[0];
y = e[1];
z = e[2];
maxmass = e[3];
}
}
tx += "("+Math.floor(maxmass/1000)+"t, "+x+"x"+y+"x"+z+"m): ";
if( s && s[0] && s[0][3] ) {
tx += s[0][3].substring(0,38);// + " Fuel: "+Math.round(s[9]*10)/10+"ly";
// msg += w.$EscortDeck_MFDAlign(tx, s.AIState)+"\n";
} else {
tx += "Empty";
co = "grayColor";
}
tx += "\n";
var cc = "01_" + ( pad < 10 ? "0" : "" ) + pad;
curChoices[cc] = {text:tx, color:co, alignment:"LEFT"};
if( pad == w.$EscortDeckSelectedPad ) {
ic = cc; //initialChoicesKey
}
}
pad = w.$EscortDeckSelectedPad;
}
msg = "Deck "+(pad+1)+": ";
if( d[pad] && d[pad][1] ) {
dkm = "["+d[pad][1]+"]";//dataKey
mp = d[pad][13];//personality
msg += w.$EscortDeck_InterfaceShipData(d[pad],
w.$EscortDeckSpinModel ); //no eqs when spinning
} else msg += "Empty";
}
if(w.$EscortDeckI) curChoices[ex] = {text:"Back", color:oc};
else curChoices[ex] = {text:"Exit", color:oc, alignment:"LEFT"};
var xl = "";
if( ps.equipmentStatus("EQ_ESCORTDECKXL") == "EQUIPMENT_OK" ) xl = " XL";
var opts = {
screenID: "escortdeck",
title: "Escort Deck"+xl,
background: {name:"escortdeck_bg.png", height:512},//532//546
allowInterrupt: false,
exitScreen: exitscr,
choices: curChoices,
initialChoicesKey: ic,
message: msg,
model: dkm,
modelPersonality: mp,
spinModel: w.$EscortDeckSpinModel
};
if (getHH) {
worldScripts["Paginated Screen"].$runScreen(opts, w.$EscortDeck_InterfaceChoice, w, true);
} else {
mission.runScreen(opts, w.$EscortDeck_InterfaceChoice, w);
}
// if Gallery or Towbar has been loaded and used, remove the frame callback
var g = w.$EscortDeckGWS;
if (g && isValidFrameCallback(g.$GalleryFCB)) removeFrameCallback(g.$GalleryFCB);
var r = w.$EscortDeckToWS;
if( r && isValidFrameCallback( r.$TowbarFCB2 ) ) removeFrameCallback( r.$TowbarFCB2 );
var m = mission.displayModel;
if( m && !w.$EscortDeckSpinModel ) {
m.orientation = m.orientation.rotateZ(-Math.PI/2).rotateX(-Math.PI/4).rotateY(Math.PI/8);
m.position = Vector3D(m.position.x, m.position.y, 150);
}
}
//-----------------------------------------------------------------------------------------------//
this.$EscortDeck_InterfaceChoice = function _escortdeck_InterfaceChoice(choice) {
var that = _escortdeck_InterfaceChoice;
var w = (that.w = that.w || worldScripts.escortdeck);
// log(this.name, choice+" "+choice.indexOf("01_")+" "+choice.substr(3));
if (choice === "99_EXIT") {
if( !w.$EscortDeckI ) { //selected on the first page
w.$EscortDeckSelectedPad = -1; //clear pad selection
player.ship.hudHidden = false;
return;
} else w.$EscortDeckI = false; //back to the first page
}
if (choice.indexOf("01_") === 0) {
w.$EscortDeckSelectedPad = 1*choice.substr(3);
w.$EscortDeckI = true; //go to the subpage
} else if (choice.indexOf("22_") === 0) { //get ship from HyperHangar
var k = choice.substr(3);
w.$EscortDeck_HHGet(k, w.$EscortDeckSelectedPad, w);
w.$EscortDeckI = false; //back to the first page
} else if (choice === "41_RENAME") {
var pad = w.$EscortDeckSelectedPad;
var d = w.$EscortDeckShipData[pad];
if (d && d[1] ) {
var msg = "Deck "+(pad+1)+": ";
var dkm = "["+d[1]+"]";//dataKey
var mp = d[13];//personality
msg += w.$EscortDeck_InterfaceShipData(d, true)+
"\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\nType the new name of "
+d[0][3]+":";
mission.runScreen({
screenID: "escortdeck",
title: "Escort Deck",
background: {name:"escortdeck_bg.png", height:512},
allowInterrupt: false,
message: msg,
model: dkm,
modelPersonality: mp,
spinModel: w.$EscortDeckSpinModel,
textEntry: true
},function(text) {
if (text && text.length > 0) {
if( d && d[0] ) {
w.$EscortDeckShipData[pad][0][8]=text;//shipUniqueName
var t = "";
if( d[0][7] ) t = d[0][7]+": ";
w.$EscortDeckShipData[pad][0][3] = t + text;
}
}
w.$EscortDeckI = false; //back to the first page
w.$EscortDeck_InterfaceCallBack(choice); //display the menu again
});
}
return;
} else if (choice === "51_SPIN") {
if( w.$EscortDeckSpinModel ) w.$EscortDeckSpinModel = false;
else w.$EscortDeckSpinModel = true;
} else if (choice === "61_REFUEL") {
this.$EscortDeck_RefuelEscort(w.$EscortDeckShipData[w.$EscortDeckSelectedPad]);
} else if (choice === "71_HH") { //put ship into HyperHangar
w.$EscortDeck_HHStoreShipData(w.$EscortDeckShipData[w.$EscortDeckSelectedPad]);
player.consoleMessage(w.$EscortDeckShipData[w.$EscortDeckSelectedPad][0][3]+" from deck "+(w.$EscortDeckSelectedPad+1)+" stored into Hyperspace Hangar", 10);
w.$EscortDeckShipData[w.$EscortDeckSelectedPad] = [];
w.$EscortDeckI = false; //back to the first page
} else if (choice === "81_SALVAGE") {
//are you sure?
w.$EscortDeck_Salvage(w.$EscortDeckSelectedPad, player.ship);
w.$EscortDeckI = false; //back to the first page
}
w.$EscortDeck_InterfaceCallBack(choice); //display the menu again
}
//-----------------------------------------------------------------------------------------------//
this.$EscortDeck_InterfaceShipData = function _escortdeck_InterfaceShipData(d, noeq, all) {
if (!d || !d[1]) return("No data");
var psd = d[0][3]+"\n";//ship.displayName+
var equip="";
var s="";
s=d[3]; if(s) s=EquipmentInfo.infoForKey(s);//ship.forwardWeapon;
if (s!=null && s.equipmentKey !== "EQ_WEAPON_NONE") equip+="Forward "+s.name;
s=d[2]; if(s) s=EquipmentInfo.infoForKey(s)//ship.aftWeapon;
if (s!=null && s.equipmentKey !== "EQ_WEAPON_NONE") {
if (equip.length > 0) equip+=", "; //Anaconda has aft weapon only
equip+="Aft "+s.name;
}
s=d[4]; if(s) s=EquipmentInfo.infoForKey(s)//ship.portWeapon;
if (s!=null && s.equipmentKey !== "EQ_WEAPON_NONE") equip+=", Port "+s.name;
s=d[5]; if(s) s=EquipmentInfo.infoForKey(s)//ship.starboardWeapon;
if (s!=null && s.equipmentKey !== "EQ_WEAPON_NONE") equip+=", Starboard "+s.name;
if (equip.length > 0) equip+=""; //no newline, save space
else equip+="No Laser";
var eqs = "";
var savedEQ = d[7], counter;
if (!noeq && savedEQ) for (counter = 0; counter < savedEQ.length; counter++) {
if (savedEQ[counter][1] !== "EQUIPMENT_DAMAGED" && savedEQ[counter][0].indexOf("EQ_NUMERIC3_") === -1 && savedEQ[counter][0].indexOf("EQ_ADDHUD_") === -1 && savedEQ[counter][0].indexOf("EQ_XENONHUD_") === -1) {
s = EquipmentInfo.infoForKey(savedEQ[counter][0]);
if (s) {
var n = s.name;
if (n.length > 0) eqs += n + ", ";
}
}
}
eqs = eqs.substr(0, eqs.length - 2); //cut last comma
if (eqs.length > 0) equip+=", "+eqs+"\n";
eqs = "";
if (!noeq && savedEQ) for (counter = 0; counter < savedEQ.length; counter++) {
if (savedEQ[counter][1] === "EQUIPMENT_DAMAGED" && savedEQ[counter][0].indexOf("EQ_NUMERIC3_") === -1 && savedEQ[counter][0].indexOf("EQ_ADDHUD_") === -1 && savedEQ[counter][0].indexOf("EQ_XENONHUD_") === -1) {
s = EquipmentInfo.infoForKey(savedEQ[counter][0]);
if (s) {
var n = s.name;
if (n.length > 0) eqs += n + ", ";
}
}
}
eqs = eqs.substr( 0, eqs.length - 2 ); //cut last comma
if (eqs.length > 0) equip+="\nDamaged: "+eqs+"\n";
var desc = "";
var speed = d[0][19];
psd += "Fuel: "+Math.round(d[9]*10)/10+" ly ";
if (speed > 0) psd += "Speed: "+speed+" mLS ";
if (d[0][17] > 0) psd += "Thrust: "+d[0][17]+" ";
if (d[0][15] > 0) psd += "Pitch: "+Math.round(d[0][15]*100)/100+" ";
if (d[0][16] > 0) psd += "Roll: "+Math.round(d[0][16]*100)/100+" ";
if (d[0][14] > 0) psd += "Yaw: "+Math.round(d[0][14]*100)/100+" ";
var en = "";
var r = "";
if (all && d[0][20] > 0) en = "Energy+Shields: "+d[0][20]+" ";
else {
if (d[0][20] > 0) {
var eb = Math.max(1, Math.floor(d[0][20]/64));
en = "Energy+Shields";
en += ": "+eb+" ";
}
}
var er = d[0][21];
if (er > 0) {
var ers = "";
if (er < 2.5) ers = "Poor";
else if (er < 3.5) ers = "Medium";
else if (er < 4.5) ers = "Good";
else if (er < 10) ers = "Excellent";
else ers = "Extreme";
if (all) ers += " ("+er+")";
r = "Recharge: "+ers+" ";
}
if (d[0][6] > 0) {
var v = Math.round(d[0][6]/1333)*1000; //trade-in value is 75% of ship price
psd += "Value: "+formatCredits(v, false, true)+" ";
}
psd += "\n"+en+r;
//{usable:u, mass:ship.mass, x:b.x, y:b.y, z:b.z});//storedShipArray[14]
if (d[14]) psd += "Mass: "+Math.max(Math.round(d[14].mass/100)/10, 1)+ //min.1t
"t Size: "+Math.round(d[14].x)+"*"+
Math.round(d[14].y)+"*"+Math.round(d[14].z)+"m ";
// "m Radius: "+Math.round(ship.collisionRadius)+"m"+//" Sphere: "+sp+"m^2"+
psd += "\n"+equip;
psd = desc + psd;
return(psd);
}
//-----------------------------------------------------------------------------------------------//
this.$EscortDeck_isFreePadFor = function _escortdeck_isFreePadFor(eqKey, owner, context, w, mini) {
//called from escortship equipment conditions
//should check TooLarge but no ship nor mass,etc. data available
//due to shouldn't spawn ships in equipment conditions
//so must provide in the prefilled $EscortDeckEqs array for TooLarge3
var len = w.$EscortDeckPadPos.length;
for(var i = 0; i < len; i++ ) {
if( !w.$EscortDeckShipData[i] && //find the first empty pad where fit
( !w.$EscortDeck_isMiniPad(i, owner, w) || mini ) ) {
if( !w.$EscortDeckEqs || !w.$EscortDeckEqs[eqKey] ||
!w.$EscortDeck_TooLarge3( owner, w,
w.$EscortDeckEqs[eqKey][3],
w.$EscortDeckEqs[eqKey][0],
w.$EscortDeckEqs[eqKey][1],
w.$EscortDeckEqs[eqKey][2], i ) )
return true;
}
}
return false;
}
//-----------------------------------------------------------------------------------------------//
this.$EscortDeck_isLarge = function _escortdeck_isLarge(ship, w) { //check if the ship is over the large border
var m = ship.mass; //revert HardShips' double mass caused by density=2
if( ship.scriptInfo && ship.scriptInfo.hardarmour > 1 ) m = m / 2;
if( m >= w.$EscortDeckLargeMass ) return true;
return false;
}
//-----------------------------------------------------------------------------------------------//
this.$EscortDeck_isMini = function _escortdeck_isMini(ship) { //check if the ship is like an Adder or smaller
if( !ship || !ship.isValid ) return false;
var b = ship.boundingBox;
if( !b || ship.mass > this.$EscortDeckPad.mini[3]
|| b.x > this.$EscortDeckPad.mini[0]
|| b.y > this.$EscortDeckPad.mini[1]
|| b.z > this.$EscortDeckPad.mini[2] ) return false;
return true;
}
//-----------------------------------------------------------------------------------------------//
this.$EscortDeck_isMiniPad = function _escortdeck_isMiniPad(pad, owner, w) { //check if this pad is for mini ships only
if( w.$EscortDeckCWS && owner.dataKey.indexOf("carrier") > -1 ) {
if( w.$EscortDeckXL ) {
if( pad != 8 && pad != 9 ) return false;
} else if( pad != 7 && pad != 8 ) return false;
} else if( w.$EscortDeckXL || pad != 4 && pad != 5 ) return false;
return true;
}
//-----------------------------------------------------------------------------------------------//
this.$EscortDeck_isPadFree = function _escortdeck_isPadFree(pad, owner, w) { //is this pad free for this ship?
var s = null; //or the assigned ship is not valid or not in scanner range
if( w && w.$EscortDeckShip ) s = w.$EscortDeckShip[pad];
if( w && pad < w.$EscortDeckPadPos.length && ( !s || !s.isValid
// || owner && owner.isValid && owner.position.distanceTo(s.position) > owner.scannerRange
|| pad == w.$EscortDeckSelectedPad ) ) { //pad is selected to replace ship?
return(true);
}
return(false);
}
//-----------------------------------------------------------------------------------------------//
this.$EscortDeck_MakeShipData = function _escortdeck_MakeShipData(ship) {
var that = _escortdeck_MakeShipData;
var ssh = (that.ssh = that.ssh || worldScripts["Ship_Storage_Helper.js"]);
if (!ship || !ship.isValid) return null;
var storedShipArray = [];
var entry_time = Date.now();
if (ssh && ssh.storeCurrentShipToArray) {
storedShipArray = ssh.storeCurrentShipToArray(ship);
} else {
var stationAttributes;
if (ship.dockedStation) {
stationAttributes = [ship.dockedStation.primaryRole,ship.dockedStation.dataKey, ship.dockedStation.position];
} else {
stationAttributes = [null,null,null];
}
storedShipArray.push([galaxyNumber, system.ID, clock.seconds, ship.displayName,
system.name, stationAttributes, ship.price,
ship.shipClassName, ship.shipUniqueName, ship.AIScriptWakeTime, ship.beaconLabel,
ship.destinationSystem, ship.exhaustEmissiveColor, ship.homeSystem,
ship.maxYaw, ship.maxPitch, ship.maxRoll, ship.maxThrust, ship.thrust,
ship.maxSpeed, ship.maxEnergy, ship.energyRechargeRate, ship.cargoSpaceCapacity,
ship.injectorBurnRate, ship.injectorSpeedFactor,
ship.scanDescription, ship.hyperspaceSpinTime,
ship.scannerHostileDisplayColor1, ship.scannerHostileDisplayColor2,
ship.forwardShield, ship.maxForwardShield, ship.forwardShieldRechargeRate,
ship.aftShield, ship.maxAftShield, ship.aftShieldRechargeRate,
ship.passengerCapacity]); //storedShipArray[0]
storedShipArray.push(ship.dataKey); // storedShipArray[1]
if (!ship.aftWeapon) { // storedShipArray[2]
storedShipArray.push(ship.aftWeapon);
} else {
storedShipArray.push(ship.aftWeapon.equipmentKey);
}
if (!ship.forwardWeapon) { // storedShipArray[3]
storedShipArray.push(ship.forwardWeapon);
} else {
storedShipArray.push(ship.forwardWeapon.equipmentKey);
}
if (!ship.portWeapon) { // storedShipArray[4]
storedShipArray.push(ship.portWeapon);
} else {
storedShipArray.push(ship.portWeapon.equipmentKey);
}
if (!ship.starboardWeapon) { // storedShipArray[5]
storedShipArray.push(ship.starboardWeapon);
} else {
storedShipArray.push(ship.starboardWeapon.equipmentKey);
}
var missiles = [];
if( ship.missileCapacity > 0 ) { //bugfix of "Tried to init array with nil object" in Oolite 1.81
missiles = ship.missiles;
var counter;
for(counter = 0;counter<missiles.length;counter++)
missiles.splice(counter,1,missiles[counter].equipmentKey);
}
storedShipArray.push(missiles); // storedShipArray[6]
var equipment = ship.equipment;
var tempArray;
for(counter = 0;counter<equipment.length;counter++) {
tempArray = [equipment[counter].equipmentKey, ship.equipmentStatus(equipment[counter])];
equipment.splice(counter,1,tempArray);
}
storedShipArray.push(equipment); // storedShipArray[7]
storedShipArray.push(ship.cargoList); // storedShipArray[8]
storedShipArray.push(ship.fuel); //storedShipArray[9]
storedShipArray.push(new Array); //passengers is player only, storedShipArray[10]
var subEnts = new Array;
if (ship.subEntityCapacity > 0 && ship.subEntityCapacity !== ship.subEntities.length) {
// only store subEnts if at less than capacity
for (counter = 0;counter < ship.subEntities.length; counter++) {
subEnts.push([ship.subEntities[counter].dataKey,
ship.subEntities[counter].position.x,
ship.subEntities[counter].position.y,
ship.subEntities[counter].position.z]);
}
}
storedShipArray.push(subEnts);//storedShipArray[11]
if( ship == player.ship ) {
storedShipArray.push(ship.serviceLevel);//storedShipArray[12]
ship.script.$SSH_serviceLevel = ship.serviceLevel; //save for Carriers OXP
} else if( ship.script ) storedShipArray.push(ship.script.$SSH_serviceLevel);
storedShipArray.push(ship.entityPersonality);//storedShipArray[13]
var u = 0; //escortdeck specific data
if( ship.script && ship.script.$EscortDeckUsable > 0 )
u = ship.script.$EscortDeckUsable;
var b = ship.boundingBox; //mass for TooLarge and ship price, sizes needed in playerboughtnewship
storedShipArray.push({usable:u, mass:ship.mass, x:b.x, y:b.y, z:b.z});//storedShipArray[14]
}
if (this.$debug) log(this.name, "Serializing ship "+ship.displayName+" took "+(Date.now()-entry_time).toFixed(0)+"ms");
return(storedShipArray);
}
//-----------------------------------------------------------------------------------------------//
this.$EscortDeck_MassFactor = function _escortdeck_MassFactor(ship, w) { //compute thrust reduction due to extra mass in deck
var m = 0; //total mass of ships on deck
for(var i = 0; i < w.$EscortDeckPadPos.length; i++ ) {
var d = w.$EscortDeckShipData[i]; //inside stations must use the saved data array
var s = w.$EscortDeckShip[i];
if( ( d || s ) && w.$EscortDeckShipPos[i] ) { //there is a ship in this deck
if( d && d[14] && d[14].mass > 0 ) m += d[14].mass;
else if( s && s.mass > 0 ) m += s.mass;
else m += 50000; //guess
}
}
//in proportion with the original mass (max. 1)
return( ship.mass / ( ship.mass + m / 3 ) );
}
//-----------------------------------------------------------------------------------------------//
this.$EscortDeck_MFD = function _escortdeck_MFD(w) { //update MFD
var ps = player.ship; if (!ps || !ps.isValid) return;
var d = w.$EscortDeckShipData;
var mfd = "";
var j = 0;
var len = w.$EscortDeckPadPos.length;
if (w.$EscortDeckDockingTunnel) ; //show an empty MFD while the player travel in the tunnel
else if (ps.equipmentStatus("EQ_ESCORTDECK") != "EQUIPMENT_OK"
&& ps.equipmentStatus("EQ_ESCORTDECKXL") != "EQUIPMENT_OK")
mfd = "No Escort Deck\n";
else for ( var pad = 0; pad < len && j++ < 10; pad++) {
var s = w.$EscortDeckShip[pad];
var state = "";
if (w.$EscortDeckLocked[pad]) state = "LOCKED";
var l = (pad+1)+": ";
if (pad == w.$EscortDeckSelectedPad) l += "-> "; //mark selected pad
if (s && s.isValid) {
if (state.length == 0) state = s.AIState; //only if not locked
if (s.script && s.script.$EscortDeckUsable > 0) { //show for usables only
//l += s.shipUniqueName.substring(0,18)+" E"+Math.round(s.energy/64);
l += (s.shipUniqueName ? s.shipUniqueName.substring(0,18) : s.shipClassName.substr(0,18))+" E"+s.energy.toFixed(0);
//+"/"+Math.round(s.maxEnergy/64);
//first 18 char to fit into but show Sidewinder Special
//l += " E"+Math.round(s.energy);
// var r = "";
// var t = Math.round(s.maxEnergy/64); //total letters
// for(var k = 0; k < t; k++ )
// if( k < Math.round(s.energy/64) ) r += "E";
// else r += "-"; //empty bank
// l += " "+r;
//if( s.equipmentStatus("EQ_FUEL_INJECTION") == "EQUIPMENT_OK" )
l += " F"+Math.ceil(s.fuel-0.01);
if (s.forwardShield)
l += " S"+Math.ceil(s.forwardShield/64)+Math.ceil(s.aftShield/64);
if (s.forwardShield < 64 || s.aftShield < 64 )
log(this.name, "Escort "+s.displayName+" has low shields: "+s.forwardShield.toFixed(0)+"/"+s.aftShield.toFixed(0));
if (!w.$EscortDeckShipPos[pad]) //not in deck
l += " "+Math.floor(ps.position.distanceTo(s.position)/1000)+"km "+w.$EscortDeck_MFDFrom(s.position);
} else l += s.displayName+" ("+Math.round(s.mass/1000)+"t)";
mfd += this.$EscortDeck_MFDAlign(l, state)+"\n";
} else {
var x = 0, y = 0, z = 0, maxmass = 0;
var s = w.$EscortDeckPadSize;
if (s && s[0]) {
var e = null;
var len = s.length-1;
if (pad < 0 || pad > len) e = s[len]; //the last line contain the maximums
else e = s[pad];
if (e && e[0]) {
x = e[0];
y = e[1];
z = e[2];
maxmass = e[3];
}
}
l += "("+Math.floor(maxmass/1000)+"t, "+x+"x"+y+"x"+z+"m)";
if (state.length > 0) mfd += this.$EscortDeck_MFDAlign(l, state)+"\n";
else mfd += l+"\n";
}
}
var t = this.$EscortDeckToWS; //Towbar
if (t && j < 10 && !w.$EscortDeckDockingTunnel) {
var l = "T: ";
if (j == w.$EscortDeckSelectedPad) l += "-> "; //mark selected pad
var s = t.$TowbarShip;
if (s) {
l += s.displayName+" ("+Math.round(s.mass/1000)+"t)";
mfd += this.$EscortDeck_MFDAlign(l, (s.script && s.script.$EscortDeckUsable && s.script.$EscortDeckUsable > 0 ? "Usable" : "Salvage"))+"\n";
} else mfd += l+"("+Math.floor(ps.mass*1.6/1000)+"t)\n"; //max. mass on towbar
}
if (ps.setMultiFunctionText) ps.setMultiFunctionText(w.name, mfd, false);
}
//-----------------------------------------------------------------------------------------------//
this.$EscortDeck_MFDAlign = function _escortdeck_MFDAlign(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.$EscortDeck_MFDFrom = function _escortdeck_MFDFrom(whomposition) { //direction mark
var ship = player.ship;
if( !ship || !ship.isValid || !whomposition || !whomposition.dot ) return ("");
var v = ship.position.subtract(whomposition);
var fw = ship.vectorForward.angleTo(v);
var ri = ship.vectorRight.angleTo(v);
var up = ship.vectorUp.angleTo(v);
var s = ""; // refine if out of front 1 degree cone
if( ri < 1.56 ) s += "<";
if( up > 1.58 ) s += "^";
else if( up < 1.56 ) s += "v";
if( ri > 1.58 ) s += ">";
// if( s.length > 0 || fw < 1 ) //do not exclude the aft 1 degree cone
// s = Math.round( 180 * ( 1 - fw / Math.PI ) ) + "° " + s;
return( s );
}
//-----------------------------------------------------------------------------------------------//
this.$EscortDeck_PutShip = function _escortdeck_PutShip(deckinit, ship, pad, owner, msg, land, xch ) { //put ship into the pad
var that = _escortdeck_PutShip;
var w = (that.w = that.w || worldScripts.escortdeck);
var r = w.$EscortDeck_TooLarge2(ship, owner, w, pad);
if (r) return (r); //too large
if (ship && ship.isValid && owner && owner.isValid
&& (land || w.$EscortDeck_isPadFree(pad, owner, w))) { //landing back or new ship
if (!w.$EscortDeckShip) w.$EscortDeckShip = [];
var old = w.$EscortDeckShip[pad];
var sp = w.$EscortDeckShip.indexOf(ship); //ship pad if already owned
if (old && old.isValid && !xch) { //prevent recursion during exchange with old ship
if (sp > -1 && sp != pad) { //put an owned ship to other pad
//exchange with the old ship if old fit into the another pad
//if( w.$EscortDeck_PutShip(old, sp, owner, w, msg, true, true ) )
w.$EscortDeck_Release(owner, pad, w, //relock old if fit
w.$EscortDeckLandingDist-100, msg);
w.$EscortDeckSelectedPad = -1; //prevent lock to the new pad
owner.target = old; //for relock
} else if (ship != old)
w.$EscortDeck_Release(owner, pad, w, false, msg); //replace old ship
}
if (sp > -1) { //remove from the previous pad
var d = [], s = [];
for(var i = 0; i < w.$EscortDeckPadPos.length; i++ ) {
if (i == sp) {
s[i] = null;
d[i] = null;
} else {
s[i] = w.$EscortDeckShip[i];
d[i] = w.$EscortDeckShipData[i];
}
}
w.$EscortDeckShipData = d;
w.$EscortDeckShip = s;
}
w.$EscortDeckShip[pad] = ship;
if (!deckinit) {
w.$EscortDeckShipData[pad] = w.$EscortDeck_MakeShipData(ship);
if (w.$debug) log(w.name, (pad+1)+". "+JSON.stringify(w.$EscortDeckShipData[pad]));
}
var usable = 0;
if (ship.script && ship.script.$EscortDeckUsable > 0)
usable = ship.script.$EscortDeckUsable;
if (usable > 0 && ship.isDerelict) { //removing derelict flag need respawn
var ship2 = w.$EscortDeck_SpawnShip(pad, w, owner);
if (ship2 && ship2.isValid) {
ship.remove(true);
w.$EscortDeckShip[pad] = ship = ship2;
} //else fallback to the derelict ship
}
if (ship.AI != "EscortDeck_AI.plist") ship.switchAI("EscortDeck_AI.plist");
// var js = "escortdeck_escort";
// if( !ship.script || ship.script.name != js ) {
// var sl = 0; //must set usable and sl again after setScript
// if( ship.script ) sl = ship.script.$SSH_serviceLevel;
// ship.setScript(js+".js");
// if( ship.script ) {
// ship.script.$EscortDeckUsable = usable;
// ship.script.$SSH_serviceLevel = sl;
// }
// }
ship.script.$EscortDeckOnDeck = true;
if (usable > 0) {
log(this.name, "Adding escort "+ship.displayName+" with accuracy "+ship.accuracy+" to pad "+pad+", escortdeckUsable:"+ship.script.$EscortDeckUsable);
ship.AIState = "DECK";
if (!owner.group) { //escort group for performEscort AI command
owner.group = new ShipGroup();
owner.group.addShip(owner);
//from Oolite 1.84 the player can not be the leader of a group
if (owner != player.ship) owner.group.leader = owner;
}
if (owner && owner.group.leader != owner && owner != player.ship) owner.group.leader = owner;
if (!owner.group.containsShip(ship)) owner.group.addShip(ship);
} else ship.AIState = "SALVAGE";
ship.beaconCode = "E"+pad+" "+ship.name;
ship.bounty = 0;
ship.scanClass = "CLASS_CARGO"; //prevent masslock
ship.scannerDisplayColor1 = [0,0,0]; //black
//
var carrier = false;
if (w.$EscortDeckCWS && owner.dataKey.indexOf("carrier") > -1) carrier = true;
var p = w.$EscortDeckPadPos[pad];
if (!p) {
w.$EscortDeck_SetPads(owner, w);
p = w.$EscortDeckPadPos[pad];
if (!p) p = [0,70*(pad+1),0];
}
var sp = Vector3D(p[0], p[1], p[2]);
var x = ship.boundingBox.x * 0.5;
var y = ship.boundingBox.y * 0.5;
var z = ship.boundingBox.z * 0.5;
if (w.$EscortDeckXL && (!carrier || pad > 3 && pad < 8)) {
var left = -0.5; //left side of the xl deck
if( Math.round(pad/2) != pad/2 ) left = 0.5; //right side
sp = sp.add(Vector3D(left * 2 * z, 0, 0));
} else {
var v = w.$EscortDeckPadVec[pad];
if (!v) v = [0,1,0];//up
var vx = v[0] * x;
if (!carrier && y > 12) y = 12 - ( y - 12 ); //sunk if taller than 24m
var vy = v[1] * y;
var vz = v[2] * z;
sp = sp.add(Vector3D(vx, vy, vz));
}
if (owner.addCollisionException) owner.addCollisionException(ship); //from v1.81
else if(carrier) { //must move up to avoid kill by Injector speed
var a = y + 25;
if (w.$EscortDeck_isMiniPad(pad, owner, w)) a = 2 * x + 30;
sp = sp.add(Vector3D(0, a, 0));
}
if (sp.magnitude() > 500) ship.position = sp; //prevent terminated by Witchpoint Beacon
if (owner != player.ship && ship.script) {
if (!ship.script.$EscortDeckShipPos)
ship.script.$EscortDeckShipPos = [];
ship.script.$EscortDeckShipPos[pad] = sp; //NPC
} else w.$EscortDeckShipPos[pad] = sp; //player ship
//ship.orientation = owner.orientation;
ship.velocity = owner.velocity;
//slower steering and acceleration in proportion with the mass on deck
w.$EscortDeck_SetMaxs(owner, w);
if (ship.script) {
ship.script.$Towbar_TimedBombCounter = -1; //disarm timed bomb
if (ship.script.$TowbarUsableShip)
ship.script.$TowbarUsableShip = null;
}
if (owner == player.ship) {
if (msg) {
var m = ship.displayName+" locked on deck "+(pad+1);
player.consoleMessage(m, 10);
if (w.$debug) log(w.name, m); //debug
w.$EscortDeckSound.play();
}
}
return false;
}
return("no room");
}
//-----------------------------------------------------------------------------------------------//
this.$EscortDeck_Release = function _escortdeck_Release(owner, pad, w, dist, nomsg) {
if( !dist ) var dist = w.$EscortDeckLandingDist + 500; //drop distance over lock range
var s = w.$EscortDeckShip[pad];
if( s && s.isValid ) {
// if (s.script.$EscortDeckUsable < 0) s.script.$EscortDeckUsable = 0;
//move the old ship backward, over the relock range if on deck
if( w.$EscortDeckShipPos[pad] ) {
var sp = s.position.add(player.ship.heading.multiply(-dist));
if( sp.magnitude() > 500 ) s.position = sp; //prevent terminated by Witchpoint Beacon
w.$EscortDeckSound.play();
}
if( owner.removeCollisionException ) //from v1.81
owner.removeCollisionException(s);
w.$EscortDeckShipData[pad] = null;
w.$EscortDeckShip[pad] = null;
w.$EscortDeckShipPos[pad] = null;
s.script.$EscortDeckOnDeck = false;
if(!nomsg && s.dataKey.indexOf("carrier-player") == -1 ) //no msg for carrier
player.consoleMessage( s.displayName+" released from deck "+(pad+1), 10 );
}
}
//-----------------------------------------------------------------------------------------------//
this.$EscortDeck_RestoreShipData = function _escortdeck_RestoreShipData(s, d, w, owner) {
var that = _escortdeck_RestoreShipData;
var ssh = (that.ssh = that.ssh || worldScripts["Ship_Storage_Helper.js"]);
if (s && s.isValid && d && d[0]) {
var entry_time = Date.now();
if (!s.script) s.setScript("escortdeck_escort.js");
if (ssh && ssh.restoreStoredShipFromArray) {
ssh.restoreStoredShipFromArray(d, s);
} else {
if( d[0][7] ) s.shipClassName = d[0][7];
if( d[0][8] ) s.shipUniqueName = d[0][8];
if( s != player.ship ) s.displayName = d[0][3];
if(d[2]) s.aftWeapon = EquipmentInfo.infoForKey(d[2]);
if(d[3]) s.forwardWeapon = EquipmentInfo.infoForKey(d[3]);
if(d[4]) s.portWeapon = EquipmentInfo.infoForKey(d[4]);
if(d[5]) s.starboardWeapon = EquipmentInfo.infoForKey(d[5]);
//set new writable ship properties in Oolite 1.82
var oolite182 = false;
if (0 < oolite.compareVersion("1.82")) { ; }
else oolite182 = true;
if( oolite182 ) { //these are not writable before Oolite 1.82
var a = d[0];
if( a[9] ) s.AIScriptWakeTime = a[9];
if( a[11] ) s.destinationSystem = a[11];
if( a[12] ) s.exhaustEmissiveColor = a[12];
if( a[13] ) s.homeSystem = a[13];
if( a[14] ) s.maxYaw = a[14];
if( a[15] ) s.maxPitch = a[15];
if( a[16] ) s.maxRoll = a[16];
if( a[17] ) s.maxThrust = a[17];
if( a[18] ) s.thrust = a[18];
if( a[19] ) s.maxSpeed = a[19];
if( a[20] > 0 ) s.maxEnergy = a[20];
if( a[20] > 0 ) {
s.maxEnergy = a[20];
if(s.energy < s.maxEnergy ) s.energy = s.maxEnergy;
}
if( a[21] > 0 ) s.energyRechargeRate = a[21];
if( a[22] ) s.cargoSpaceCapacity = a[22];
if( a[23] ) s.injectorBurnRate = a[23];
if( a[24] ) s.injectorSpeedFactor = a[24];
if( a[25] ) s.scanDescription = a[25];
if( a[26] ) s.hyperspaceSpinTime = a[26];
if( a[27] ) s.scannerHostileDisplayColor1 = a[27];
if( a[28] ) s.scannerHostileDisplayColor2 = a[28];
if( s == player.ship ) {
if( a[29] ) s.forwardShield = a[29];
if( a[30] ) s.maxForwardShield = a[30];
if( a[31] ) s.forwardShieldRechargeRate = a[31];
if( a[32] ) s.aftShield = a[32];
if( a[33] ) s.maxAftShield = a[33];
if( a[34] ) s.aftShieldRechargeRate = a[34];
} else if( a[10] ) s.beaconLabel = a[10]; //read-only for player
}
var counter, counter1;
var equipment = s.equipment;
//remove all equipments
var r = [];
for(counter = 0;counter<equipment.length;counter++)
r.push(equipment[counter].equipmentKey);
for(counter = 0;counter<r.length;counter++) //separated to avoid problems during eq array shortening
s.removeEquipment(r[counter]);
// if(w.$debug) log(w.name, "RestoreShipData2 d:"+d);
// if(w.$debug) log(w.name, "RestoreShipData2e "+equipment);
var tempArray = [];
var savedEQ = d[7];
for(counter = 0; counter < savedEQ.length; counter++) {
tempArray.push(savedEQ[counter][0]);
}
for(counter = 0; counter < equipment.length; counter++) {
var e = equipment[counter].equipmentKey;
var index = tempArray.indexOf(e);
if( index == -1 ) s.removeEquipment(e);
else if( savedEQ[index][1] == "EQUIPMENT_DAMAGED" )
s.setEquipmentStatus( e, "EQUIPMENT_DAMAGED" );
}
var depth = 0;
var retryArray = []; //try again if a required equipment comes later
retryArray[depth] = savedEQ;
do {
var added = false;
retryArray[depth+1] = [];
for(counter = 0; counter < retryArray[depth].length; counter++) {
var e = retryArray[depth][counter][0]; //award if exists and has not
var ed = retryArray[depth][counter][1];
if( EquipmentInfo.infoForKey(e) ) {
if( !s.awardEquipment(e) ) //retry next time if failed
retryArray[depth+1].push([e,ed]);
else {
added = true;
if( ed == "EQUIPMENT_DAMAGED" )
s.setEquipmentStatus( e, "EQUIPMENT_DAMAGED" );
}
}
}
depth++;
// if(w.$debug) log(w.name, "RestoreShipData3 depth:"+depth+" retryArray:"+retryArray[depth]);
} while( retryArray[depth].length > 0 && added && depth < 100 ) //until no more added eq
/* old code lose equipments due to make changes in the original d array
for(counter = 0; counter<equipment.length; counter++) {
tempArray = [equipment[counter].equipmentKey,
s.equipmentStatus(equipment[counter])];
for(counter1 = 0; counter1<savedEQ.length; counter1++) {
if (savedEQ[counter1][0] === tempArray[0]) {
tempArray.push(true);
tempArray.push(counter1);
if (savedEQ[counter1][1] === "EQUIPMENT_DAMAGED")
s.setEquipmentStatus(tempArray[0],
"EQUIPMENT_DAMAGED");
break;
}
}
if (!tempArray[2]) {
s.removeEquipment(tempArray[0]);
continue;
} else savedEQ.splice(tempArray[3],1);
}
for(counter = 0; counter<savedEQ.length; counter++) {
if(EquipmentInfo.infoForKey(savedEQ[counter][0])) {
s.awardEquipment(savedEQ[counter][0]);
if(savedEQ[counter][1] === "EQUIPMENT_DAMAGED")
s.setEquipmentStatus(savedEQ[counter][0],savedEQ[counter][1]);
}
}*/
// if(w.$debug) log(w.name, "RestoreShipData3 d[7]:"+d[7]);
// if(w.$debug) log(w.name, "RestoreShipData3e "+s.equipment);
//ship.cargoList is read-only and no ship.manifest so d[8] is skipped
s.fuel = d[9];
//passengers is player only, so d[10] is skipped
var subEnts = d[11]; //will only exist if less subEnts than capacity when ship was stored.
if (subEnts && subEnts.length>0) {
var currentSubEnts = s.subEntities;
for (counter = 0; counter<currentSubEnts.length; counter++) {
currentSubEnts[counter].toBeRemoved = true;
for(counter1 = 0; counter1<subEnts.length; counter1++) {
if(subEnts[counter1][0] === currentSubEnts[counter].dataKey &&
subEnts[counter1][1] === currentSubEnts[counter].position.x &&
subEnts[counter1][2] === currentSubEnts[counter].position.y &&
subEnts[counter1][3] === currentSubEnts[counter].position.z )
delete currentSubEnts[counter].toBeRemoved;
}
if (currentSubEnts[counter].toBeRemoved) {
delete currentSubEnts[counter].toBeRemoved;
currentSubEnts[counter].remove(true);
}
}
}
//serviceLevel is player only, NPCs store it in script variable
if( d[12] > 0 ) {
if( s == player.ship && d[12] ) s.serviceLevel = d[12];
else if( s.script ) s.script.$SSH_serviceLevel = d[12]; //must set after setScript
}
if (oolite.compareVersion("1.80") < 0) {// can only do this in Oolite 1.81 or greater
s.entityPersonality = d[13];
}
if( s.script && d[14] )
//s.script.$EscortDeckUsable = d[14].usable; //must set after setScript
s.script.$EscortDeckUsable = 1; //must set after setScript
// if(w.$debug) log(w.name, "missileCapacity:"+s.missileCapacity+" d[6]:"+d[6]);
if( s.missileCapacity > 0 ) {
//do missiles at the end due to there is a core bug in Oolite 1.80
//when land back to the carrier from an Asp which has max_missiles=0:
//Error: Native exception: Tried to init array with nil object
var missiles = [];
if( s.missiles && s.missiles.length > 0 ) {
missiles = s.missiles;
for(counter = 0;counter<missiles.length;counter++) {
s.removeEquipment(missiles[counter]);
}
}
missiles = d[6];
if( missiles && missiles.length > 0 ) {
for(counter = 0; counter<missiles.length; counter++)
if(EquipmentInfo.infoForKey(missiles[counter]))
s.awardEquipment(missiles[counter]);
}
}
}
if (s.script && s.script.$EscortDeckUsable === 1) {
var js = "escortdeck_escort";
if (!s.script || s.script.name != js) {
// sanity check only, since we set ship.script as soon as we spawn it
var sl = 0; //must set usable and sl again after setScript
if (s.script) sl = s.script.$SSH_serviceLevel;
s.setScript(js+".js");
if (s.script) {
s.script.$EscortDeckUsable = 1;
s.script.$SSH_serviceLevel = sl;
}
}
// make sure the escorts can use LaserReductor if they have it - if the player wants derelicts, they will have it!
s.script.shipAttackedOther = this.shipAttackedOther;
s.script.shipKilledOther = this.shipKilledOther;
// if running HarderWay, attach SolarWind to ship script if it hasn't been already to set fuel scooping/consumption
if (worldScripts.SolarWind && typeof s.script.$debugHarderWay === 'undefined') worldScripts.SolarWind.shipSpawned(s);
s.script.$debugHarderWay = true; // see log messages form the escorts in Latest.log
// nobody would hire a novice as a escort pilot, so take out the lower accuracy range
if (s.accuracy == null || s.accuracy < 6.1) s.accuracy = 6.1 + 4 * Math.random();
}
var now = Date.now();
if (this.$debug) log(this.name, "Deserializing ship "+s.displayName+" took "+(now-entry_time).toFixed(0)+"ms");
}
if (this.$debug) log(this.name, s.displayName+": shipRestore returns");
return (s);
}
//-----------------------------------------------------------------------------------------------//
this.$EscortDeck_RollBack = function _escortdeck_RollBack(eq, msg) {
var f = 1;
if( player.ship && player.ship.dockedStation ) {
var e = player.ship.dockedStation.equipmentPriceFactor;
if( e > 0 ) f = e;
}
player.credits += f * EquipmentInfo.infoForKey(eq).price/10;
player.consoleMessage(msg, 5);
}
//-----------------------------------------------------------------------------------------------//
this.$EscortDeck_Salvage = function _escortdeck_Salvage( pad, owner ) { //salvage the escort on the given pad
var that = _escortdeck_Salvage;
var w = (that.w = that.w || worldScripts.escortdeck);
var d = null;
if( w.$EscortDeckShipData ) d = w.$EscortDeckShipData[pad];
var s = null;
if( w.$EscortDeckShip ) s = w.$EscortDeckShip[pad];
var sn = "escort";
var t = w.$EscortDeckToWS;
if( t ) {
var s = w.$EscortDeck_SpawnShip( pad, w, owner );
if (s && s.isValid) {
if (w.$debug) log(w.name, "Using Towbar_Payout to salvage "+s.displayName);
t.$Towbar_Payout(s); //call payout in towbar oxp
}
} else { //pay salvage price like in towbar
var cr = w.$EscortDeck_SalvagePadPrice(pad); //salvage payment
log(w.name, "Salvage "+pad+". pad for "+cr+"cr: "+d);
if( s && s.isValid ) sn = s.shipClassName; //must be short name to fit into a line
else {
if( !d || !d[1] ) return false; //no ship in this pad
sn = d[0][7]; //shipClassName
}
player.consoleMessage("You got "+cr+" Cr for salvaging "+sn, 10);
player.credits += cr;
w.$EscortDeckSound2.play();
}
if( s && s.isValid ) s.remove(true);
w.$EscortDeckShipData[pad] = null;
w.$EscortDeckShip[pad] = null;
}
//-----------------------------------------------------------------------------------------------//
this.$EscortDeck_Salvage2 = function _escortdeck_Salvage2(pad, owner) { //salvage or save into Hyperspace Hangar
var that = _escortdeck_Salvage2;
var w = (that.w = that.w || worldScripts.escortdeck);
var hh = (that.hh = that.hh || worldScripts.HyperspaceHangar);
if (hh && w.$EscortDeckShipData[pad]) {
if (w.$EscortDeckShipData[pad][0])
player.consoleMessage(w.$EscortDeckShipData[pad][0][3]+" from deck "+(pad+1)+" stored into Hyperspace Hangar", 10);
w.$EscortDeck_HHStoreShipData(w.$EscortDeckShipData[pad]);
w.$EscortDeckShipData[pad] = [];//clear this deck
} else w.$EscortDeck_Salvage(pad, owner); //salvage the escort on the given pad
}
//-----------------------------------------------------------------------------------------------//
this.$EscortDeck_SalvageBadEscorts = function _escortdeck_SalvageBadEscorts(owner) {
var that = _escortdeck_SalvageBadEscorts;
var w = (that.w = that.w || worldScripts.escortdeck);
if (!owner) return;
var oldlength = w.$EscortDeckPadPos.length; //save the old length before SetPads when sold a Carrier
w.$EscortDeck_SetPads( owner, w );
if (owner.mass < w.$EscortDeckMinMass) { //bought a too small ship
owner.removeEquipment("EQ_ESCORTDECK");
owner.removeEquipment("EQ_ESCORTDECKXL");
for(var i = 0; i < oldlength; i++ ) //salvage all escorts
w.$EscortDeck_Salvage2(i, owner); //or save into Hyperspace Hangar
} else if (!w.$EscortDeck_isLarge(owner, w)) { //no side pads
w.$EscortDeck_Salvage2(4, owner); //salvage or save left adder
w.$EscortDeck_Salvage2(5, owner); //salvage or save right adder
}
if (owner.mass < w.$EscortDeckXLMass) { //bought a non-xl ship
if (owner.equipmentStatus("EQ_ESCORTDECKXL") == "EQUIPMENT_OK") {
owner.removeEquipment("EQ_ESCORTDECKXL"); //downgrade
owner.awardEquipment("EQ_ESCORTDECK");
}
}
if (owner.equipmentStatus("EQ_ESCORTDECKXL") == "EQUIPMENT_OK") w.$EscortDeckXL = true;
else w.$EscortDeckXL = false; //must set XL for the following TooLarge call
for(var i = 0; i < oldlength; i++ ) {
var t = null;
if (w.$EscortDeckShip) t = w.$EscortDeckShip[i]; //the ship on this pad
if (i >= w.$EscortDeckPadPos.length || //salvage too much decks when bougth a smaller ship
w.$EscortDeck_TooLarge2(t, owner, w, i)) //can use ShipData array if no ship
w.$EscortDeck_Salvage2(i, owner); //salvage too large escorts or save into HH
}
}
//-----------------------------------------------------------------------------------------------//
this.$EscortDeck_SalvagePadPrice = function _escortdeck_SalvagePadPrice(pad) { //salvage price of an escort on this pad
var that = _escortdeck_SalvagePadPrice;
var w = (that.w = that.w || worldScripts.escortdeck);
var s = null;
var t = w.$EscortDeckToWS;
if (t) return null; // Towbar_Payout will be used to salvage, we don't know the salvage price
if (w.$EscortDeckShip) s = w.$EscortDeckShip[pad]; //the ship on this pad if player is not docked
if (s && s.isValid) return(w.$EscortDeck_SalvageShipPrice(s, w));
var d = null;
if (w.$EscortDeckShipData) d = w.$EscortDeckShipData[pad]; //inside stations must use the saved data array
if (!d || !d[1]) return(0); //no price for a nonexistent ship
var eq = [];
for (var i = 0; i < d[7].length; i++) {
var ep = 0;
var e = EquipmentInfo.infoForKey(d[7][i][0]);
if (e) ep = e.price;
eq[i] = {price:ep};
if (w.$debug) log(w.name, i+". "+e+" fullprice:"+eq[i].price); //debug
}
var mass = 30000;//guess
if (d[14] && d[14].mass > 0) mass = d[14].mass;
return(w.$EscortDeck_SalvagePrice(mass, eq, d[3], d[2], w));
}
//-----------------------------------------------------------------------------------------------//
this.$EscortDeck_SalvagePrice = function _escortdeck_SalvagePrice( mass, eq, fw, af, w ) {//price like in towbar OXP
var hull = Math.round( 32.8 * Math.min(80, Math.round( //1t alloys = 32.8cr in average
0.3 * mass * 0.001 )) * 0.1 ) * 10 //alloys from the hull
var comp = 0; if( mass > 30000 ) comp = 84; //price of 1t computers
var eqcr = 0; //payment for all equipments
var rnd = 0.5;//Math.random(); - removed randomness due to repeated price listing in interface
var i = -1;
function weapon_value(eqKey) {
var value = 0;
if (eqKey) {
var eqInfo = EquipmentInfo.infoForKey(eqKey);
if (eqInfo) {
value = Math.round(eqInfo.price/10*(0.2+rnd*0.2)/10)*10;
}
}
return value;
}
while( eq[++i] ) {
var p = eq[i].price * 0.04; //20% of the original cost in credits (not in credits*10)
if( p > 300 ) {
// rnd = rnd * ( 300 / p ); //costly eqs always damaged - removed, already cheap
eqcr += Math.round( Math.max( 50, Math.round(p * rnd ))/10)*10; //min. 50cr
} else eqcr += Math.round( Math.max( 10,
Math.round( p * rnd ))/10)*10; //at least 10cr
}
//payment for fw and aft weapons, min. 20%, max 40% of original price, fixed rnd mean fix 30%
var fc = weapon_value(fw);
var ac = weapon_value(af);
var cr = hull + comp + eqcr + fc + ac; //all payments but nothing for cargo
return( cr ); //* 0.5 ); //deduct 50% commission - removed, an escort already cheap
}
//-----------------------------------------------------------------------------------------------//
this.$EscortDeck_SalvageShipPrice = function _escortdeck_SalvageShipPrice( ship, w ) { //salvage price of this ship
return( w.$EscortDeck_SalvagePrice( ship.mass, ship.equipment,
ship.forwardWeapon, ship.aftWeapon, w ) );
}
//-----------------------------------------------------------------------------------------------//
this.$EscortDeck_SaveMaxs = function _escortdeck_SaveMaxs( ship, w ) {
if(ship)
w.$EscortDeckMaxs = {
maxThrust:ship.maxThrust,
maxPitch:ship.maxPitch,
maxRoll:ship.maxRoll,
maxYaw:ship.maxYaw
};
else w.$EscortDeckMaxs = { maxThrust:32, maxPitch:1, maxRoll:2, maxYaw:1 };
}
//-----------------------------------------------------------------------------------------------//
this.$EscortDeck_RestoreMaxs = function _EscortDeck_RestoreMaxs(ship, w) {
var stored_max = w.$EscortDeckMaxs;
if (stored_max) {
ship.thrust = stored_max.maxThrust;
ship.maxPitch = stored_max.maxPitch;
ship.maxRoll = stored_max.maxRoll;
ship.maxYaw = stored_max.maxYaw;
}
}
//-----------------------------------------------------------------------------------------------//
this.$EscortDeck_SetMaxs = function _escortdeck_SetMaxs( ship, w ) {
var old_f = this.$EscortDeckMassFactor;
if( !ship || !ship.addCollisionException //need Oolite v1.81
|| !w.$EscortDeckMassEffect ) return;
var f = w.$EscortDeck_MassFactor(ship, w);
// var m = w.$EscortDeckMaxs; //original maximums of player ship
ship.thrust *= f / old_f;
ship.maxPitch *= f / old_f;
ship.maxRoll *= f / old_f;
ship.maxYaw *= f / old_f;
this.$EscortDeckMassFactor = f;
}
//-----------------------------------------------------------------------------------------------//
this.$EscortDeck_SetPads = function _escortdeck_SetPads(owner, w) {
var carrier = false;
if (w.$EscortDeckCWS && owner.dataKey.indexOf("carrier") > -1) carrier = true;
var p = w.$EscortDeckLPos;
var v = w.$EscortDeckLVec;
var xl = "escortdeck";
var m, s = [], si, x, y, z, maxmass;
if (owner && owner.isValid) {
if (owner.equipmentStatus("EQ_ESCORTDECKXL") === "EQUIPMENT_OK") {
w.$EscortDeckXL = true;
} else w.$EscortDeckXL = false;
if (carrier) {
xl = null; //use the carrier's hull as deck
if (w.$EscortDeckXL) { //XL deck on carrier
p = w.$EscortDeckCXPos;
v = w.$EscortDeckCXVec;
si = w.$EscortDeckCXSize;
} else { //normal deck on carrier
p = w.$EscortDeckCPos;
v = w.$EscortDeckCVec;
si = w.$EscortDeckCSize;
}
for (var i = 0; i < p.length; i++) {
var siz = si[i];
if( siz ) {
x = siz[0];
y = siz[1];
z = siz[2];
maxmass = siz[3];
} else x = y = z = maxmass = 0;
s[i] = [x, y, z, maxmass];
if (i == 0) m = [x, y, z, maxmass];
else {
m[0] = Math.max(x, m[0]);
m[1] = Math.max(y, m[1]);
m[2] = Math.max(z, m[2]);
m[3] = Math.max(maxmass, m[3]);
}
}
s.push(m); //the last item contain the maximums of size values
} else if (w.$EscortDeckXL) { //XL deck on non-carrier
xl += "xl";
p = w.$EscortDeckXLPos;
v = w.$EscortDeckXLVec;
x = w.$EscortDeckPad.xl[0];
y = w.$EscortDeckPad.xl[1];
z = w.$EscortDeckPad.xl[2];
maxmass = w.$EscortDeckPad.xl[3]; //60t
for (var i = 0; i <= p.length; i++) { // <= due to all equal
s[i] = [x, y, z, maxmass]; //so the maximums are the same
}
} else { //normal deck on non-carrier
if (owner.mass >= w.$EscortDeckWMass) {
xl += "w";
p = w.$EscortDeckWPos;
v = w.$EscortDeckWVec;
}
if (owner.mass >= w.$EscortDeckWMass) { //wide pad
x = w.$EscortDeckPad.wide[0];
y = w.$EscortDeckPad.wide[1];
z = w.$EscortDeckPad.wide[2];
maxmass = w.$EscortDeckPad.wide[3];
} else { //normal escort pad (non-wide)
x = w.$EscortDeckPad.escort[0];
y = w.$EscortDeckPad.escort[1];
z = w.$EscortDeckPad.escort[2];
maxmass = w.$EscortDeckPad.escort[3];
}
maxmass = Math.min(owner.mass * 0.4, maxmass); //max. 40% of the owner's mass
// maxmass = Math.min(owner.mass * 0.5, maxmass); //max. 50% of the owner's mass
maxmass = Math.min(w.$EscortDeckLargeMass, maxmass); //max. 130t
for ( var i = 0; i <= p.length; i++) {
if (w.$EscortDeck_isMiniPad(i, owner, w))
s[i] = [w.$EscortDeckPad.mini[0],
w.$EscortDeckPad.mini[1],
w.$EscortDeckPad.mini[2],
w.$EscortDeckPad.mini[3]];
else s[i] = [x, y, z, maxmass];
}
}
}
var len = p.length;
if (owner && owner.isValid && !w.$EscortDeck_isLarge(owner, w))
len = 4; //without adder pads
w.$EscortDeckPadPos = []; //landing pad positions
w.$EscortDeckPadSize = []; //landing pad sizes
w.$EscortDeckPadVec = []; //vectors from landing pad to ship center
for (var i = 0; i < len; i++) { //fill up from the proper array
w.$EscortDeckPadPos[i] = p[i];
w.$EscortDeckPadSize[i] = s[i];
w.$EscortDeckPadVec[i] = v[i];
}
w.$EscortDeckPadSize[len] = s[s.length-1]; //the last item contain the maximums of size values
log(w.name, "Pads Size:"+JSON.stringify(w.$EscortDeckPadSize)+", wide:"+JSON.stringify(w.$EscortDeckPad.wide));
return(xl);
}
//-----------------------------------------------------------------------------------------------//
this.$EscortDeck_Show = function _escortdeck_Show(owner) { //create the deck and escorts
var that = _escortdeck_Show;
var w = (that.w = that.w || worldScripts.escortdeck);
var xl = "notcarrier"; //carriers set xl to null
if ((owner.equipmentStatus("EQ_ESCORTDECK") === "EQUIPMENT_OK"
|| owner.equipmentStatus("EQ_ESCORTDECKXL") === "EQUIPMENT_OK")
&& owner.mass >= w.$EscortDeckMinMass) {
xl = w.$EscortDeck_SetPads(owner, w); //return null for Carriers
if (!w.$EscortDeckV && xl) {
var r = system.addVisualEffect(xl, [0,0,0]);
if (r) w.$EscortDeckV = r; //position and orientation will be set in FCB
}
//make the z offset of deck position from the center of the owner ship
var zv = owner.weaponPositionAft;//to be able to fire backward if the deck is solid
if (!zv.z) zv = zv[0]; //fix for the change of weaponPositions in Oolite 1.83.
zv.x = 0;
var hl = owner.boundingBox.z * 0.5 + 30;
if (zv.z < hl) zv.z = -hl; //correct z position for carriers
//if there is an aft Sniper Gun on the ship then move the deck nearer
var sg = w.$EscortDeckSGWS;
if (sg) {
var sub = sg._checkAftSubEnt(owner, "snipergun", sg);
if (sub >= 0) {
var e = owner.exhausts;
if (e && e[0] && e[0].position) {
if (w.$debug) log(w.name, "zvz:"+zv.z+" ez:"+(e[0].position.z-3));
zv.z = e[0].position.z - 3; //3m after engine
} else { //no engine on this ship, like Nyoka
var subh = sg._checkAftSubEnt(owner, "snipergun-heavy", sg);
if (subh >= 0) { //Heavy Sniper Gun, move about 50m
var g = owner.subEntities[subh];
var subz = g.position.z - g.boundingBox.z;
if (subz < zv.z) zv.z += g.boundingBox.z - 10;
} else { //normal Sniper Gun, move about 20m
var g = owner.subEntities[sub];
var subz = g.position.z - g.boundingBox.z;
if (subz < zv.z) zv.z += g.boundingBox.z - 10;
}
if (w.$debug) log(w.name, "subz:"+subz+" zvz:"+zv.z);
}
}
}
if (zv.y > 0) zv.y = 0; //do not raise over the center line
if (owner == player.ship) w.$EscortDeckZV = zv;
else { //NPC deck
if (!owner.script) owner.script = "oolite-default-ship-script.js"; //for sure
owner.script.$EscortDeckZV = zv;
}
//spawn ships based on $EscortDeckShipData
var b = system.shipsWithPrimaryRole("buoy-witchpoint"); //avoid instant collision after hyperjump
for (var i = 0; i < w.$EscortDeckPadPos.length; i++) { //fill up ShipPos array
var s = w.$EscortDeck_SpawnShip(i, w, owner);
if (s && s.isValid) {
if (b && b[0] && b[0].addCollisionException) //from v1.81
b[0].addCollisionException(s);
var r = w.$EscortDeck_PutShip(true, s, i, owner);
if (w.$debug) log(w.name, (i+1)+". spawn "+s+" r:"+r); //debug
}
}
}
w.$EscortDeck_MFD(w); //update MFD
if ((w.$EscortDeckV || !xl) && !isValidFrameCallback(w.$EscortDeckFCB))
w.$EscortDeckFCB = addFrameCallback(w.$EscortDeck_FCB);
}
//-----------------------------------------------------------------------------------------------//
this.$EscortDeck_SpawnOne = function _escortdeck_SpawnOne(role, w, isShip) {
var s = system.addShips(role, 1, [10000000,0,0], 5000);
// set script as soon as possible, hopefully before any OXP's shipSpawned event handler set any property in th ship's script
if (isShip && s && s[0]) s[0].setScript("escortdeck_escort.js");
return( s );
}
//-----------------------------------------------------------------------------------------------//
this.$EscortDeck_SpawnShip = function _escortdeck_SpawnShip(pad, w, owner) {
var s = null;
var d = w.$EscortDeckShipData[pad];
if (d && d[1]) {
if (w.$EscortDeckCWS && d[14] && d[14].usable > 0
&& w.$EscortDeckPlayableDataKeys.indexOf(d[1]) === -1) {
var n = d[1]+"-player"; //try the player variant for Carriers
if (w.$EscortDeckPlayableDataKeys.indexOf(n) > -1) d[1] = n;
else {
var e = d[1].indexOf("-trader"); //try without suffix
if (e == d[1].length-(e.length)) {
n = d[1].substr(0, e);
if (w.$EscortDeckPlayableDataKeys.indexOf(n) > -1)
d[1] = n;
else {
n += "-player"; //try replace -trader to -player
if (w.$EscortDeckPlayableDataKeys.indexOf(n) > -1)
d[1] = n;
}
} else {
var e = d[1].indexOf("-pirate"); //try without suffix
if (e == d[1].length-(e.length)) {
n = d[1].substr(0, e);
if (w.$EscortDeckPlayableDataKeys.indexOf(n) > -1)
d[1] = n;
else {
n += "-player"; //try replace to -player
if(w.$EscortDeckPlayableDataKeys.indexOf(n) > -1) d[1] = n;
}
}
}
}
}
s = w.$EscortDeck_SpawnOne("["+d[1]+"]", w, true);
if (s && s[0]) {
if (s.script) s.script.$ShipVersionIgnore = true;
if (w.$debug) log(w.name, "Spawn escort pad:"+pad+" d:"+d+" s:"+s);
s = w.$EscortDeck_RestoreShipData(s[0], d, w, owner);
//setup restore in FCB also, need after ShipVersion changes in shipSpawned
w.$EscortDeckSpawnedShips.push(s);
}
}
return(s);
}
//-----------------------------------------------------------------------------------------------//
this.$EscortDeck_TooLarge = function _escortdeck_TooLarge(ship, owner, w) { //size or mass is too large to this deck?
return(w.$EscortDeck_TooLarge2(ship, owner, w, -1));
}
//-----------------------------------------------------------------------------------------------//
this.$EscortDeck_TooLarge2 = function _escortdeck_TooLarge2(ship, owner, w, pad) { //use saved data if no ship
if( !ship || !ship.isValid ) {
if( pad < 0 ) return("invalid escort ship");
var d = w.$EscortDeckShipData;
if( !d ) return("missing escort ship data");
d = d[pad];
return(w.$EscortDeck_TooLarge2b(owner, w, pad, d ));
} else { //ship is present
var shipmass = ship.mass;
if( ship.scriptInfo && ship.scriptInfo.hardarmour > 1 )
shipmass = shipmass / 2; //remove extra density from HardShips and Granite ships
var b = ship.boundingBox;
return(w.$EscortDeck_TooLarge3(owner, w, shipmass, b.x, b.y, b.z, pad ));
}
}
//-----------------------------------------------------------------------------------------------//
this.$EscortDeck_TooLarge2b = function _escortdeck_TooLarge2b(owner, w, pad, d) { //use saved data if no ship
if( !d || !d[14] || !d[14].mass ) return("invalid escort ship data");
var shipmass = d[14].mass;
if( d[1].indexOf("hard") == 0 ) //if this is a HardShip or Granite ship
shipmass = shipmass / 2; //remove the extra density
return(w.$EscortDeck_TooLarge3(owner, w, shipmass, d[14].x, d[14].y, d[14].z, pad ));
}
//-----------------------------------------------------------------------------------------------//
this.$EscortDeck_TooLarge3 = function _escortdeck_TooLarge3(owner, w, shipmass, bx, by, bz, pad) { //check given data
if (!owner || !owner.isValid) return("invalid owner ship");
if (!(shipmass > 0)) return("missing escort ship mass");
if (!bx || !(bx>0) || !by || !(by>0) || !bz || !(bz>0))
return("invalid escort ship size");
var e, l, x = 0, y = 0, z = 0, maxmass = 0;
var s = w.$EscortDeckPadSize;
if (s && s[0]) {
l = s.length-1;
if (pad < 0 || pad > l) e = s[l]; //the last line contain the maximums
else e = s[pad];
if (e && e[0]) {
x = e[0];
y = e[1];
z = e[2];
maxmass = e[3];
}
}
var r = null;
if (shipmass > maxmass) r = "too heavy ("+Math.ceil(shipmass/1000)+"t > "+Math.floor(maxmass/1000)+"t)";
else if (bx > x) r = "too wide ("+Math.ceil(bx)+"m > "+x+"m)";
else if (by > y) r = "too tall ("+Math.ceil(by)+"m > "+y+"m)";
else if (bz > z) r = "too long ("+Math.ceil(bz)+"m > "+z+"m)";
if (w.$debug) {
var p = pad+1;
if (p == 0) p = "any";
if (r) var l = r;
else var l = "["+Math.ceil(bx*100)/100+","+Math.ceil(by*100)/100+","+
Math.ceil(bz*100)/100+","+Math.ceil(shipmass)+"] fit into ["+Math.floor(x*100)/100+","+
Math.floor(y*100)/100+","+Math.floor(z*100)/100+","+Math.floor(maxmass)+"]";
// log(w.name, "Pad "+p+": "+l); //debug
}
return(r); //if null then not too large
}
//-----------------------------------------------------------------------------------------------//
this.$EscortDeck_VClear = function _escortdeck_VClear(w) { //remove the models of deck and escorts
var p = player.ship;
if( isValidFrameCallback( w.$EscortDeckFCB ) )
removeFrameCallback( w.$EscortDeckFCB );
if( w.$EscortDeckV ) w.$EscortDeckV.remove();
w.$EscortDeckV = null;
w.$EscortDeckSelectedPad = -1;
if( w.$EscortDeckShip ) {
for(var i = 0; i < w.$EscortDeckPadPos.length; i++ ) {
var s = w.$EscortDeckShip[i];
if( s && s.isValid ) {
var lost = false;
if( !w.$EscortDeckShipPos[i] ) { //not in deck
var rnd = ( 5 * Math.random() + 1 ) * p.scannerRange;
var d = s.position.distanceTo(p.position) - p.scannerRange;
if( d > rnd ) lost = true; //leaved farther than its luck
}
if( lost ) player.consoleMessage(s.name+" is left behind from pad "+i, 10);
else {
w.$EscortDeckShipData[i] = w.$EscortDeck_MakeShipData(s);
s.remove(true);
}
if(w.$debug) log(w.name, (i+1)+". "+w.$EscortDeckShipData[i]); //debug
}
w.$EscortDeckShip[i] = null;
w.$EscortDeckShipPos[i] = null;
}
} else {
w.$EscortDeckShip = [];
w.$EscortDeckShipPos = [];
}
}
//-----------------------------------------------------------------------------------------------//
this.$EscortDeck_TugDrone_Processing = function _escortdeck_TugDrone_Processing(ship) {
var that = _escortdeck_TugDrone_Processing;
var t = (that.t = that.t || worldScripts.towbar);
var wse = (that.wse = that.wse || worldScripts.escortdeck);
log(wse.name, "_escortdeck_TugDrone_Processing_escortdeck: Tug Drone towed ship "+ship.displayName);
if (ship && ship.isValid && ship.script && ship.script.$EscortDeckUsable > 0) {
log(wse.name, "Tug Drone towed ship "+ship.displayName+" is usable, storing in Hyperspace Hangar");
wse.$EscortDeck_HHStoreShip(ship);
player.consoleMessage("Usable ship "+ship.displayName+" stored into Hyperspace Hangar", 10);
player.commsMessage("Usable ship "+ship.displayName+" stored into Hyperspace Hangar", 10);
ship.remove(true);
} else {
log(wse.name, "Tug Drone towed ship "+ship.displayName+" not usable");
t.$Towbar_TugDrone_ProcessingOrig(ship);
}
}
//-----------------------------------------------------------------------------------------------//
this.$printDeckLoad = function _escortdeck_printDeckLoad() {
var w = worldScripts.escortdeck;
var _sArray = w.$EscortDeckShip;
var _sDataArray = w.$EscortDeckShipData;
var i;
i = w.$EscortDeckPadPos.length;
log(w.name, "Decks has "+i+" pads");
while (i--) {
if (_sArray[i])
log(w.name, "Pad "+i+": "+_sArray[i].shipUniqueName+" ("+_sArray[i].dataKey+")");
else
log(w.name, "Pad "+i+": "+_sDataArray[i]);
}
}
//-----------------------------------------------------------------------------------------------//
this.$EscortDeck_RefuelEscort = function _escortdeck_RefuelEscort(d) {
if (!d) return;
if (d[9] >= 7) return;
var f = 7 - d[9];
var v = f * 3; // 3 CR/LY (gotta charge extra for the service :-)
if (player.credits < v) {
log(this.name, "Insufficient funds to refuel escort "+d[0][3]);
player.consoleMessage("Insufficient funds to refuel escort "+d[0][3]);
} else {
player.credits -= v;
d[9] = 7;
log(this.name, "Refuelled escort "+d[0][3]+": "+ f.toFixed(1) +"LY for "+ formatCredits(v, true, true));
player.consoleMessage("Refuelled escort "+d[0][3]+": "+ f.toFixed(1) +"LY for "+ formatCredits(v, true, true));
}
}
|