| 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.equipmentAdded = function(equipment) {
if (equipment.indexOf("EQ_ESCORTDECK_") == 0) return; // don't run when buying escorts otherwise it runs twice
this.playerBoughtEquipment(equipment);
}
this.playerBoughtEquipment = 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));
}
}
|