Back to Index Page generated: Feb 22, 2026, 1:21:04 AM

Expansion Variable Masslock

Content

Manifest

from Expansion Manager's OXP list from Expansion Manifest
Description Makes masslock ranges variable based on ship mass, from Adder at 17km to Anaconda at 26km. Asteroids, buoys, cargo boxes and mines turn off Torus when needed to prevent impact. Makes masslock ranges variable based on ship mass, from Adder at 17km to Anaconda at 26km. Asteroids, buoys, cargo boxes and mines turn off Torus when needed to prevent impact.
Identifier oolite.oxp.Norby.VariableMasslock oolite.oxp.Norby.VariableMasslock
Title Variable Masslock Variable Masslock
Category Mechanics Mechanics
Author Norby, cag Norby, cag
Version 2.0 2.0
Tags
Required Oolite Version
Maximum Oolite Version
Required Expansions
Optional Expansions
Conflict Expansions
Information URL http://wiki.alioth.net/index.php/VariableMasslock n/a
Download URL https://wiki.alioth.net/img_auth.php/a/a2/VariableMasslock_2.0.oxz n/a
License CC BY-NC-SA 4 CC BY-NC-SA 4
File Size n/a
Upload date 1771575813

Documentation

Also read http://wiki.alioth.net/index.php/Variable%20Masslock

VariableMasslock_readme.txt

Variable Masslock

Ships have shorter masslock radius based on mass.

 Ship name	    Mass    Masslock radius (r=mass*0.02+17000)
 Adder          16 t    17 km
 Gecko          25 t    17.5 km
 Sidewinder     25 t    17.5 km
 Viper          34 t    17.7 km
 Mamba          36 t    17.7 km
 Moray          51 t    18 km
 Cobra Mk I     52 t    18 km
 Fer-de-Lance   53 t    18 km
 Asp            76 t    18.5 km
 Krait          93 t    19 km
 Boa            182 t   20.5 km
 Cobra Mk III   215 t   21 km
 Boa2           216 t   21 km
 Python         267 t   22 km
 Anaconda       438 t   26 km

You can't use Tours drive in combat situations due to red alert, regardless of these distances.

Torus will disengage when a new ship arrives in scanner range but you can engage it again when your alert level is green.

Ships outside of masslock range are marked with darker yellow lollipop in green alert.

Torus will disengage when there are asteroids, buoys, cargo boxes or mines in a collision course to allow time to slow down and prevent impact.

Instructions:

Do not unzip the .oxz file, just move into the AddOns folder of your Oolite installation.

License:

This work is licensed under the Creative Commons Attribution-NonCommercial-ShareAlike License version 4.0.
If you are re-using any piece of this OXP, please let me know by sending an e-mail to norbylite@gmail.com.

Changelog:
 2026.02.20. v2.0  Rewrites by cag.
 2015.05.22. v1.1  Set proper scannerDisplayColors for ships out of masslock range.
                   Torus stop before Buoys and cargo boxes, not only asteroids.
 2015.04.20. v1.0  First release.

Equipment

This expansion declares no equipment.

Ships

This expansion declares no ships.

Models

This expansion declares no models.

Scripts

Path
Scripts/variablemasslock.js
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";


/*		(function () {	// variablemasslock IIFE to run prior to reloading entire script
    var vm = worldScripts.variablemasslock;
    vm.$VMStopTimer(vm);
})()	// */




