| Scripts/carriers.js | "use strict";
this.name        = "carriers";
this.author      = "Norby";
this.copyright   = "2015 Norbert Nagy";
this.licence     = "CC BY-NC-SA 4.0";
this.description = "Carriers with open decks.";
this.$Debug = false; //verbose log
this.$CarriersSitInto = true; //turn on the "sit into the ecort during flight"
this.$CarriersSitIntoWithMode = true; //show the mode menu item on primed EscortDeck to enable sit into
//internal properties, should not touch
this.$CarriersBG = ""; //backgroung image for missionscreens of Carriers
this.$CarriersCarrier = null; //the carrier ship object, used in carriers-escort.js ship script
this.$CarriersDocked = false; //flag if the player is the current ship is a carrier when player is docked
this.$CarriersED = null; //store a pointer to escortdeck worldscript
this.$CarriersEDEQ = null; //which deck player have: normal or XL
this.$CarriersEscortData = null; //temporary storage of selected escort's data when sit into
this.$CarriersEDCarrierPad = -1; //this pad on Escort Deck store the Carrier when player sit into an escort
this.$CarriersFCB = null; //FrameCallBack for calling event handlers after launch
this.$CarriersFCBArray = []; //store event handlers for FrameCallBack
this.$CarriersLastShip = null; //store the last player ship which is docked into a station for detect restore
this.$CarriersPirate = null; //store a created pirate carrier
this.$CarriersPlayerInEscort = false; //flag for EscortDeck FCB
this.$CarriersPrevCarrier = null; //save last name to avoid repeated landing clearance message
this.$CarriersS = null; //store the created trader ship to prevent recreates
this.$CarriersShip = null; //store the created trader ship to prevent recreates
this.$CarriersShipStorage = []; //store the carried escort ships, the 0. position store the carrier itself
this.$CarriersSH = null; //store a pointer to Ship_Storage_Helper.js worldscript
this.$CarriersSV = null; //store a pointer to shipversion worldscript
this.$CarriersSVB = null; //store the whole playerBoughtNewShip() code of shipversion
this.$CarriersT = null; //store a pointer to telescope worldscript
this.$CarriersTimer = null; //general timer for landing back on Carrier
//this.$CarriersTimerL = null; //delay timer for PS.launch()
//this.$CarriersTimerV = null; //delay timer for vdock remove
this.$CarriersVDock = null; //Virtual dock for ship change during launch an escort from the carrier
this.$EscortDeckLandingDist = 500; //landing distance, will be overwritten if EscortDeck OXP present
//world script events
this.startUp = function() {
	if(worldScripts["BGS-M"]) this.$CarriersBG = "bgs-i_equipship.png";
	else if(worldScripts["Better Screens"]) this.$CarriersBG = "bs-base-bg.png";
	else if(worldScripts["Gallery"]) this.$CarriersBG = "gallery_bg.png";
	
	this.$CarriersTimer = new Timer(this, this.$Carriers_Timed.bind(this), 2, 2);
	this.$CarriersED = worldScripts.escortdeck;
	if(!this.$CarriersED) log("Carriers", "Error: Carriers can not handle escorts without EscortDeck OXP");
	else {
		this.$EscortDeckLandingDist = this.$CarriersED.$EscortDeckLandingDist;
		this.$CarriersED.$EscortDeckSitInto = this.$CarriersSitInto;
		this.$CarriersED.$EscortDeckSitIntoWithMode = this.$CarriersSitIntoWithMode;
	}
	
	this.$CarriersSH = worldScripts["Ship_Storage_Helper.js"];
	if(!this.$CarriersSH) log("Carriers", "Error: Carriers can not change ship without Ship Storage Helper OXP");
	else {
//		player.credits += 1000000;//debug ;)
	}
	this.$CarriersSV = worldScripts.shipversion;
	this.$CarriersT = worldScripts.telescope;
	var s = missionVariables.$CarriersShipStorage;
	if( s && s.length > 0 )	this.$CarriersShipStorage = JSON.parse(s);
	this.$CarriersVDock = null; //Virtual dock for ship change during launch an escort from the carrier
	this.$CarriersEDCarrierPad = -1; //this pad store the Carrier when player sit into an escort
	this.$CarriersPlayerInEscort = false;
	this.$Carriers_CheckCarrier();
}
this.guiScreenChanged = function(to, from) {
	var ps = player.ship;
	var w = this.$CarriersED;
	if( to != "GUI_SCREEN_STATUS" || !ps.docked ) return;
	if(this.$Debug) log("Carriers", "guiScreenChanged "+to+" docked:"+ps.docked+" "+ps.dockedStation+" "
		+this.$CarriersSitInto+" vdock:"+this.$CarriersVDock);
	if( !w || !this.$CarriersVDock ) return;
	var h = worldScripts.hudselector;
	if( h ) h.$HUDSelectorRestoreHUD(); //give back the actual HUD - must if restoreShipData breaks
//5. step: Call launch() to do not stay within the virtual dock.
	ps.launch();
//call in timer if do not work well here
//	this.$CarriersTimerL = new Timer(this, this.$Carriers_TimedL.bind(this), 0.001, 0);
//	if(this.$Debug) log(this.name,"TimerL started: "+this.$CarriersTimerL);
//	var f = this.$Carriers_TimedL.bind(this); f("aha"); //skip timer delay and call instantly
	
	if( ps != this.$CarriersCarrier ) { //restore escort equipments
		w.$EscortDeck_RestoreShipData(ps, this.$CarriersEscortData, w);
		//Escort Deck or XL deck for using MFD and primable functions
		ps.awardEquipment(this.$CarriersEDEQ);//again for sure
		if( h ) h.$HUDSelectorRestoreHUD(); //give back the actual HUD again for sure
	}
}
this.playerWillSaveGame = function(message) {
       missionVariables.$CarriersShipStorage = JSON.stringify(this.$CarriersShipStorage);
}
this.shipAttackedOther = function(other) { //player hits other
	if( !other.script ) other.script = "oolite-default-ship-script.js"; //for sure
	if( other.script ) other.script.$Carriers_Target = 1; //flag for carrier AI
}
this.shipCollided = function( pt ) {
	if( pt && pt.isValid && pt == this.$CarriersCarrier //exclude carriers in exhibitions of Gallery OXP
		&& player.ship && player.ship.isValid && player.ship.target == pt ) {
		this.$Carriers_DockPlayer( pt );
	}
}
this.shipDockedWithStation = function(station) {
	var ps = player.ship;
	var w = this.$CarriersED;
	if(this.$Debug) log("Carriers", "sDWS station.dataKey:"+station.dataKey
		+" pad:"+w.$EscortDeckSelectedPad+" cpad:"+this.$CarriersEDCarrierPad
		+" ps.dockedStation:"+ps.dockedStation+" inEscort:"+this.$CarriersPlayerInEscort
		+" vdock:"+this.$CarriersVDock);
	if( !w || !this.$CarriersVDock ) return;
	if( this.$CarriersPlayerInEscort ) { //sit into the carrier
		this.$CarriersPlayerInEscort = false;
		player.consoleMessage("Welcome home, Commander "+player.name+"!", 5);
		this.$Carriers_SitIntoTheCarrier();
		this.$CarriersEDCarrierPad = -1;
		if( ps.equipmentStatus("EQ_ESCORTDECKXL") == "EQUIPMENT_OK" )
			ps.removeEquipment("EQ_ESCORTDECK"); //bugfix
		return;
	} else { //launch player in an escort
//3. step: Replace player ship to the escort using Ship Storage Helper.
		//this pad on Escort Deck store the Carrier when player sit into an escort
		var pad = this.$CarriersEDCarrierPad;
		this.$CarriersEDEQ = "EQ_ESCORTDECK";
		if( ps.equipmentStatus("EQ_ESCORTDECKXL") == "EQUIPMENT_OK" )
			this.$CarriersEDEQ = "EQ_ESCORTDECKXL"; //save the current deck equipment
		this.$CarriersEscortData = w.$EscortDeckShipData[pad];
		var s = JSON.stringify(this.$CarriersEscortData);
		if(this.$Debug) log("Carriers", "pad "+pad+": "+s);
		if( s && s.length > 0 ) {
			this.$CarriersCarrier = ps;
			if(ps.script) ps.script.$EscortDeckUsable = 1; //carrier is always usable
			this.$CarriersShipStorage[0] = this.$Carriers_StoreShip(); //save the carrier
//4. step: Remove the old escort and spawn a Carrier.
			if( w.$EscortDeckShip[pad] && w.$EscortDeckShip[pad].isValid
				&& w.$EscortDeckShip[pad] != ps )
				w.$EscortDeckShip[pad].remove(true);//remove NPC escort
			w.$EscortDeckShipData[pad] = null; //prevent recreation in dock
			w.$EscortDeckShipPos[pad] = null; //do not update position in FCB
			if(this.$Debug) log("Carriers","Stored: "+this.$CarriersShipStorage[0]);
			this.$CarriersPlayerInEscort = true; //set it before RestoreShip to avoid salvaging
			if( !this.$Carriers_RestoreShip(s, true) ) {
				this.$CarriersPlayerInEscort = false;//set back if restore failed
				log("Carriers","Error: can not sit into "+s);
				return; //failed to sit into the escort
			}
		} else return;
		if(this.$Debug) log("Carriers", "Player ship changed to pad "+pad+": "+s);
		var c = null; //spawn the carrier behind the player after takeoff
		var pos = this.$CarriersVDock.position.add(this.$CarriersVDock.heading
			.multiply(-(this.$EscortDeckLandingDist-100)));
		var b = JSON.parse(this.$CarriersShipStorage[0]); // destringify the carrier
		if( b.length > 1 && b[1].length > 0 ) //b[1] is the stored dataKey of carrier
			c = system.addShips("["+b[1]+"]", 1, pos, 10);
		if( c && c[0] && c[0].isValid ) {
			w.$EscortDeck_RestoreShipData(c[0], b, w);
			if(c[0].addCollisionException) { //from v1.81
				for(var i = 0; i < w.$EscortDeckPadPos.length; i++ ) {
					if( w.$EscortDeckShip && w.$EscortDeckShip[i] )
						c[0].addCollisionException(w.$EscortDeckShip[i]);
				}
			}
			w.$EscortDeckShip[pad] = c[0]; //replace the escort with the Carrier in MFD
			this.$Carriers_EscortSetup(c[0], 0); //give AI to the npc carrier
			this.$CarriersCarrier = c[0];
			
		} else log("Carriers","Error: can not create Carrier at escort takeoff");
		if(this.$Debug) {
			log("Carriers","b:"+b);
			log("Carriers","c:"+c);
		}
		
		w.$EscortDeck_RestoreShipData(ps, this.$CarriersEscortData, w);
		//Escort Deck or XL deck for using MFD and primable functions
		ps.awardEquipment(this.$CarriersEDEQ);
//5. step: Call launch() to do not stay within the virtual dock.
//		ps.launch(); - drop player.ship.dockedStation is null error in Commander's Log
	}
}
this.shipLaunchedFromStation = function() {
//6. step: Remove the virtual dock - need allow_launching=true; of vdock in shipdata.plist
	if(this.$Debug) log(this.name,this.name+".shipLaunchedFromStation called");
	this.$Carriers_RemoveVDock();
}
this.shipTargetAcquired = function(target) {
	this.$CarriersPrevCarrier = null; //to get landing clearance message a bit later
}
this.shipWillDockWithStation = function() {
	this.$Carriers_CheckCarrier();
}
this.shipWillLaunchFromStation = function() {
	if(this.$Debug) log(this.name,this.name+".shipWillLaunchFromStation called");
/*	if( this.$CarriersVDock ) {
		this.$CarriersTimerV = new Timer(this, this.$Carriers_TimedV.bind(this), 4, 0);
		if(this.$Debug) log(this.name,"TimerV started: "+this.$CarriersTimerV);
	}*/
}
//Carriers functions
this.$Carriers_CheckCarrier = function() {
	var ws = worldScripts["carriers"];
	var ps = player.ship;
	if( ps && ps.isValid && ps.dataKey && ps.dataKey.indexOf("carrier-player") > -1 ) { //current ship is a carrier
		ws.$CarriersDocked = true;
		ws.$CarriersCarrier = ps;
		ws.$CarriersShipStorage[0] = ws.$Carriers_StoreShip(); //save the carrier
	} else ws.$CarriersDocked = false;
}
this.$Carriers_DockPlayer = function( pt ) {
	var ws = worldScripts.carriers;
	//check speed difference
	var diff = player.ship.velocity.subtract( pt.velocity ).magnitude();
	if( diff > 100 ) {
		player.consoleMessage(Math.round(diff)+" m/s speed difference deny landing on "
			+ws.$CarriersPrevCarrier, 5);
	} else {
		var ps = player.ship;
		var w = ws.$CarriersED;
		var pos = pt.position.add(pt.vectorForward.multiply(1000));
		ws.$CarriersVDock = system.addShips("[carriers-vdock]", 1, pos, 100)[0];
		if( w && ws.$CarriersVDock ) {
			ws.$CarriersVDock.orientation = pt.orientation; //the direction of launch
			if(ps.addCollisionException)  //from v1.81
				ps.addCollisionException(ws.$CarriersVDock);
			ws.$CarriersVDock.dockPlayer();
		} else log("Carriers", "Error: can not create virtual dock for sit into the carrier");
	}
}
this.$Carriers_EscortSetup = function( e, id ) {
	var ws = worldScripts.carriers;
	var w = ws.$CarriersED;
	var ai = "interceptAI.plist"; //fallback ai if no escortdeck oxp present
	if( w ) ai = "EscortDeck_AI.plist";
	var ps = player.ship;
	if( e && e.isValid ) {
		if(!ps.group) ps.group = new ShipGroup("CarriersEscorts", ps); //player ship is the leader
		ps.group.addShip(e);
		e.switchAI(ai);
		if( w ) e.AIState = ("GOTO");
		e.scannerDisplayColor1 = [0.3, 1, 0.3]; //lightgreen
		var usable = 0;
		var sl = 0;
		if(e.script) {
			usable = e.script.$EscortDeckUsable; //must set again after setScript
			sl = e.script.$SSH_serviceLevel; //must set again after setScript
		}
		if( w ) e.setScript("escortdeck_escort.js");
		if(e.script) {
			e.script.$CarriersEscortHome = ws.$CarriersCarrier; //set the owner carrier
			e.script.$CarriersShipStorageID = id; //position in $CarriersShipStorage array
			e.script.$EscortDeckUsable = usable;
			e.script.$SSH_serviceLevel = sl;
		}
		e.orientation = ps.orientation;
	} else log("Carriers","Error: can not setup escort #"+id+" "+e);
}
			
