Tired of using the ruler two stars at a time and manually adding up your ETAs? ME TOO!
The following is a script I started recently. I got some awesome help today from @Qwerty! It overrides ruler functions so that instead of measuring between two stars you can continue clicking stars and measure a more complicated route. I’m sure there are some problems with it, and there are still some things I’d like it to do, so I’ll provide the “unminified” code here so that others can collaborate.
Here’s a couple of screenshots:
No warpgates:
Through warpgates, the total “Warp Gate Flight Time” will be correct:
Now here’s the code. In chrome, you can paste this into the dev console and it should work:
//------------------------------------------------------------------------------
// The MIT License (MIT)
//
// Copyright (c) 2014 Dakota Hawkins and Vítězslav Ferko
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
//------------------------------------------------------------------------------
var universe = NeptunesPride.universe;
// Override initRuler() to use an array of stars rather than starA/starB
universe.initRuler = function () {
universe.ruler = {};
universe.ruler.stars = [];
universe.ruler.eta = 0;
universe.ruler.baseEta = 0;
universe.ruler.gateEta = 0;
universe.ruler.gate = true;
universe.ruler.totalDist = 0;
universe.ruler.ly = "0.0";
universe.ruler.hsRequired = 0;
};
// Provide a helper function to determine if two items represent a gated flight between a
// fleet and a star.
universe.isGatedFlight = function (fleet, star) {
if (fleet.kind !== "fleet" || star.kind !== "star") return false;
return (fleet.warpSpeed === 1 && fleet.path.length > 0 && fleet.path[0].uid === star.uid);
};
// Override updateRuler() to use an array of stars rather than starA/starB
universe.updateRuler = function (star) {
// If the "star" is a fleet and it is orbiting a star, use the orbited star.
if (star.kind === "fleet" && star.orbiting) {
universe.ruler.stars.push(star.orbiting);
} else {
universe.ruler.stars.push(star);
}
var numStars = universe.ruler.stars.length;
if (numStars < 2) return;
var starA = universe.ruler.stars[numStars - 2];
var starB = universe.ruler.stars[numStars - 1];
var dist = universe.distance(starA.x, starA.y, starB.x, starB.y);
var speed = universe.galaxy.fleet_speed;
var baseEta = Math.floor(dist / speed) + 1;
var gateEta = Math.floor(dist / (3 * speed)) + 1;
universe.ruler.baseEta += baseEta;
// Check whether the distance should be gated. This is the case if starA and starB are gated
// or if one of the stars is a fleet traveling at warp to the other star.
var eta = baseEta;
var gated = false;
if (universe.starsGated(starA, starB) || universe.isGatedFlight(starA, starB) || universe.isGatedFlight(starB, starA)) {
gated = true;
eta = gateEta;
}
universe.ruler.eta += eta;
// Add up the gatedEta. This will now always represent the time if the stars are gated
// regardless of whether they actually are. The only caviat is if one of the stars is a
// fleet and we haven't decided it's a gated case. In that case, we have to add the ungated
// eta, since you can't build a warp gate on a carrier.
if (!gated && (starA.kind === "fleet" || starB.kind === "fleet")) {
universe.ruler.gateEta += eta;
} else {
universe.ruler.gateEta += gateEta;
}
universe.ruler.totalDist += dist;
var ly = (8 * universe.ruler.totalDist);
universe.ruler.ly = (Math.round(ly * 10) / 10).toFixed(1);
// Hyperspace required will be the max hyperspace required along the entire route.
universe.ruler.hsRequired = Math.max(universe.ruler.hsRequired, Math.floor(8 * dist) - 2, 1);
};
var map = NeptunesPride.npui.map;
// Override drawRuler() to use an array of stars rather than starA/starB
map.drawRuler = function () {
var numStars = universe.ruler.stars.length;
if (numStars < 2) return;
var minAlpha = 0.3, maxAlpha = 0.6, alphaStep = 0.05;
// Draw backwards to make it easier to step off the alpha
var alpha = maxAlpha;
for (var i = numStars - 1; i > 0; i--) {
map.context.strokeStyle = "rgba(255, 255, 255, " + alpha + ")";
map.context.lineWidth = 8 * map.pixelRatio;
map.context.beginPath();
var x1 = map.worldToScreenX(universe.ruler.stars[i].x);
var y1 = map.worldToScreenY(universe.ruler.stars[i].y);
var x2 = map.worldToScreenX(universe.ruler.stars[i - 1].x);
var y2 = map.worldToScreenY(universe.ruler.stars[i - 1].y);
map.context.moveTo(x1, y1);
map.context.lineTo(x2, y2);
map.context.stroke();
alpha = Math.max(minAlpha, alpha - alphaStep);
}
};
var np = NeptunesPride.np;
// Override onStartRuler() to clear out the ruler.
np.onStartRuler = function () {
universe.initRuler();
if (universe.editMode === "ruler") {
np.onEndRuler();
return;
}
// Change to allow the ruler to start on a selected star OR fleet.
if (universe.selectedStar) {
universe.updateRuler(universe.selectedStar);
} else if (universe.selectedFleet) {
universe.updateRuler(universe.selectedFleet);
}
np.onResetEditMode();
universe.editMode = "ruler";
universe.interfaceSettings.showRuler = true;
np.trigger("show_ruler_toolbar");
np.trigger("map_refresh");
np.trigger("hide_screen");
np.trigger("hide_side_menu");
np.trigger("hide_selection_menu");
np.trigger("play_sound", "alt_open");
};
// Bind (or re-bind)? the v key to the ruler.
Mousetrap.bind(["v", "V"], function () { Crux.crux.trigger("start_ruler"); });
// For some reason this line must be doubled. WTF.
Crux.crux.on("start_ruler", np.onStartRuler);
Crux.crux.on("start_ruler", np.onStartRuler);
universe.initRuler();
Hopefully somebody can help minify it and make it in to a bookmarklet, I took a couple of stabs at encoding it to base64 but couldn’t get it to work. Of course, what would be REALLY swell is if @JayKyburz put this behavior in the game!
Update
Here is a bookmarklet that should work. Don’t forget to remove the leading +
+javascript:(function(){var n=NeptunesPride.universe,t,i;n.initRuler=function(){n.ruler={};n.ruler.stars=[];n.ruler.eta=0;n.ruler.baseEta=0;n.ruler.gateEta=0;n.ruler.gate=!0;n.ruler.totalDist=0;n.ruler.ly="0.0";n.ruler.hsRequired=0};n.isGatedFlight=function(n,t){return n.kind!=="fleet"||t.kind!=="star"?!1:n.warpSpeed===1&&n.path.length>0&&n.path[0].uid===t.uid};n.updateRuler=function(t){var u,e,o,l;if(t.kind==="fleet"&&t.orbiting?n.ruler.stars.push(t.orbiting):n.ruler.stars.push(t),u=n.ruler.stars.length,!(u<2)){var i=n.ruler.stars[u-2],r=n.ruler.stars[u-1],f=n.distance(i.x,i.y,r.x,r.y),s=n.galaxy.fleet_speed,h=Math.floor(f/s)+1,c=Math.floor(f/(3*s))+1;n.ruler.baseEta+=h;e=h;o=!1;(n.starsGated(i,r)||n.isGatedFlight(i,r)||n.isGatedFlight(r,i))&&(o=!0,e=c);n.ruler.eta+=e;n.ruler.gateEta+=o||i.kind!=="fleet"&&r.kind!=="fleet"?c:e;n.ruler.totalDist+=f;l=8*n.ruler.totalDist;n.ruler.ly=(Math.round(l*10)/10).toFixed(1);n.ruler.hsRequired=Math.max(n.ruler.hsRequired,Math.floor(8*f)-2,1)}};t=NeptunesPride.npui.map;t.drawRuler=function(){var u=n.ruler.stars.length,i;if(!(u<2)){var r=.6;for(i=u-1;i>0;i--){t.context.strokeStyle="rgba(255, 255, 255, "+r+")";t.context.lineWidth=8*t.pixelRatio;t.context.beginPath();var f=t.worldToScreenX(n.ruler.stars[i].x),e=t.worldToScreenY(n.ruler.stars[i].y),o=t.worldToScreenX(n.ruler.stars[i-1].x),s=t.worldToScreenY(n.ruler.stars[i-1].y);t.context.moveTo(f,e);t.context.lineTo(o,s);t.context.stroke();r=Math.max(.3,r-.05)}}};i=NeptunesPride.np;i.onStartRuler=function(){if(n.initRuler(),n.editMode==="ruler"){i.onEndRuler();return}n.selectedStar?n.updateRuler(n.selectedStar):n.selectedFleet&&n.updateRuler(n.selectedFleet);i.onResetEditMode();n.editMode="ruler";n.interfaceSettings.showRuler=!0;i.trigger("show_ruler_toolbar");i.trigger("map_refresh");i.trigger("hide_screen");i.trigger("hide_side_menu");i.trigger("hide_selection_menu");i.trigger("play_sound","alt_open")};Mousetrap.bind(["v","V"],function(){Crux.crux.trigger("start_ruler")});Crux.crux.on("start_ruler",i.onStartRuler);Crux.crux.on("start_ruler",i.onStartRuler);n.initRuler()})();
Update
Switched to the MIT license.
Update
If you start the ruler from a carrier and measure to it’s immediate destination, the ruler will use the carrier’s ETA rather than calculating it. This is to account for a carrier travelling between two gates:
Previously, that ETA would have been 3 hours since it would have been calculated off of the carrier distance.
Use the stuff @JayKyburz exposed for us rather than searching for it.
Update
Per discussion with @JayKyburz:
- Ruler is always “gated” meaning it always displays both the eta and the gated eta. Now the eta should be correct and the gated eta should represent the eta if all the stars are gated. The only time that’s not true is some logic to account for the fact that you can’t build gates on carriers.
- Ruler will display the total distance of the chain instead of the last link’s distance.
- Will display the maximum hyperspace required for any link in the ruler chain.
Update
Tweaked the drawing code slightly to make the ruler more transparent (to a point) the farther you go. In other words, as you add links, the older ones will be more transparent and the most recent will be the least transparent.
Update
Fixed the global loop variable.
Update
It got put in the game! Best ruler ever!