// ==UserScript==
// @name Vnav.io In-Game Time Overlay
// @namespace https://gist.github.com/JustaSqu1d/8314ae50960cd16ae833a7148868820e
// @version 1.3.2
// @description A Vnav.io In-Game Time display. For World Record attempts and general usage.
// @author JustaSqu1d (https://github.com/JustaSqu1d)
// @match https://vnav.io/*
// @grant none
// @license MIT
// ==/UserScript==
(function () {
'use strict';
// Add HTML elements
console.log('Vnav.io In-Game Time Overlay v1.3.2 Loaded');
const button = document.createElement("button");
const textContainer = document.createElement("div");
function updateSettings() {
showTime = document.getElementById("time").checked;
showRatio = document.getElementById("ratio").checked;
showPrediction = document.getElementById("pred").checked;
showWR = document.getElementById("wr").checked;
lock = document.getElementById("lock").checked;
updateText();
};
function settings() {
div.style.position = "absolute";
div.style.width = window.innerWidth / 3 + "px";
div.style.height = window.innerHeight + "px";
div.style.backgroundColor = "rgba(0, 0, 0, 0.2)";
div.style.zIndex = "1";
div.innerHTML = `
<br>
<br>
<table style="color: white">
<tr>
<th>Show Time</th>
<th><input id="time" type="checkbox" ${showTime ? "checked" : ""}/></th>
</tr>
<tr>
<th>Show Ratio</th>
<th><input id="ratio" type="checkbox" ${showRatio ? "checked" : ""}/></th>
</tr>
<tr>
<th>Show Prediction</th>
<th><input id="pred" type="checkbox" ${showPrediction ? "checked" : ""}/></th>
</tr>
<tr>
<th>Show WR</th>
<th><input id="wr" type="checkbox" ${showWR ? "checked" : ""}/></th>
</tr>
<tr>
<th>Lock Overlay</th>
<th><input id="lock" type="checkbox" ${lock ? "checked" : ""}/></th>
</tr>
</table>`;
var apply = document.createElement("button");
apply.innerHTML = "Apply";
apply.style.color = "white";
apply.style.backgroundColor = "rgba(0, 0, 0, 0.2)";
apply.style.fontSize = "20px";
apply.style.height = "50px";
apply.style.position = "absolute";
apply.style.zIndex = "2";
apply.onclick = updateSettings;
div.appendChild(apply);
showSettings = !showSettings;
if (showSettings) {
button.innerHTML = "<";
document.body.append(div);
} else {
button.innerHTML = ">";
document.body.removeChild(div);
};
};
textContainer.style.position = "absolute";
textContainer.style.top = "75px";
textContainer.style.left = "25px";
textContainer.style.zIndex = "0";
document.body.appendChild(textContainer);
document.body.append(button);
button.innerHTML = ">";
button.style.color = "white";
button.style.backgroundColor = "rgba(0, 0, 0, 0.2)";
button.style.fontSize = "20px";
button.style.width = "50px";
button.style.height = "50px";
button.style.position = "absolute";
button.style.zIndex = "2";
button.onclick = settings;
// Set variables
var time = '0:00:000';
var start = 0;
var present = 0;
var fontSizeIndex = 4;
var showText = true;
var ratio = '-/min';
var alive = false;
var rawRatio = 0;
var death = 0;
var ship = "Auto 4";
var gamemode = "FFA";
var wr = "Fetching...";
var categoryIndex = 0;
var clicked = false;
var oldCategoryIndex = categoryIndex;
var showSettings = false;
var showTime = false;
var showRatio = false;
var showPrediction = false;
var showWR = false;
var lock = false;
if (localStorage.getItem("showTime") == "true") {
showTime = true;
} else {
localStorage.setItem("showTime", "true");
}
if (localStorage.getItem("showRatio") == "true") {
showRatio = true;
} else {
localStorage.setItem("showRatio", "true");
}
if (localStorage.getItem("showPrediction") == "true") {
showPrediction = true;
} else {
localStorage.setItem("showPrediction", "true");
}
if (localStorage.getItem("showWR") == "true") {
showWR = true;
} else {
localStorage.setItem("showWR", "true");
}
if (localStorage.getItem("fontSizeIndex") != fontSizeIndex) {
fontSizeIndex = localStorage.getItem("fontSizeIndex");
}
var div = document.createElement("div");
const fontSizes = ["xx-small",
"x-small",
"small",
"medium",
"large",
"x-large",
"xx-large"
];
const categories = ["High Score",
"Fast 500k",
"Fast 1m",
"Fast 1,5m"
];
updateText();
events.addEventListener('spawn', function () {
resetTimes();
alive = true;
start = new Date();
start = start.getTime();
start = start / 1000;
});
events.addEventListener('spawn', function () {
ship = network.mockups[world.player.type].name;
});
events.addEventListener('death', function () {
alive = false;
death = new Date();
death = death.getTime();
death /= 1000;
var timeDelta2 = death - start;
var hours = parseInt(timeDelta2 / 3600);
timeDelta2 -= hours * 3600;
var minutes = parseInt(timeDelta2 / 60);
timeDelta2 -= minutes * 60;
var seconds = parseInt(timeDelta2);
timeDelta2 -= seconds;
var milliseconds = parseInt(timeDelta2 * 1000);
if (milliseconds.toString().length == 1) {
milliseconds = "00" + milliseconds;
}
else if (milliseconds.toString().length == 2) {
milliseconds = "0" + milliseconds;
};
if (parseInt(seconds) !== 60) {
let addZero = (parseInt(seconds)).toString().length == 1 ? '0' : '';
seconds = `${addZero}${parseInt(seconds)}`;
} else {
if (parseInt(minutes) !== 60) {
seconds = '00';
minutes = `${parseInt(minutes)}`;
} else {
seconds = '00';
minutes = '00';
hours = `${parseInt(hours)}`;
};
};
if (hours == 0) {
time = `${minutes}:${seconds}.${milliseconds}`;
}
else {
time = `${hours}:${minutes}:${seconds}.${milliseconds}`;
}
updateText();
});
// Reset times at the start of each round
function resetTimes() {
time = '0:00:000';
start = 0;
present = 0;
ratio = '-/min';
};
// Make the DIV element draggable:
dragElement(textContainer);
function dragElement(elmnt) {
if (lock) {
return;
}
var pos1 = 0, pos2 = 0, pos3 = 0, pos4 = 0;
if (document.getElementById(elmnt.id + "header")) {
// if present, the header is where you move the DIV from:
document.getElementById(elmnt.id + "header").onmousedown = dragMouseDown;
} else {
// otherwise, move the DIV from anywhere inside the DIV:
elmnt.onmousedown = dragMouseDown;
}
function dragMouseDown(e) {
if (lock) {
return;
}
e = e || window.event;
e.preventDefault();
// get the mouse cursor position at startup:
pos3 = e.clientX;
pos4 = e.clientY;
document.onmouseup = closeDragElement;
// call a function whenever the cursor moves:
document.onmousemove = elementDrag;
}
function elementDrag(e) {
if (lock) {
return;
}
e = e || window.event;
e.preventDefault();
// calculate the new cursor position:
pos1 = pos3 - e.clientX;
pos2 = pos4 - e.clientY;
pos3 = e.clientX;
pos4 = e.clientY;
// set the element's new position:
elmnt.style.top = (elmnt.offsetTop - pos2) + "px";
elmnt.style.left = (elmnt.offsetLeft - pos1) + "px";
}
function closeDragElement() {
if (lock) {
return;
}
// stop moving when mouse button is released:
document.onmouseup = null;
document.onmousemove = null;
}
}
// Press X to toggle text; O to increase size; L to decrease size
document.body.onkeydown = function (e) {
if (e.key == "x") {
if (showText == false) {
textContainer.style.opacity = '100%';
button.style.opacity = '100%';
showText = true;
} else {
textContainer.style.opacity = '0%';
button.style.opacity = '0%';
showText = false;
};
}
else if (e.key == "o") {
if (fontSizeIndex < 6) fontSizeIndex += 1;
}
else if (e.key == "l") {
if (fontSizeIndex > 0) fontSizeIndex -= 1;
}
if (showWR) {
if ((e.key == "[") && (showWR)) {
if (categoryIndex < 3) categoryIndex += 1;
else { categoryIndex = 0 };
clicked = true;
sleep(500).then(() => {
clicked = false;
});
}
else if ((e.key == "]") && (showWR)) {
if (categoryIndex > 0) categoryIndex -= 1;
else { categoryIndex = 3 };
clicked = true;
sleep(500).then(() => {
clicked = false;
});
};
};
updateText();
};
function sleep(time) {
return new Promise((resolve) => setTimeout(resolve, time));
};
// Update text
function updateText() {
var score = world.player.score.value;
if (score < 500000) {
var target = 500000;
}
else {
var target = score - (score % 1000000) + 1000000;
};
var ratioCal = rawRatio;
if (ratioCal == 0) {
var estTimeFormated = "500k: -:-";
}
else {
var estTime = target / ratioCal;
var hours = parseInt(estTime / 60);
estTime -= hours * 60;
var minutes = parseInt(estTime);
estTime -= minutes;
var seconds = parseInt(estTime * 60);
if (seconds.toString().length == 1) {
seconds = "0" + seconds;
}
if (minutes.toString().length == 1 && hours !== 0) {
minutes = "0" + minutes;
}
if (hours == 0) {
var formatTimeEst = `${minutes}:${seconds}`;
}
else {
var formatTimeEst = `${hours}:${minutes}:${seconds}`;
}
if (target !== 500000) { var estTimeFormated = `${intToString(target)}: ${formatTimeEst}`; }
else { var estTimeFormated = `500k: ${formatTimeEst}`; };
};
textContainer.style.fontSize = fontSizes[fontSizeIndex];
textContainer.innerHTML = `<div class = "parent" style='font-style:normal'></div>`;
if (showTime) {
textContainer.innerHTML += `<div class="child" style='color:#03fc7f'>${time}</text>`;
};
if (showRatio) {
textContainer.innerHTML += `<div class="child" style='color:#44d9ff'>${ratio}</text>`;
};
if (showPrediction) {
textContainer.innerHTML += `<div class="child" style='color:#fa4983'>${estTimeFormated}</text>`;
};
if (showWR) {
textContainer.innerHTML += `<div class="child" style='color:#eee154'>WR: ${wr}</text>`;
};
if (clicked) {
textContainer.innerHTML += `
<div class="child" style='color:#8466dc'>Category: ${categories[categoryIndex]}</text>`;
};
};
function intToString(value) {
var suffixes = ["", "k", "m", "b", "t"];
var suffixNum = Math.floor(("" + value).length / 3);
var shortValue = parseFloat((suffixNum != 0 ? (value / Math.pow(1000, suffixNum)) : value).toPrecision(2));
if (shortValue % 1 != 0) {
shortValue = shortValue.toFixed(1);
};
return shortValue + suffixes[suffixNum];
};
function numberWithCommas(n) {
return n.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ",");
};
function fetchWorldRecord() {
const request = new XMLHttpRequest();
request.addEventListener("readystatechange", () => {
var category = categories[categoryIndex];
if (request.readyState == 4) {
var wrData = JSON.parse(request.responseText);
if (wrData.hours + wrData.minutes + wrData.seconds == 0) {
wr = `None`;
}
else if (category.includes("Fast")) {
if (wrData.seconds.toString().length == 1) {
wrData.seconds = "0" + wrData.seconds;
};
if (wrData.minutes.toString().length == 1 && wrData.hours !== 0) {
wrData.minutes = "0" + wrData.minutes;
};
if (wrData.hours == 0) {
wr = `${wrData.minutes}:${wrData.seconds}`;
}
else {
wr = `${wrData.hours}:${wrData.minutes}:${wrData.seconds}`;
};
}
else {
wr = `${numberWithCommas(wrData.score)}`;
};
};
});
request.open('POST', 'https://vnavwr.up.railway.app/api/');
request.setRequestHeader("Content-Type", "application/json");
var data = JSON.stringify({
"ship": ship,
"mode": gamemode,
"category": categories[categoryIndex],
"place": "1"
});
request.send(data);
};
// Main code (runs 250 times a second)
setInterval(() => {
if (alive) {
present = new Date();
present = present.getTime() / 1000;
// present -= 1;
var timeDelta = present - start;
// Get time and display on main timer
var timeDelta2 = timeDelta;
var hours = parseInt(timeDelta2 / 3600);
timeDelta2 -= hours * 3600;
var minutes = parseInt(timeDelta2 / 60);
timeDelta2 -= minutes * 60;
var seconds = parseInt(timeDelta2);
timeDelta2 -= seconds;
var milliseconds = parseInt(timeDelta2 * 1000);
if (milliseconds.toString().length == 1) {
milliseconds = "00" + milliseconds;
}
else if (milliseconds.toString().length == 2) {
milliseconds = "0" + milliseconds;
};
if (parseInt(seconds) !== 60) {
let addZero = (parseInt(seconds)).toString().length == 1 ? '0' : '';
seconds = `${addZero}${parseInt(seconds)}`;
} else {
if (parseInt(minutes) !== 60) {
seconds = '00';
minutes = `${parseInt(minutes)}`;
} else {
seconds = '00';
minutes = '00';
hours = `${parseInt(hours)}`;
};
};
if (hours == 0) {
time = `${minutes}:${seconds}:${milliseconds}`;
}
else {
time = `${hours}:${minutes}:${seconds}:${milliseconds}`;
};
// Calculate and display ratio
if (start != 0) {
ratio = (world.player.score.value / (timeDelta / 60));
rawRatio = (world.player.score.value / (timeDelta / 60));
}
else {
ratio = 0;
};
if (timeDelta <= 15) {
ratio = '-';
rawRatio = 0;
} else if (ratio >= 1000000) {
ratio /= 1000000;
ratio = ratio.toFixed(2);
ratio = ratio + 'm';
} else if (ratio >= 1000) {
ratio /= 1000;
ratio = ratio.toFixed(2);
ratio = ratio + 'k';
} else ratio = Math.round(ratio);
ratio = ratio + '/min';
// Update text
updateText();
};
}, 4);
//Checking player ship every second
setInterval(() => {
if (!showTime) {
localStorage.setItem("showTime", "false");
}
else if (localStorage.getItem("showTime") == "true") {
showTime = true;
}
else {
localStorage.setItem("showTime", "true");
};
if (!showRatio) {
localStorage.setItem("showRatio", "false");
}
else if (localStorage.getItem("showRatio") == "true") {
showRatio = true;
}
else {
localStorage.setItem("showRatio", "true");
};
if (!showPrediction) {
localStorage.setItem("showPrediction", "false");
}
else if (localStorage.getItem("showPrediction") == "true") {
showPrediction = true;
}
else {
localStorage.setItem("showPrediction", "true");
};
if (!showWR) {
localStorage.setItem("showWR", "false");
}
else if (localStorage.getItem("showWR") == "true") {
showWR = true;
}
else {
localStorage.setItem("showWR", "true");
};
if (localStorage.getItem("fontSizeIndex") != fontSizeIndex) {
localStorage.setItem("fontSizeIndex", fontSizeIndex);
}
if (alive && (ship != network.mockups[world.player.type].name || oldCategoryIndex !== categoryIndex)) {
ship = network.mockups[world.player.type].name;
fetchWorldRecord();
};
oldCategoryIndex = categoryIndex;
if (window.gamemode == "ffa") {
gamemode = "FFA";
fetchWorldRecord();
}
else {
gamemode = "2 Teams";
fetchWorldRecord();
};
}, 1000);
})();