Greasy Fork is available in English.

Planets.nu gravitonic connections

Shows paths between planets that can be reached by gravitonic and hyperjump connections

// ==UserScript==
// @name          Planets.nu gravitonic connections
// @description   Shows paths between planets that can be reached by gravitonic  and hyperjump connections
// @author        robodoc
// @license       Creative Commons Attribution 4.0 (CC BY 4.0)
// @include       http://planets.nu/home
// @include       http://planets.nu/games/*
// @include       http://*.planets.nu/*
// @include       https://*.planets.nu/*
// @include       http://planets.nu/*
// @include       https://planets.nu/*

// @version 3.0 (2019)
// @date          2019-08-01
// @namespace https://greasyfork.org/users/5275
// ==/UserScript==

// The movement information is based on the article
// "Movement Limits Tim's Host: How far can you go?" by Stefan Reuther:
// http://www.phost.de/~stefan/movement.html

// Thanks to McNimble for suggesting improvements to version 2!
// 1. Fixed a line weight problem that caused this plugin to not play well with
//    others such as Ion Storm Predictor.
// 2. Made some cosmetic improvements to make the Grav and HYP lines
//    easier on the eyes.

// Improvements to Version 3:
// 1. This version includes connections that can be reached via "inexact"
// movement (i.e., movement where you don't end up at your waypoint)
// The inexact movement displacements are partly taken from Reuther's document
// and partly taken from new calculations using the Nu client source code.
// 2. These "inexact" connections are shown in purple.
// 3. The W9Connect tool now shows "regular" connections (i.e. the same ones
// shown by the Nu client) in gray and "hidden" connections in a different
// color.

// Features:
// 1. The GravConnect tool shows paths between planets that can be reached by
//    gravitonic connections. Paths that can be reached by non-gravitonic
//    engines via normal connections are not shown. Special "inexact"
//    connections that can only be reached by setting a waypoint that can't be
//    reached in a single turn are shown in a differnt color (purple).

// 2. The HYPConnect tool shows paths between planets that can be reached by
//    hyperjump connections.

// 3. The W9Connect tool is an improved version of the built-in connections
//    tool that shows normal connections (gray), hidden connections (orange)
//    and special "inexact" connections that can only be reached by setting a
//    waypoint that can't be reached in a single turn (purple).

// Instructions: Click the "GravConnect", "W9Connect" or "HYPConnect" items on
// the Map Tools menu to turn these features on and off.

