| Scripts/variablemasslock.js | "use strict";
this.name	= "variablemasslock";
this.author	= "Norby";
this.copyright	= "2015 Norby";
this.description= "Variable masslocks based on object masses";
this.licence	= "CC BY-NC-SA 4.0";
//internal properties, should not touch
this.$VMActive = false; //there is at least a ship with changed scanClass to prevent masslock
this.$VMFCB = null; //FrameCallBack pointer
this.$VMRocks = []; //store asteroids, bouys and cargo boxes with changed scanClass
this.$VMShips = []; //store ships with changed scanClass and its original scanClass
this.$VMTimer = null; //Timer for detect new ships
//worldscript events
this.startUp = function() {
//	this.$WSED = worldScripts.escortdeck;
}
this.alertConditionChanged = function(newCondition, oldCondition) {
	var w = this;
	if( newCondition == 1 ) { //green alert
		if( !isValidFrameCallback( w.$VMFCB ) )
			w.$VMFCB = addFrameCallback( w.$VM_FCB.bind(w) );
	} else { //other than green alert
		w.$VMActive = false;
		if( isValidFrameCallback( w.$VMFCB ) )
			removeFrameCallback( w.$VMFCB );
		w.$VM_Restore(w);
		if( newCondition == 2 ) w.$VM_Timed.bind(w);
	}
}
this.shipDockedWithStation = function() {
	this.$VM_StopTimer(this);
}
this.shipWillLaunchFromStation = function() {
	this.$VM_StartTimer(this);
}
//VariableMasslock methods
this.$Range = function(mass) { //callable from other scripts
	return( mass * 0.02 + 17000 ); //small masslock radius of this ship
}
this.$VM_FCB = function() { //FrameCallBack in green alert
	//detect if a ship with changed scanClass is arrived within the shortened masslock range
	//and restore scanClasses to go out from green alert
	var p = player.ship;
	var w = this;
	if( !w.$VMActive || !p || !p.isValid ) return;
	var s = w.$VMShips;
	if( s ) for( var i = 0; i < s.length; i++ ) {
		var si = s[i];
		if( si ) {
			var t = si[0];
			if( t && t.isValid && si[2] > t.position.distanceTo( p.position ) ) {
				t.scanClass = si[1]; //end of green alert within radius
				t.scannerDisplayColor1 = null; //restore default color
			}
		}
	}
}
this.$VM_Restore = function(w) { //restore scanClasses of changed ships to its original scanClass
	var s = w.$VMShips;
	if( s ) for( var i = 0; i < s.length; i++ ) {
		var si = s[i];
		if( si ) {
			var t = si[0];
			if( t && t.isValid ) {
				t.scanClass = si[1]; //restore original scanClass
				t.scannerDisplayColor1 = null; //restore default color
			}
		}
	}
	w.$VMShips = [];
}
this.$VM_Rocks = function(w) {
	var p = player.ship;
	for( var i = 0; i < w.$VMRocks.length; i++ ) {
		var r = w.$VMRocks[i]; //restore changed asteroids
		if( r ) {
			var t = r[0]; //Asteroids do not masslock over the given distance
			if( t && t.isValid ) {
				var dist = t.position.distanceTo( p.position );
				var angle = p.heading.angleTo( t.position.subtract(p.position) );
				if( dist >= p.speed || angle >= 0.1 ) {
//					log( w.name, t.name+" "+t.scanClass+" "+dist+"m "+angle+"rad" ); //debug
					t.scanClass = r[1];
					t.scannerDisplayColor1 = null; //restore default color
					w.$VMRocks[i] = false; //remove from the array
				}
			}
		}
	}
}
this.$VM_StartTimer = function(w) {
	this.$VMRocks = []; //store asteroids with changed scanClass
	if( !w.$VMTimer ) w.$VMTimer = new Timer(w, w.$VM_Timed.bind(w), 1, 0.31);
}
this.$VM_StopTimer = function(w) {
	w.$VM_Restore(w); //restore ship scanClasses in variable masslock range
	for( var i = 0; i < w.$VMRocks.length; i++ ) {
		var r = w.$VMRocks[i]; //restore changed asteroids
		if( r && r.isValid ) {
			var t = r[0]; //restore changed asteroids
			if( t && t.isValid ) {
				t.scanClass = r[1];
				t.scannerDisplayColor1 = null; //restore default color
			}
		}
	}
	w.$VMRocks = [];
	if( w.$VMTimer ) {
		w.$VMTimer.stop();
		delete w.$VMTimer;
	}
}
this.$VM_Timed = function() { //detect if there is a ship on scanner over the shortened
			// masslock range and change scanClass to do not masslock
	var p = player.ship;
	if( !p || !p.isValid ) return;
	var w = this;
	w.$VM_Restore(w); //restore ship scanClasses in variable masslock range
	w.$VM_Rocks(w); //restore asteroids over given range 
	if( player.alertCondition > 2 ) return; //no check in red alert
	var s = p.checkScanner(false);//much faster than filteredEntities
//	var s = system.filteredEntities(w, w._isShip, p, p.scannerRange); //slow
	var vm = true; //variable masslock active
	if( s ) for( var i = 0; vm && i < s.length; i++ ) {
		var t = s[i];
		if( t && t.isValid ) {
			var dist = t.position.distanceTo( p.position );
//			log( w.name, t.name+" "+t.scanClass+" "+dist+"m"); //debug
			var rock = false;
			var c = t.scanClass;
			if( c == "CLASS_BUOY" || c == "CLASS_CARGO" || c == "CLASS_MINE" ||
				c == "CLASS_NO_DRAW" || c == "CLASS_ROCK" )
				rock = true;
			if( rock && dist < p.speed
				&& p.heading.angleTo( t.position.subtract(p.position) ) < 0.1 ) {
				w.$VMRocks.push( [t, c] ); //save
				t.scanClass = "CLASS_STATION"; //Asteroids, buoys, etc. do masslock
				t.scannerDisplayColor1 = [0.9, 0.9, 0.9]; //keep at gray
			} else if( !rock ) {
				var r = w.$Range( t.mass ); //small masslock radius of this ship
				if( r < dist ) {
					w.$VMShips.push( [t, t.scanClass, r] ); //save r for FCB
				} else vm = false; //there is a ship within small radius
			}
		}
	}
	var v = w.$VMShips;
	if( vm && v && v.length > 0 ) {
		//change scanClasses only if all ships are out of radius, cause green alert
		w.$VMActive = true;
		for( var i = 0; i < v.length; i++ ) {
			var t = v[i][0];
			if( t && t.isValid ) {
				var c = t.scanClass, d;
				if( c == "CLASS_MILITARY" || c == "CLASS_POLICE" )
					d = [0.5, 0.0, 0.5]; //purple
				else if( c == "CLASS_MISSILE" )
					d = [0.0, 0.9, 0.9]; //darker cyan
				else if( c == "CLASS_STATION" )
					d = [0.0, 0.9, 0.0]; //darker green
				else if( c == "CLASS_THARGOID" )
					d = [0.9, 0.0, 0.0]; //darker red
				else d = [0.7, 0.7, 0]; //darker yellow
				t.scanClass = "CLASS_BUOY";
				t.scannerDisplayColor1 = d;
			}
		}
//		log(w.name, v); //debug
	} else {
		w.$VMActive = false;
		w.$VMShips = [];
	}
	
}
/* http://wiki.alioth.net/index.php/Shipdata.plist#scan_class
Will alter the model's appearance on the IFF system. If this line is omitted, it will usually become by default a standard ship entity (CLASS_NEUTRAL), appearing as a yellow flag on the radar (red if hostile to the player), but may be given a different scan class if added with other roles. There are other options.
!   CLASS_BUOY - green/yellow on scanner, will rotate in idle state, does not masslock
!   CLASS_CARGO - white on scanner, can be scooped, does not masslock
    CLASS_MILITARY - purple on scanner, better pilots, will not attack other military ships, flashes purple/magenta if hostile to player
    CLASS_MISSILE - cyan on scanner, will not avoid collisions
    CLASS_POLICE - purple on scanner, will not attack other police ships, legal penalties for attacking, never has bounty, flashes purple/magenta if hostile to player
!   CLASS_ROCK - white on scanner, launched defense ships do not inherit scan class, does not masslock
    CLASS_STATION - green on scanner, launched defense ships do not inherit scan class
    CLASS_THARGOID - green/red on scanner, considered hostile to any non-thargoid ship
!   CLASS_NO_DRAW - invisible on scanner, cannot be targeted by missiles, does not masslock
!   CLASS_MINE - red/yellow on scanner, automatically set on mines launched by player to override existing scan class, does not masslock 
(This property was called "scanClass" before Oolite 1.74.) Scan classes marked as "does not masslock" will masslock the player anyway if they are hostile to the player (as condition will be Red)
Scripts can define custom colours for ships on the IFF system so ships may have a scanner appearance different to the default for their scan class.
Example:
"scan_class" = "CLASS_ROCK";
Developer Note
Oolite uses scan_class internally to determine the behaviour of some ships (particularly with regard to who may shoot whom without incurring legal penalties). Bear this in mind and don't allocate CLASS_POLICE or CLASS_THARGOID to ships lightly! 
*/
 |