this.$Carriers_FCB = function( delta ) { //FrameCallBack for calling event handlers after sit into an escort
	var ws = worldScripts.carriers;
	var f = null;
	if( ws.$CarriersFCBArray ) f = ws.$CarriersFCBArray.pop();
	if( f ) {
		//if(ws.$Debug) log(ws.name,(ws.$CarriersFCBArray.length+1)+". call "+f);
		f( ws.$CarriersVDock );
	}
}
this.$Carriers_RemoveVDock = function() {
	var ws = worldScripts.carriers;
	if( ws.$CarriersVDock ) {
		ws.$CarriersVDock.remove(true);
		ws.$CarriersVDock = null;
		if(ws.$Debug) log(ws.name,"VDock removed");
	}
	if( isValidFrameCallback( ws.$CarriersFCB ) )
		removeFrameCallback( ws.$CarriersFCB );
	if( ws.$CarriersTimerV ) {
		ws.$CarriersTimerV.stop();
		delete ws.$CarriersTimerV;
	}
}
this.$Carriers_RestoreShip = function(storedShipString, escort) {
	var ws = worldScripts.carriers;
	if(ws.$CarriersSH) {
		var h = worldScripts.hudselector;
		if( h ) h.$HUDSelectorRestoreHUD(); //give back the actual HUD
		if( escort ) {
			var d = JSON.parse(storedShipString);
			if(ws.$Debug) log("Carriers", "d1:"+d[1]+" d13:"+d[13]+" d:"+d);
			if( !player.replaceShip(d[1],d[13]) ) return false;
			var w = ws.$CarriersED;
			if( w ) w.$EscortDeck_RestoreShipData(player.ship, d, w);
			if(ws.$Debug) log("Carriers", "new player.ship: "+player.ship);
		} else {
			var e = ws.$CarriersSH.restoreStoredShip(storedShipString); //carrier
			if(ws.$Debug) log("Carriers", "player.ship: "+player.ship+" e:"+e);
			if( e && e.length > 1 ) return false;
		}
//		if(ws.$Debug) log("Carriers", "player.ship.maxEscorts: "+player.ship.maxEscorts);
		//player.ship.maxEscorts= 16; //16 is the maximum allowed by the core game - but read only
		return true;
	} else log("Carriers", "Error: Carriers can not restore ship without Ship Storage Helper OXP");
	return false;
}
this.$Carriers_SitIntoAnEscort = function(ship) { //called from EscortDeck's EQActivated
	var ps = player.ship;
	this.$CarriersCarrier = ps;
	var ws = worldScripts.carriers;
	
//1. step: Spawn a virtual dock (a ship with a dock subentity) near the player ship.
	var pos = ps.position.add(ps.vectorForward.multiply(1000));
	ws.$CarriersVDock = system.addShips("[carriers-vdock]", 1, pos, 100)[0];
	if( ws.$CarriersVDock && ws.$CarriersED ) {
		ws.$CarriersEDCarrierPad = ws.$CarriersED.$EscortDeckSelectedPad;
		player.consoleMessage( ship.name+" takeoff from "+ps.displayName, 10 );
		ws.$CarriersVDock.orientation = ps.orientation; //the direction of launch
		if(ps.addCollisionException)  //from v1.81
			ps.addCollisionException(ws.$CarriersVDock);
//2. step: Call dockPlayer() into vdock which show the docking tunnel.
		ws.$CarriersVDock.dockPlayer();
		//continued in this.guiScreenChanged
	} else log("Carriers", "Error: can not create virtual dock for launch an escort");
}
this.$Carriers_SitIntoTheCarrier = function() {
	//add the current escort ship into the first empty item of ship storage
	var ws = worldScripts.carriers;
	var w = ws.$CarriersED;
	if( w ) {
		w.$EscortDeckSelectedPad = -1;
		var b = w.$EscortDeck_MakeShipData(player.ship);//need to be docked
		var c = null; //spawn the escort behind the player
		var pad = ws.$CarriersEDCarrierPad;
		var pos = player.ship.position.add(player.ship.heading
			.multiply(-(w.$EscortDeckLandingDist-100)));
		if( b.length > 1 && b[1].length > 0 ) //b[1] is the stored dataKey of escort
			c = system.addShips("["+b[1]+"]", 1, pos, 1000);
		if( c && c[0] && c[0].isValid ) {
			if(c[0].addCollisionException) { //from v1.81
				c[0].addCollisionException(player.ship);
			}
			w.$CarriersLastShip = c[0];
			w.$EscortDeck_RestoreShipData(c[0], b, w);
			w.$EscortDeck_PutShip(c[0], pad, player.ship, w, false, true );
		} else log("Carriers","Error: can not create escort in"
			+"$Carriers_SitIntoTheCarrier b[1]:"+b[1]+" c:"+c+" pos:"+pos);
		if( ws.$CarriersCarrier != player.ship ) ws.$CarriersCarrier.remove(true); //remove the npc carrier
		ws.$Carriers_RestoreShip(ws.$CarriersShipStorage[0]);
		ws.$CarriersCarrier = player.ship;
	}
	if(ws.$Debug) log("Carriers", "CSITC Player ship:"+player.ship.dataKey
		+" "+player.ship.name+"/"+player.ship.displayName);//debug
}
this.$Carriers_StoreShip = function() {
	var ws = worldScripts.carriers;
	var sh = ws.$CarriersSH;
	if( sh ) {
		var storedShipString = sh.storeCurrentShip();
		sh.disableVortexPBNSFunc("delete");
		sh.disableMaelstromPBNSFunc("delete");
		sh.disableTCATPBNSFunc();
		return(storedShipString);
	} else log("Carriers", "Error: Carriers can not store ship without Ship Storage Helper OXP");
}
this.$Carriers_Timed = function() {
	var ps = player.ship;
	if( !ps || !ps.isValid ) return; //player died
	if( this.$CarriersPlayerInEscort ) {
		var c = this.$CarriersCarrier;
		if( !c || !c.isValid ) {
			this.$CarriersPlayerInEscort = false; //if carrier died
			var w = this.$CarriersED;
			if( w ) {
				w.$EscortDeck_ControlAll( true, false, w ); //launch all
				var pad = w.$CarriersEDCarrierPad;
				w.$EscortDeckShip[pad] = null;
				w.$EscortDeckShipData[pad] = null;
				w.$CarriersEDCarrierPad = -1;
			}
		}
	}
	var m = system.mainStation; //add some NPC carriers
	if( m && m.isValid && ( !this.$CarriersShip || !this.$CarriersShip.isValid ) ) { //delayed create
		if( system.government < 5 )
			this.$CarriersPirate = system.addShipsToRoute("carriers-pirate", 1, Math.random(), "wp")[0];
		this.$CarriersShip = system.addShips("carriers-trader", 1, m.position, 150000)[0];
//		this.$CarriersShip = system.addShips("anaconda-carrier", 1, m.position, 15000)[0];//debug
//		this.$CarriersShip = system.addShips("cobra1-carrier", 1, m.position, 15000)[0];//debug
//		this.$CarriersShipRH = system.addShips("rockhermit", 1, this.$CarriersShip.position, 15000)[0];//debug
//		this.$CarriersShipRH.target = this.$CarriersShip;
//		this.$CarriersShip.target = this.$CarriersShipRH;
//		this.$CarriersShip.setAI("dockingAI.plist");
//		this.$CarriersShip = system.addShips("empty-carrier", 1, m.position, 5000)[0];//debug
//		this.$CarriersShip.setAI("nullAI.plist");//debug
		if(this.$Debug) log("Carriers", "Added "+this.$CarriersShip);//debug
	}
	
	var pt = ps.target; //player is landing on carrier
	if( pt && pt.isValid && pt.dataKey && pt.dataKey.indexOf("carrier") > -1
		&& pt == this.$CarriersCarrier ) { //exclude carriers in exhibitions of Gallery OXP
		var d = ps.position.distanceTo(pt) - pt.collisionRadius;
		if( d < 25000 && pt.displayName != this.$CarriersPrevCarrier ) { //carrier within 25km
			pt.switchAI("EscortDeck_AI.plist"); //leave waypoint AI
			pt.AIState = "LANDING"; //do not run from the player
			player.consoleMessage( "Landing approved on "+pt.displayName, 10 );
			this.$CarriersPrevCarrier = pt.displayName; //save last name to avoid repeated message
		} else if( d < this.$EscortDeckLandingDist ) { //carrier within 500m
			this.$Carriers_DockPlayer( pt );
		}
	}
	
	if( this.$CarriersT && ps.viewPositionForward ) { //fix telescope visual target pos.(need 1.79)
		this.$CarriersT.$TelescopeVPos =
			ps.viewPositionForward.add(this.$CarriersT.$TelescopeVPosHUD);
	}
}
/*
this.$Carriers_TimedL = function(a) {
	var ps = player.ship;
	if( !ps || !ps.isValid ) return; //player died
	var ws = worldScripts.carriers;
	var h = worldScripts.hudselector;
	if( h ) h.$HUDSelectorRestoreHUD(); //give back the actual HUD
	
//5. step: Call willLaunch handlers  - not needed if allow_launching=true; in the vdock
//then launch() to do not stay within the virtual dock.
	if( !isValidFrameCallback( ws.$CarriersFCB ) )
		ws.$CarriersFCB = addFrameCallback( ws.$Carriers_FCB );
	ws.$CarriersFCBArray = [];
	ws.$CarriersFCBArray.push( function() {player.ship.launch();} );
	for(var i in worldScripts) {
		var f = worldScripts[i].shipWillLaunchFromStation;
		if( f ) {
			ws.$CarriersFCBArray.push( f.bind( worldScripts[i] ) );
			if(ws.$Debug) log(ws.name, ws.$CarriersFCBArray.length+". "+i
				+".shipWillLaunchFromStation added to FCBArray");
		}
	}
	ps.launch();
	if( ws.$CarriersTimerL ) {
		ws.$CarriersTimerL.stop();
		delete ws.$CarriersTimerL;
	}
	if(ws.$Debug) log(ws.name,"TimerL executed, a:"+a+" this:"+this);
}
this.$Carriers_TimedV = function() {
	var ps = player.ship;
	if( !ps || !ps.isValid ) return; //player died
	var ws = worldScripts.carriers;
//6. step: call launched events - not needed if allow_launching=true; in the vdock
// then remove the virtual dock
	ws.$CarriersFCBArray = [];
	ws.$CarriersFCBArray.push( ws.$Carriers_RemoveVDock.bind(ws) );
	for(var i in worldScripts) {
		var f = worldScripts[i].shipLaunchedFromStation;
		if( f && i != ws.name ) {
			ws.$CarriersFCBArray.push( f.bind( worldScripts[i] ) );
			if(ws.$Debug) log(ws.name, ws.$CarriersFCBArray.length+". "+i
				+".shipLaunchedFromStation added to FCBArray");
		}
	}
	if( ws.$CarriersTimerV ) {
		ws.$CarriersTimerV.stop();
		delete ws.$CarriersTimerV;
	}
	if(ws.$Debug) log(ws.name,"TimerV executed");
}*/
 |