/*		(function () {	// variablemasslock IIFE to run after reloading entire script
    var vm = worldScripts.variablemasslock;
    vm.$VMStartTimer(vm);
})()	// */




    (function(){


"use strict";


//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
					// - causes masslock (to avoid collision); those close & dead ahead
this.$VMShips = []; //store ships with changed scanClass and its original scanClass
					// - prevents masslock until breach reduced range
this.$VMTimer = null; //Timer for detect new ships


//worldscript events
this.startUp = function() {
//	this.$WSED = worldScripts.escortdeck;
}


this.alertConditionChanged = function alertConditionChanged(newCondition /*, oldCondition */) {
	var that = alertConditionChanged;
	var vm = (that.vm = that.vm || worldScripts.variablemasslock);


	var fcb = vm.$VMFCB;
	if( fcb ) {
		if( isValidFrameCallback( fcb ) ) {
			removeFrameCallback( fcb );
		}
		vm.$$VMFCB = null;
	}
	if( newCondition == 1 ) { //green alert
		vm.$VMFCB = addFrameCallback( vm.$VM_FCB.bind(vm) );
	} else { //other than green alert
		vm.$VMActive = false;
		vm.$VM_Restore();
		if( newCondition == 2 ) vm.$VM_Timed.bind(vm);
	}
}


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 VM_FCB() { //FrameCallBack in green alert
	var that = VM_FCB;
	var vm = (that.vm = that.vm || worldScripts.variablemasslock);


	//detect if a ship with changed scanClass is arrived within the shortened masslock range
	//and restore scanClasses to go out from green alert
	var ps = player.ship;
	if( !ps || !ps.isValid || !vm.$VMActive ) return;


	var vmShips = vm.$VMShips;
	if( vmShips ) {
		var ppos = ps.position;
		for( var i = 0, len = vmShips.length; i < len; i++ ) {
			var sData = vmShips[i];
			if( sData ) {
				var ship = sData[0];
				if( ship && ship.isValid && sData[2] > ship.position.distanceTo( ppos ) ) {
					ship.scanClass = sData[1];	//end of green alert as ship is within its radius
												// - restoring class will cause masslock
					ship.scannerDisplayColor1 = null; //restore default color
					return;
				}
			}
		}
	}
}


this.$VM_Restore = function VM_Restore() { //restore scanClasses of changed ships to its original scanClass
	var that = VM_Restore;
	var vm = (that.vm = that.vm || worldScripts.variablemasslock);
	var arrays = (that.arrays = that.arrays || vm.$VM_arrays);


	var vmShips = vm.$VMShips;
	if( vmShips ) { 
		for( var i = 0, len = vmShips.length; i < len; i++ ) {
			var sData = vmShips[i];
			if( sData ) {
				var ship = sData[0];
				if( ship && ship.isValid ) {
					ship.scanClass = sData[1]; //restore original scanClass
					ship.scannerDisplayColor1 = null; //restore default color
				}
				vmShips[i] = arrays.pop( sData );
			}
		}
	}
	vmShips.length = 0;		// clear & re-use array
}


this.$VM_Rocks = function VM_Rocks(flush) {
	var that = VM_Rocks;
	var vm = (that.vm = that.vm || worldScripts.variablemasslock);
	var arrays = (that.arrays = that.arrays || vm.$VM_arrays);


	function restore( ship, details ) {
		ship.scanClass = details[1];
		ship.scannerDisplayColor1 = null; //restore default color
		return arrays.pop( details ); //remove array from the array
	}
	
	var ps = player.ship;
	if( !ps || !ps.isValid ) return;
	var ppos = ps.position;
	var vmRocks = vm.$VMRocks; //restore changed asteroids
	for( var i = 0, len = vmRocks.length; i < len; i++ ) {
		var rData = vmRocks[i];
		if( rData ) {
			var rock = rData[0]; //Asteroids do not masslock over the given distance
			if( rock && rock.isValid ) {
				if( flush !== undefined ) {
					vmRocks[i] = restore(rock, rData);
				} else {
					var dist = rock.position.distanceTo( ppos );
					var angle = ps.heading.angleTo( rock.position.subtract(ppos) );
					if( dist >= ps.speed || angle >= 0.1 ) {
						// log( vm.name, 'VM_Rocks, ' + rock.name+" "+rock.scanClass+" "+dist+"m "+angle+"rad" ); //debug
						vmRocks[i] = restore(rock, rData);
					}
				}
			} else {
				vmRocks[i] = arrays.pop( rData ); //remove array from the array
			}
		}
	}
	if( flush !== undefined ) {
		vmRocks.length = 0;		// clear & re-use array
	}
}


this.$VM_StartTimer = function VM_StartTimer() {
	var that = VM_StartTimer;
	var vm = (that.vm = that.vm || worldScripts.variablemasslock);


	if( vm.$VMTimer ) {
		vm.$VM_StopTimer();
	}
	vm.$VMTimer = new Timer(vm, vm.$VM_Timed.bind(vm), 1, 0.31);
}


this.$VM_StopTimer = function VM_StopTimer() {
	var that = VM_StopTimer;
	var vm = (that.vm = that.vm || worldScripts.variablemasslock);


	let timer = vm.$VMTimer;
	if( timer ) {
		if( timer.isRunning ) {
			timer.stop();
		}
		vm.$VMTimer = null;
	}
	vm.$VM_Restore(); //restore ship scanClasses in variable masslock range
	vm.$VM_Rocks(true);  //restore changed asteroids
}


this.$VM_Timed = function VM_Timed() { //detect if there is a ship on scanner over the shortened
                                       // masslock range and change scanClass to do not masslock
	var that = VM_Timed;
	var arrays = (that.arrays = that.arrays || this.$VM_arrays);


	var ps = player.ship;
	if( !ps || !ps.isValid ) return;
	var ppos = ps.position;
	this.$VM_Restore(this); //restore ship scanClasses in variable masslock range
	this.$VM_Rocks(this); //restore asteroids over given range 


	if( player.alertCondition > 2 ) return; //no check in red alert
	var f = ps.checkScanner(false);//much faster than filteredEntities
//	var f = system.filteredEntities(this, this._isShip, ps, ps.scannerRange); //slow


	var vm = true; //variable masslock active
	var vmShips = this.$VMShips; //save changed ships
	if( f ) { 
		var vmRocks = this.$VMRocks; //save changed asteroids
		for( var i = 0, len = f.length; vm && i < len; i++ ) {
			var ent = f[i];
			if( ent.hasOwnProperty('$TelescopeTarget') )	// telescope's marker is a tiny ship that normally doesn't masslock
				continue;
			if( ent && ent.isValid ) {
				var tpos = ent.position;
				var t2p = tpos.subtract(ppos);
				var dist = t2p.magnitude();
				// log( this.name, 'VM_Timed, ' + ent.name+" "+ent.scanClass+" "+dist+"m"); //debug
				var rock = false;
				var scan = ent.scanClass;
				if( scan == "CLASS_BUOY" || scan == "CLASS_CARGO" || scan == "CLASS_MINE" ||
					scan == "CLASS_NO_DRAW" || scan == "CLASS_ROCK" )
					rock = true;
				if( rock && dist < ps.speed
					&& ps.heading.angleTo( t2p ) < 0.1 ) {
					vmRocks.push( arrays.pushRock(ent, scan) ); //save
					ent.scanClass = "CLASS_STATION"; //Asteroids, buoys, etc. do masslock
					ent.scannerDisplayColor1 = [0.9, 0.9, 0.9]; //keep at gray
				} else if( !rock ) {
					var mlDist = this.$Range( ent.mass ); //small masslock radius of this ship
					if( mlDist < dist ) {
						vmShips.push( arrays.pushShip(ent, ent.scanClass, mlDist) ); //save masslock radius for FCB
					} else { 
						this.$VMActive = vm = false; //there is a ship within small radius
					}
				}
			}
		}
	}
	if( vm && vmShips && vmShips.length > 0 ) {
		//change scanClasses only if all ships are out of radius, cause green alert
		this.$VMActive = true;
		for( var i = 0, len = vmShips.length; i < len; i++ ) {
			var ship = vmShips[i][0];
			if( ship && ship.isValid ) {
				var scan = ship.scanClass, dColor;
				if( scan == "CLASS_MILITARY" || scan == "CLASS_POLICE" )
					dColor = [0.5, 0.0, 0.5]; //purple
				else if( scan == "CLASS_MISSILE" )
					dColor = [0.0, 0.9, 0.9]; //darker cyan
				else if( scan == "CLASS_STATION" )
					dColor = [0.0, 0.9, 0.0]; //darker green
				else if( scan == "CLASS_THARGOID" )
					dColor = [0.9, 0.0, 0.0]; //darker red
				else dColor = [0.7, 0.7, 0]; //darker yellow
				ship.scanClass = "CLASS_BUOY";
				ship.scannerDisplayColor1 = dColor;
			}
		}
		// log(this.name, 'VM_Timed, ' + vmShips); //debug
	}
}


this.$VM_cache = function() {
	this.cache = [];
}
this.$VM_cache.prototype.pushRock = function( rock, scanClass ) {
	// log(this.name, '$VM_cache.pushRock, cache: ' + this.cache.length + ', rock: ' + rock + ', scanClass: ' + scanClass); //debug
	var array = this.cache.length > 0 ? this.cache.pop() : [];
	array[0] = rock;
	array[1] = scanClass;
	return array;
}
this.$VM_cache.prototype.pushShip = function( ship, scanClass, radius ) {
	// log(this.name, '$VM_cache.pushShip, cache: ' + this.cache.length + ', ship: ' + ship + ', scanClass: ' + scanClass + ', radius: ' + radius); //debug
	var array = this.cache.length > 0 ? this.cache.pop() : [];
	array[0] = ship;
	array[1] = scanClass;
	array[2] = radius;
	return array;
}
this.$VM_cache.prototype.pop = function( array ) {
	if( !array || !Array.isArray( array ) ) {
		log(this.name, '$VM_cache.pop, got an invalid arg: ' + array);
		return;
	}
	array.length = 0;
	this.cache.push( array );
	// log(this.name, '$VM_cache.pop, cache: ' + this.cache.length); //debug
	return null;									// convenience return
}
this.$VM_arrays = new this.$VM_cache();


}).call(this);


// only use the following line instead of the preceding one if you're loading 
// this entire script (from line 20) into the debug console
// }).call(worldScripts.variablemasslock);






/* 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! 
*/