| Scripts/thecollector.js | "use strict";
this.name	= "thecollector";
this.author	= "mossfoot, Norby";
this.copyright	= "2014 Norby";
this.description= "Missions from The Collector";
this.version	= "0.4";
this.licence	= "CC BY-NC-SA 4.0";
this.$TheCollectorBuoyDistance = 20000; //distance between two buoys before the collector's station
this.$TheCollectorMinBuoys = 5; //minimum how many buoys created
//internal properties, should not touch
this.$TheCollectorMissionStage = 0; //Where the player stay in the missions
this.$TheCollectorMissionSub = null; //Where the player stay within a MissionStage
this.$TheCollectorMSO = true; //flag for the first missionScreenOpportunity after landing
this.$TheCollectorStation = null; //store the station to detect if spawned
this.$TheCollectorStationKey = "thecollector_adck_coriolis-station"; //dataKey of collector's station
this.$TheCollectorTimes = []; //When the stages started
//worldscript events
this.startUp = function() {
	this.$TheCollectorMissionStage = missionVariables.$TheCollectorMissionStage;
	if(!(this.$TheCollectorMissionStage)) this.$TheCollectorMissionStage = 0;
	if(this.$TheCollectorMissionStage > 0) this.$TheCollectorInstructions(0); //set the F5F5 info only
	this.$TheCollectorTimes = JSON.parse(missionVariables.$TheCollectorTimes); //destringify
	if( !this.$TheCollectorTimes ) this.$TheCollectorTimes = []; //When the stages started
	this.$TheCollectorSound1 = new SoundSource;
	if( worldScripts["BGS-M"] ) this.$TheCollectorSound1.sound = "bgs-c_buy.ogg";
	else this.$TheCollectorSound1.sound = "buy.ogg";
	this.$TheCollectorSound1.loop = false;
	this.$TheCollectorSound1.repeatCount = 1;
}
this.systemWillPopulate = function() { //for Oolite 1.79 and above
	this.$TheCollectorCreateStation(true);
}
this.missionScreenOpportunity = function() {
	var p = player.ship;
//	log(this.name, p.dockedStation);//debug
	if( p && p.docked && this.$TheCollectorMSO && system ) {
		this.$TheCollectorMSO = false; //only in the first missionScreenOpportunity after landing
		if( this.$TheCollectorMissionStage < 2 && Math.random() < 0.1 ) { //10% chance
			this.$TheCollectorInstructions( 1 ); //galaxy hint talk until stage 2
			return;
		} else if( galaxyNumber > 0 ) return; //nothing more in other galaxies
		if( p.dockedStation == system.mainStation ) {
			if( system.ID == 119 && this.$TheCollectorMissionStage < 4 //Rarere
				&& p.dockedStation.dataKey != this.$TheCollectorStationKey ) {
				this.$TheCollectorInstructions( 3 ); //local rumor until stage 4
			} else if( system.ID == 160 && ( this.$TheCollectorMissionStage == 5  //Leritean
				|| this.$TheCollectorMissionStage == 6 ) ) {
				var h = 0; //elapsed time in hours
				var s = this.$TheCollectorTimes[5];
				if( s > 0 ) h = ( clock.seconds - s ) / 3600;
				var tt = Math.ceil( h * 10 ) / 10 + " hours";
//				log(this.name, tct+" "+h+" "+s);//debug
				if( s && h < 63 ) { //arrived in time (63h)
					this.$TheCollectorInstructions( 7, tt );
				} else this.$TheCollectorInstructions( 6, tt ); //too late
			} else if( system.ID == 65 && this.$TheCollectorMissionStage < -1 ) { //Sori
				this.$TheCollectorInstructions( -1 ); //todo: a ship is delivered
			} else if( system.ID == 66 && this.$TheCollectorMissionStage < -1 ) { //Aate
				this.$TheCollectorInstructions( -1 ); //todo: a ship is delivered
			}
		} else if( p.dockedStation.dataKey == this.$TheCollectorStationKey ) {
			if( this.$TheCollectorMissionStage < 5 ) { //offer Leritean mission
				this.$TheCollectorInstructions( 4 );
			} else if( this.$TheCollectorMissionStage == 6 ) { //Leritean mission failed
				this.$TheCollectorInstructions( 4 ); //back to stage 4
			} else if( this.$TheCollectorMissionStage == 7 ) { //Leritean mission done
				this.$TheCollectorInstructions( 8 );
			}
		}
	}
}
this.playerWillSaveGame = function( message ) {
	missionVariables.$TheCollectorMissionStage = this.$TheCollectorMissionStage;
	missionVariables.$TheCollectorTimes = JSON.stringify(this.$TheCollectorTimes);
}
this.shipWillDockWithStation = function( station ) {
	this.$TheCollectorMSO = true; //flag for the first missionScreenOpportunity after landing
}
this.shipWillEnterWitchspace = function() {
	this.$TheCollectorStation = null; //clear the station storage
}
this.shipWillExitWitchspace = function() { //use this event due to shipExitedWitchspace is not called for player ship
	this.$TheCollectorCreateStation();
	if( galaxyNumber == 0 && system ) {
		if( this.$TheCollectorMissionStage > 3 //trap setup after met Mr. Smith
			&& ( system.ID == 122 || system.ID == 181 ) ) { //in Bibe and Bioris
			var n = Math.floor( 1 + 3 * Math.random() ); //1-3 attackers
			var s = system.addShips("pirate", n, player.ship.position, 25000);
			if( s ) for(var i=0; i < s.length; i++ ) {
				if( s[i] ) {
					s[i].setAI("thecollector_trapAI.plist");
					s[i].awardEquipment("EQ_FUEL_INJECTION");
					s[i].fuel = 7;
					s[i].target = player.ship;
				}
			}
		}
	}
}
this.shipWillLaunchFromStation = function( station ) {
	this.$TheCollectorCreateStation(false); //for Oolite v1.77
}
//TheCollector methods
this.$TheCollectorCreateStation = function( populator ) {
	if( galaxyNumber == 0 && system && system.ID == 119 && !this.$TheCollectorStation ) {
		var dist = 1656000; //how far is the collector's station in the main station to sun lane
		var mpos = system.mainStation.position;
		var msvector = system.sun.position.subtract(mpos); //mainStation to current sun vector
		var mlen = (this.$TheCollectorMinBuoys+1) * this.$TheCollectorBuoyDistance; //min. length of the buoy line
		var apos = mpos.add( msvector.direction().multiply( dist - mlen ) ); //asteroids position
//		var p = system.sun.position; log(this.name, "Sunpos:"+p.x+" "+p.y+" "+p.z);//get orig. sunpos
//		var osp = Vector3D(-1679104.3673211336, -1513520.786781907, -43617.31031984091);//original sun
//		var movector = osp.subtract(mpos); //mainStation to original sun vector
//		var spos = mpos.add( movector.direction().multiply( dist ) ) //station position
//			.add( mpos.direction().multiply( 40000 ) );
//		log(this.name, "spos:"+spos.x+" "+spos.y+" "+spos.z);//get the following coordinates
		var spos = Vector3D(-1219441.8670473576, -1134372.290313594, 113154.84710425357); //fix for sure
		if(populator) { //for Oolite 1.79 and above
			system.setPopulator("thecollector_station", {
				callback: function(pos) {
					this.$TheCollectorCreateSGB( apos, spos );
				}.bind(this),
				location: "COORDINATES",
				coordinates: spos,
				deterministic: true
			});
			system.setPopulator("thecollector_asteroids", {
				callback: function(pos) {
					this.$TheCollectorCreateAF( apos, spos );
				}.bind(this),
				location: "COORDINATES",
				coordinates: spos
			});
		} else { //for Oolite v1.77
			this.$TheCollectorCreateSGB( apos, spos ); //create station, gate and buoys
			//create asteroid fields after(!) buoys to do not prevent an aseroid the creation of buoys
			this.$TheCollectorCreateAF( apos, spos );
		}
	}
}
this.$TheCollectorCreateAF = function( apos, spos ) {//create asteroid fields
	//call after buoys to do not prevent an aseroid the creation of a buoy
	system.addShips("asteroid", 32, apos, 30000);
	var v = apos.subtract(spos);
	system.addShips("asteroid", 64, spos.add(v.direction().multiply(v.magnitude()/2)), 40000);
	system.addShips("asteroid", 32, spos, 30000);
}
this.$TheCollectorCreateSGB = function( apos, spos ) { //create station, gate and buoys
	var s = system.addShips("[" + this.$TheCollectorStationKey + "]", 1, spos, 0);
	var v = apos.subtract(spos); //targetVector from the collector's station to the asteroids
	var bd = this.$TheCollectorBuoyDistance; //buoy distance
	var d = v.direction(); //targetVector from the collector's station to the asteroids
	if(s) this.$TheCollectorStation = s[0];
	if( this.$TheCollectorStation ) { //rotate to the asteroids
		var a = this.$TheCollectorStation.heading.angleTo(d); //angle
		var c = this.$TheCollectorStation.heading.cross(d).direction(); //cross
		this.$TheCollectorStation.orientation = this.$TheCollectorStation.orientation.rotate(c, -a);
		var gpos = spos.add( d.multiply( this.$TheCollectorBuoyDistance ) ); //gate position
		var r = system.addShips("thecollector_gate", 1, gpos, 0);
		if(r && r[0]) r[0].orientation = this.$TheCollectorStation.orientation;
		else  log(this.name, "Warning: The Collector's Gate is not created");
	} else log(this.name, "Error: The Collector's Station is not created!");
	//the trick of the buoy line is if the sun is in an unortodox position then make a very long guide line
	var l = Math.round((v.magnitude())/this.$TheCollectorBuoyDistance); //length of the buoy line
	for( var i = 2; i <= l; i++ ) { //create buoys along the line except the nearest to the station (the gate)
		var p = spos.add( d.multiply( this.$TheCollectorBuoyDistance * i ) ); //buoy position
		var b = system.addShips("thecollector_buoy", 1, p, 0);
		if(b && b[0]) b[0].displayName += " "+(i-1);
		if( l > 500 && i == 496 ) i = l - 5; //do not make too much buoys
	}
	if( l > 500 ) {
		if( r && r[0] ) {
			r[0].beaconCode = r[0].name; //last resort: the gate is a beacon
			log(this.name, "Warning: The Collector's Station is too far so the gate is the beacon");
		} else log(this.name, "Error: The Collector's Station is too far and the gate is not created");
	}
}
this.$TheCollectorInstructions = function( stage, tt ) {
	var t = worldScripts.thecollector;
	if( stage > 0 ) { //step to the new stage and display missionscreen
		t.$TheCollectorMissionStage = stage;
		t.$TheCollectorMissionSub = "_"; //start
		t.$TheCollectorMissionScreen(t, tt);
		if(msg) player.commsMessage(msg, 10);
	} //if stage is 0 then set the F5F5 message to the current missionstage and do not change it
	var msg = expandMissionText(t.name+t.$TheCollectorMissionStage);
	if( msg ) {
		mission.setInstructions( msg+" (#"+t.$TheCollectorMissionStage+")", t.name);//show in F5-F5 screen
		t.$TheCollectorTimes[t.$TheCollectorMissionStage] = clock.seconds; //save the start of the stage
	}
}
this.$TheCollectorMissionScreen = function( t, tt ) {
	var mt = t.$TheCollectorMissionStage+t.$TheCollectorMissionSub;
	var todo = expandMissionText(t.name+t.$TheCollectorMissionStage);
	var m = expandMissionText(t.name+mt, {thecollectortime: tt, thecollectortodo: todo});
	if( !m || !(m.length>0) ) return;
	var c = [];
	var cr = 0;
	for( var j = 1; j < 10; j++ ) {
		var mj = t.name+mt+j;
		var txt = null;
		cr = expandMissionText(mj+"cr"); //must pay for this choice?
		if( !cr || !(cr>0) || player.credits >= cr ) //do not show if not enough money
			txt = expandMissionText(mj);
		if( txt && txt.length > 0 ) c[ j ] = txt;
	}
	if( c.length < 1 ) c[0] = "OK"; //default choice if none is defined
//	log(t.name, "Stage #"+mt+" c.length: "+c.length+" "+c[0]);//debug
	mission.runScreen({
			title: "The Collector (#"+t.$TheCollectorMissionStage+")",
			message: m,
			background: "thecollector_background.png",
			exitScreen: "GUI_SCREEN_MANIFEST",
			choices: { "_0" : c[0],
				"_1" : c[1],
				"_2" : c[2],
				"_3" : c[3],
				"_4" : c[4],
				"_5" : c[5],
				"_6" : c[6],
				"_7" : c[7],
				"_8" : c[8],
				"_9" : c[9]
			}
	},function( choice ) {
			var o = 0;
			if( choice ) o = choice.substr(1); //cut starting "_"
			var cr = expandMissionText(t.name+mt+o+"cr"); //must pay for this choice?
			var r = expandMissionText(t.name+mt+o+"redir");
			var redir = false;
			if( r && r.length > 0 ) {
				redir = true;
				if( 1*r == r ) { //redir to a single number mean new stage
					t.$TheCollectorMissionStage = r;
					t.$TheCollectorInstructions(0);
					t.$TheCollectorMissionSub = "_"; //begin of a new stage
				} else if( r.indexOf("_") == 0 ) { //redir begin with underscore mean new sub within this stage
					t.$TheCollectorMissionSub = r + "_";
				} else { //redir into a new stage lower than the first subpage
					var i = r.indexOf("_");
					t.$TheCollectorMissionStage = r.substr(0, i);
					t.$TheCollectorInstructions(0);
					t.$TheCollectorMissionSub = r.substr(i) + "_";
//		   			log(t.name, r+" "+t.$TheCollectorMissionStage+" "+t.$TheCollectorMissionSub);//debug
				}
			}
   			log(t.name, "Stage:"+mt+" choice:"+o+" "+c[o]+" cr:"+cr+" redir:"+r);//debug
			if( o > 0 ) {
				if( cr > 0 ) { //must pay for this choice!
					player.credits -= cr;
					t.$TheCollectorSound1.play();
				}
				if( !redir ) t.$TheCollectorMissionSub += o + "_";
				t.$TheCollectorMissionScreen(t); //recursive, so do not use too much level
			} else return; //exit
//			if( !isValidFrameCallback( h.$HUDSelectorFCB2 ) )
//				h.$HUDSelectorFCB2 = addFrameCallback( h.$HUDSelector_FCB2 );
 	});
}
 |