function wrapper () { // wrapper for injection

if (vgap.version < 3) {

}
// NEW VERSION
else {
	// Change values from true to false if you do not want to show the features in your menu:
	var show_GravConnect = true;
	var show_HYPConnect = true;
	var show_W9Connect = true;

	var plugin = {

			/*
			 * processload: executed whenever a turn is loaded: either the current turn or
			 * an older turn through time machine
			 */
			processload: function() {
			},

			/*
			 * loaddashboard: executed to rebuild the dashboard content after a turn is loaded
			 */
			loaddashboard: function() {
			},

			/*
			 * showdashboard: executed when switching from starmap to dashboard
			 */
			showdashboard: function() {

			},

			/*
			 * showsummary: executed when returning to the main screen of the dashboard
			 */
			showsummary: function() {
			},

			/*
			 * loadmap: executed after the first turn has been loaded to create the map
			 * as far as I can tell not executed again when using time machine
			 */
			loadmap: function() {
				vgap.map.gravconnect = false;
				 if (show_GravConnect) vgap.map.addMapTool("GravConnect", "ShowMinerals", function () {
					if (vgap.map.gravconnect) {
						vgap.map.gravconnect = false;
						vgap.map.draw();
					} else {
						vgap.map.gravconnect = true;
						vgap.plugins["GravConnect"].renderGravConnections();
						vgap.map.draw();
					}
                		} );
				vgap.map.hypconnect = false;
				if (show_HYPConnect) vgap.map.addMapTool("HYPConnect", "ShowMinerals", function () {
					if (vgap.map.hypconnect) {
						vgap.map.hypconnect = false;
						vgap.map.draw();
					} else {
						vgap.map.hypconnect = true;
						vgap.plugins["GravConnect"].renderHypConnections();
						vgap.map.draw();
					}
                		} );
				vgap.map.w9connect = false;
				if (show_W9Connect) vgap.map.addMapTool("W9Connect", "ShowMinerals", function () {
				    //vgap.map.w9connect.toggleClass("selectedmaptool")
					if (vgap.map.w9connect) {
						vgap.map.w9connect = false;
						vgap.map.draw();
					} else {
						vgap.map.w9connect = true;
						vgap.plugins["GravConnect"].renderW9Connections();
						vgap.map.draw();
					}
                		} );

			},

			/*
			 * showmap: executed when switching from dashboard to starmap
			 */
			showmap: function() {
			},

			/*
			 * draw: executed on any click or drag on the starmap
			 */
			draw: function() {
			    if (vgap.map.gravconnect)
				vgap.plugins["GravConnect"].renderGravConnections();
			    if (vgap.map.hypconnect)
				vgap.plugins["GravConnect"].renderHypConnections();
			    if (vgap.map.w9connect)
				vgap.plugins["GravConnect"].renderW9Connections();
			},

			/*
			 * loadplanet: executed a planet is selected on dashboard or starmap
		 	 * loadstarbase: executed a planet is selected on dashboard or starmap
			 * Inside the function "load" of vgapPlanetScreen (vgapPlanetScreen.prototype.load) the normal planet screen
			 * is set up. You can find the function in "nu.js" if you search for 'vgap.callPlugins("loadplanet");'.
			 *
			 * Things accessed inside this function several variables can be accessed. Elements accessed as "this.X"
			 * can be accessed here as "vgap.planetScreen.X".
			 */
			loadplanet: function() {
			},

			/*
			 * loadstarbase: executed a planet is selected on dashboard or starmap
			 * Inside the function "load" of vgapStarbaseScreen (vgapStarbaseScreen.prototype.load) the normal starbase screen
			 * is set up. You can find the function in "nu.js" if you search for 'vgap.callPlugins("loadstarbase");'.
			 *
			 * Things accessed inside this function several variables can be accessed. Elements accessed as "this.X"
			 * can be accessed here as "vgap.starbaseScreen.X".
			 */
			loadstarbase: function() {
			},

			/*
			 * loadship: executed a planet is selected on dashboard or starmap
			 * Inside the function "load" of vgapShipScreen (vgapShipScreen.prototype.load) the normal ship screen
			 * is set up. You can find the function in "nu.js" if you search for 'vgap.callPlugins("loadship");'.
			 *
			 * Things accessed inside this function several variables can be accessed. Elements accessed as "this.X"
			 * can be accessed here as "vgap.shipScreen.X".
			 */
			loadship: function() {},

			// END PLUGIN FUNCS


		    renderGravConnections: function() {
			//console.log("Rendering connections" );
			var conn2 = []; // regular grav connections
			var conn3 = []; // inexact connections
			for (var i = 0; i < vgap.planets.length; i++) {
			    var planetA = vgap.planets[i];
			    for (var j = i + 1; j < vgap.planets.length; j++) {
				var planetB = vgap.planets[j];
				var dist = Math.dist(planetA.x, planetA.y, planetB.x, planetB.y);
				var connection = false;
				if (dist > 83.5 && dist <= 165.505) {
					if (dist <= 165.0) {
						// This should always be a valid connection
						connection = 2;
					} else {
						// This will be close, so check individual points in warp well
						connection = vgap.plugins["GravConnect"].checkGravConnection(planetA.x, planetA.y, planetB.x, planetB.y);
					}
				}
				if (connection == 2) {
						conn2.push({x1:planetA.x, x2:planetB.x, y1:planetA.y, y2:planetB.y});
						// ctx.moveTo(vgap.map.screenX(planetA.x), vgap.map.screenY(planetA.y));
						// ctx.lineTo(vgap.map.screenX(planetB.x), vgap.map.screenY(planetB.y));
				} else if (connection == 3) {
						conn3.push({x1:planetA.x, x2:planetB.x, y1:planetA.y, y2:planetB.y});
						// ctx.moveTo(vgap.map.screenX(planetA.x), vgap.map.screenY(planetA.y));
						// ctx.lineTo(vgap.map.screenX(planetB.x), vgap.map.screenY(planetB.y));
				}
			    }
			}

			var ctx = vgap.map.ctx
			ctx.beginPath();
			console.log("Number of grav connections:",conn2.length)
			ctx = vgap.map.ctx
			ctx.beginPath();
			for (var i = 0; i < conn2.length; i++) {
				ctx.moveTo(vgap.map.screenX(conn2[i].x1), vgap.map.screenY(conn2[i].y1));
				ctx.lineTo(vgap.map.screenX(conn2[i].x2), vgap.map.screenY(conn2[i].y2));
			}
			ctx.closePath();
			ctx.strokeStyle = "#ff4545";
			ctx.lineWidth = 0.5; // suggested by McNimble
			ctx.stroke();

			console.log("Number of inexact grav connections:",conn3.length)
			ctx = vgap.map.ctx
			ctx.beginPath();
			for (var i = 0; i < conn3.length; i++) {
				ctx.moveTo(vgap.map.screenX(conn3[i].x1), vgap.map.screenY(conn3[i].y1));
				ctx.lineTo(vgap.map.screenX(conn3[i].x2), vgap.map.screenY(conn3[i].y2));
			}
			ctx.closePath();
			ctx.strokeStyle = "#9932CC";
			ctx.lineWidth = 1.5;
			ctx.stroke();

			ctx.lineWidth = 1; // suggested by McNimble
			//console.log("Rendered connections" );
		    },

		    checkGravConnection: function(Ax,Ay,Bx,By) {
			//console.log("Rendering connections" );
			// Determine if a point in the warp well is within the "maximum safe distance"
			var delx = [3.0,2.0,0,-2.0,-3.0,-2.0,0,2.0];
			var dely = [0,2.0,3.0,2.0,0,-2.0,-3.0,-2.0];
			var Bx2, By2;
			for (var i = 0; i < delx.length; i++) {
				Bx2 = Bx + delx[i];
				By2 = By + dely[i];
				var dist = Math.dist(Ax, Ay, Bx2, By2);
				if (dist <= 162.505) {
					//console.log("Safe distance at ",dist," to point in warp well");
					return 2;
				}
			}
			// Determine if a point in the warp well beyond the "maximum safe distance" can be reached exactly
			var exactx = [89,92,99,104,113,114,115,116,117,125,129,134,136];
			var exacty = [136,134,129,125,117,116,115,114,113,104,99,92,89];
			// Expanded list:

			for (var i = 0; i < delx.length; i++) {
				Bx2 = Bx + delx[i];
				By2 = By + dely[i];
				for (var j = 0; j < exactx.length; j++) {
					if (Math.abs(Bx2-Ax) == exactx[j] && Math.abs(By2-Ay) == exacty[j]) {
						//console.log("Beyond max safe distance at ",Math.dist(Ax, Ay, Bx2, By2)," to point in warp well");
						return 2;
					}
				}
			}

			// Determine if a point in the warp well beyond the "maximum safe distance" can be reached inexactly
			// Streu's numbers:
			// var inexactx = [13,34,42,49,52,55,63,65,81,86,95,98,102,103,126,127,130,132,138,141,149, 150,153,154,155,157,159,162];
			// var inexacty = [162,159,157,155,154,153,150,149,141,138,132,130,127,126,103,102,98,95, 86,81,65,63,55,52,49,42,34,13]

			// Inexact numbers, including Streu's:
			var inexactx = [13,34,42,49,52,55,63,65,81,86,95,98,102,103,126,127,130,132,138,141,149, 150,153,154,155,157,159,162,121,120,119,122,118,126,127,130,132,138,141,135,140,137,133,149,150,153,154,155,157,158,159,156,162,152,109,110,111,108,112,103,102,98,95,86,81,91,83,88,94,65,63,55,52,49,42,38,34,46,13,58];
			var inexacty = [162,159,157,155,154,153,150,149,141,138,132,130,127,126,103,102,98,95, 86,81,65,63,55,52,49,42,34,13,109,110,111,108,112,103,102,98,95,86,81,91, 83,88,94,65,63,55,52,49,42,38,34,46,13,58,121,120,119,122,118,126,127,130, 132,138,141,135,140,137,133,149,150,153,154,155,157,158,159,156,162,152];
			for (var i = 0; i < delx.length; i++) {
				Bx2 = Bx + delx[i];
				By2 = By + dely[i];
				for (var j = 0; j < inexactx.length; j++) {
					if (Math.abs(Bx2-Ax) == inexactx[j] && Math.abs(By2-Ay) == inexacty[j]) {
						//console.log("Beyond max safe distance at ",Math.dist(Ax, Ay, Bx2, By2)," to point in warp well");
						return 3; // Connection can be reached by "inexact" movement
					}
				}
			}

			//console.log("Connection not made for a distance of ",Math.dist(Ax, Ay, Bx, By));
			return 0;
			//console.log("Rendered connections" );
		    },

		    renderHypConnections: function() {
			// console.log("Rendering connections" );
			var ctx = vgap.map.ctx
			ctx.beginPath();
			for (var i = 0; i < vgap.planets.length; i++) {
			    var planetA = vgap.planets[i];
			    for (var j = i + 1; j < vgap.planets.length; j++) {
				var planetB = vgap.planets[j];
				var dist = Math.dist(planetA.x, planetA.y, planetB.x, planetB.y);
				var connection = false;
				if (dist >= 337 && dist <= 363) {
					if (dist >= 340 && dist <= 360) {
						// This should always be a valid connection
						connection = true;
					} else {
						// This will be close, so check individual points in warp well
						connection = vgap.plugins["GravConnect"].checkHypConnection(planetA.x, planetA.y, planetB.x, planetB.y);
					}
				}
				if (connection) {
				    ctx.moveTo(vgap.map.screenX(planetA.x), vgap.map.screenY(planetA.y));
				    ctx.lineTo(vgap.map.screenX(planetB.x), vgap.map.screenY(planetB.y));
				}
			    }
			}
			ctx.closePath();
			ctx.strokeStyle = "#8FBC8F";
			ctx.lineWidth = 0.5; // finer line weight suggested by McNimble
			ctx.stroke();
			ctx.lineWidth = 1; // suggested by McNimble
			//console.log("Rendered connections" );
		    },

		    checkHypConnection: function(Ax,Ay,Bx,By) {
			// console.log("Rendering Hyp connections" );
			// Determine if a point in the warp well can be jumped to to reach the planet
			var Bx2, By2;
			for (var i = -2; i < 2; i++) {
				for (var j = -2; j < 2; j++) {
					if (Math.abs(i) !=2)
						if (Math.abs(j) !=2) continue; // skip inner pixels
					Bx2 = Bx + i;
					By2 = By + j;
					var dist = Math.dist(Ax, Ay, Bx2, By2);
					if (dist >= 340 && dist <= 360) {
						//console.log("Hyp Connection made for a distance of ",Math.dist(Ax, Ay, Bx, By));
						return true;
					}
				}
			}
			//console.log("Hyp Connection not made for a distance of ",Math.dist(Ax, Ay, Bx, By));
			return false;
			//console.log("Rendered Hyp connections" );
		    },

		    renderW9Connections: function() {
			// console.log("Rendering connections" );
			var conn1 = []; // "normal" connections
			var conn2 = []; // hidden connections
			var conn3 = []; // inexact connections

			for (var i = 0; i < vgap.planets.length; i++) {
			    var planetA = vgap.planets[i];
			    for (var j = i + 1; j < vgap.planets.length; j++) {
				var planetB = vgap.planets[j];
				var dist = Math.dist(planetA.x, planetA.y, planetB.x, planetB.y);
				var connection = 0;
				if (dist <= 84.57) {
					if (dist <= 83.5) {
						// This should always be a valid connection
						// This is the connection reported by the Nu client
						connection = 1; // Basic "Nu client" connection
					} else {
						// This will be close, so check individual points in warp well
						connection = vgap.plugins["GravConnect"].checkW9Connection(planetA.x, planetA.y, planetB.x, planetB.y);
					}
				}
				if (connection == 1) {
						// console.log("Saving",planetA.x,planetA.y,planetB.x,planetB.y);
						conn1.push({x1:planetA.x, x2:planetB.x, y1:planetA.y, y2:planetB.y});
						// ctx.moveTo(vgap.map.screenX(planetA.x), vgap.map.screenY(planetA.y));
				    // ctx.lineTo(vgap.map.screenX(planetB.x), vgap.map.screenY(planetB.y));
				} else if (connection == 2) {
				    conn2.push({x1:planetA.x, x2:planetB.x, y1:planetA.y, y2:planetB.y});
						// ctx.moveTo(vgap.map.screenX(planetA.x), vgap.map.screenY(planetA.y));
				    // ctx.lineTo(vgap.map.screenX(planetB.x), vgap.map.screenY(planetB.y));
				} else if (connection == 3) {
				    conn3.push({x1:planetA.x, x2:planetB.x, y1:planetA.y, y2:planetB.y});
						// ctx.moveTo(vgap.map.screenX(planetA.x), vgap.map.screenY(planetA.y));
				    // ctx.lineTo(vgap.map.screenX(planetB.x), vgap.map.screenY(planetB.y));
				}
			    }
			}

			// Draw connections
			var ctx = vgap.map.ctx
			ctx.beginPath();
			for (var i = 0; i < conn1.length; i++) {
				// console.log("Drawing line",i,conn1[i].x1,conn1[i].y1,conn1[i].x2,conn1[i].y2)
				ctx.moveTo(vgap.map.screenX(conn1[i].x1), vgap.map.screenY(conn1[i].y1));
				ctx.lineTo(vgap.map.screenX(conn1[i].x2), vgap.map.screenY(conn1[i].y2));
			}
			// console.log("conn1.length",conn1.length);
			// console.log(conn1[1].x1,conn1[1].x2,conn1[1].y1,conn1[1].y2);
			ctx.closePath();
			ctx.strokeStyle = "#666666";
			ctx.lineWidth = 1; // suggested by McNimble
			ctx.stroke();

			console.log("Number of hidden connections:",conn2.length)
			ctx = vgap.map.ctx
			ctx.beginPath();
			for (var i = 0; i < conn2.length; i++) {
				ctx.moveTo(vgap.map.screenX(conn2[i].x1), vgap.map.screenY(conn2[i].y1));
				ctx.lineTo(vgap.map.screenX(conn2[i].x2), vgap.map.screenY(conn2[i].y2));
			}
			ctx.closePath();
			ctx.strokeStyle = "#FA8072";
			ctx.lineWidth = 1; // suggested by McNimble
			ctx.stroke();

			console.log("Number of inexact connections:",conn3.length)
			ctx = vgap.map.ctx
			ctx.beginPath();
			for (var i = 0; i < conn3.length; i++) {
				ctx.moveTo(vgap.map.screenX(conn3[i].x1), vgap.map.screenY(conn3[i].y1));
				ctx.lineTo(vgap.map.screenX(conn3[i].x2), vgap.map.screenY(conn3[i].y2));
			}
			ctx.closePath();
			ctx.strokeStyle = "#9932CC";
			ctx.lineWidth = 1.5;
			ctx.stroke();

			ctx.lineWidth = 1; // suggested by McNimble
		    },

		    checkW9Connection: function(Ax,Ay,Bx,By) {
			//console.log("Rendering connections" );
			// Determine if a point in the warp well is within the "maximum safe distance"
			var delx = [3.0,2.0,0,-2.0,-3.0,-2.0,0,2.0];
			var dely = [0,2.0,3.0,2.0,0,-2.0,-3.0,-2.0];
			var Bx2, By2;
			for (var i = 0; i < delx.length; i++) {
				Bx2 = Bx + delx[i];
				By2 = By + dely[i];
				var dist = Math.dist(Ax, Ay, Bx2, By2);
				if (dist <= 81.57) {
					//console.log("Safe distance at ",dist," to point in warp well");
					return 2; // Connection can be reached by "safe" movement
				}
			}
			// Determine if a point in the warp well beyond the "maximum safe distance" can be reached exactly
			var exactx = [48,66];
			var exacty = [66,48];
			for (var i = 0; i < delx.length; i++) {
				Bx2 = Bx + delx[i];
				By2 = By + dely[i];
				for (var j = 0; j < exactx.length; j++) {
					if (Math.abs(Bx2-Ax) == exactx[j] && Math.abs(By2-Ay) == exacty[j]) {
						//console.log("Beyond max safe distance at ",Math.dist(Ax, Ay, Bx2, By2)," to point in warp well");
						return 2; // Connection can be reached by "exact" movement
					}
				}
			}

			// Determine if a point in the warp well beyond the "maximum safe distance" can be reached inexactly
			// Streu's values:
			// var inexactx = [16, 24,	27,	43,	53,	54,	66,	67,	72,	78,	79,	82];
			// var inexacty = [82, 79,	78,	72,	67,	66,	54,	53,	43,	27,	24,	16];
			// Expanded values:
			// 63 52 81.68843247363729
			// 64 51 81.83520025025906
			// 70 42 81.6333265278342
			// 67 47 81.8413098624405
			// 77 27 81.59656855530139
			// 78 24 81.60882305241266
			// 80 16 81.58431221748455
			// 69 44 81.83520025025906
			// 76 30 81.70679286326198
			// 58 58 82.02438661763951
			// 59 57 82.03657720797473
			var inexactx = [16, 16,	24, 24,	27, 27,	30, 42, 43,	44, 47, 51, 52, 53,	54,	57, 58, 59, 63, 64, 66,	67, 67,	69, 70, 72,	76, 77, 78, 78,	79,	80, 82];
			var inexacty = [80, 82,	78, 79,	77, 78,	76, 70, 72,	69, 67, 64, 63, 67,	66,	59, 58, 57, 52, 51, 54,	47, 53,	44, 42, 43,	30, 27, 24, 27,	24,	16, 16];
			for (var i = 0; i < delx.length; i++) {
				Bx2 = Bx + delx[i];
				By2 = By + dely[i];
				for (var j = 0; j < inexactx.length; j++) {
					if (Math.abs(Bx2-Ax) == inexactx[j] && Math.abs(By2-Ay) == inexacty[j]) {
						//console.log("Beyond max safe distance at ",Math.dist(Ax, Ay, Bx2, By2)," to point in warp well");
						return 3; // Connection can be reached by "inexact" movement
					}
				}
			}

			//console.log("Connection not made for a distance of ",Math.dist(Ax, Ay, Bx, By));
			return 0;
			//console.log("Rendered connections" );
		    },

	};

	// register your plugin with NU
	vgap.registerPlugin(plugin, "GravConnect");

}

} //wrapper for injection

var script = document.createElement("script");
script.type = "application/javascript";
script.textContent = "(" + wrapper + ")();";

document.body.appendChild(script);