WFMario / Alternative Map Plotter

// ==UserScript==
// @name         Alternative Map Plotter
// @version      2024-08-25
// @author       Mario
// @license MIT
// @match        https://*.atmoburn.com/map_plotter.php
// @grant        unsafeWindow
// ==/UserScript==

createButtons()

// set defaults
eval(unsafeWindow.byId.toString());
unsafeWindow.eval(renderMap2.toString());
unsafeWindow.eval(createButtons.toString());
unsafeWindow.eval(checkCoords.toString());
unsafeWindow.eval("var classToColor = {O: [221, 100, 79], B: [221, 100, 82], A: [224, 100, 92], F: [264, 100, 98], G: [21, 100, 95], K: [30, 100, 85], M: [30, 100, 71]};");
byId("orientation").value = "V"; // topdown
byId("mapType").value = "known";
byId("tactical").checked = true; // Check tactical view
byId("resolution").value = "8640"; // 16k
byId("empireMapData").checked = true;
byId("empireRallyPoints").checked = true;
byId("colonies").checked = true;
byId("fleet").checked = true;
byId("rally").checked = true;
byId("mapbm").checked = false;
byId("unexploredSize").value = 5;

// render script starts here
var classToColor = {O: [221, 100, 79], B: [221, 100, 82], A: [224, 100, 92], F: [264, 100, 98], G: [21, 100, 95], K: [30, 100, 85], M: [30, 100, 71]};
function renderMap2() {
    var renderedCoords = [];
    var color1, color2, color3, size, x, y;
    var orientation = byId('orientation').value;
    var aspectRatio = orientation == 'P' ? 16/9 : 4/3;
    var resolutionY = resolutionX = byId('resolution').value;
    var resolutionX = aspectRatio * resolutionY;
    var plotterScale = 20000;
    var modX = resolutionX / plotterScale;
    var modY = resolutionY / plotterScale;
    var tactical = byId('tactical').checked ? true : false;

    var focusXmin = parseInt(byId('focusXmin').value)/100
    var focusYmin = parseInt(byId('focusYmin').value)/100
    var focusXmax = parseInt(byId('focusXmax').value)/100
    var focusYmax = parseInt(byId('focusYmax').value)/100
    var focusScalerX = 1/(focusXmax-focusXmin)
    var focusScalerY = 1/(focusYmax-focusYmin)

    loadingAnimation('mapSettings', 'on');

    var canvas = document.createElement('canvas');
    canvas.height = resolutionY;
    canvas.width = resolutionX;
    var context = canvas.getContext('2d');
    context.beginPath();
    context.fillStyle = 'rgb(0,0,0)';
    context.fillRect(0, 0, resolutionX, resolutionY);
    context.font = "12px 'Rubik'";
    context.textAlign = 'center';
    context.textBaseline = 'top';
    context.lineWidth = 3;
    var tsize = byId("unexploredSize").value;

    // start sensornet stuff
    var scannerCanvas = document.createElement('canvas');
    scannerCanvas.height = resolutionY;
    scannerCanvas.width = resolutionX;
    var scannerContext = scannerCanvas.getContext('2d');
    scannerContext.globalCompositeOperation = 'xor';
    // end sensornet stuff

    let requestArguments = "orientation=" + orientation + "&color=" + byId('mapType').value + "&galaxy=" + byId('galaxy').value + "&empireMapData=" + (byId('empireMapData') && byId('empireMapData').checked ? 1 : 0)
                + "&empireRallyPoints=" + (byId('empireRallyPoints') && byId('empireRallyPoints').checked ? 1 : 0) + "&colonies=" + (byId('colonies').checked ? 1 : 0)
                + "&fleet=" + (byId('fleet').checked ? 1 : 0) + "&rally=" + (byId('rally').checked ? 1 : 0)
                + "&mapbm=" + (byId('mapbm').checked ? 1 : 0);
    if (!document.domain.includes("beta3")) {
        requestArguments += "&sensors=" + (byId('sensors').checked ? 1 : 0)
    }

    loadContent('/ajax/map_plotter.php', function(mapData) {

        if (mapData == "FAIL" || !mapData) {
            genericPopup({content: "Plotter is busy. Please try again later."});
        } else {
            try {
                var mapItems = mapData.split('\n');
                let xValues = [];
                let yValues = [];
                mapItems.forEach(mapItem => {
                    if (parseInt(mapItem.split('\t')[1]) && parseInt(mapItem.split('\t')[2])) {
                        xValues.push(parseInt(mapItem.split('\t')[1]));
                        yValues.push(parseInt(mapItem.split('\t')[2]));
                    }
                })

                // First we crop the image to only the content.
                var minX = Math.min(...xValues);
                var maxX = Math.max(...xValues) + minX;
                var minY = Math.min(...yValues);
                var maxY = Math.max(...yValues) + minY;
                var filterTerm = byId("filterID").value;

                mapItems.forEach(function(mapItem) {
                    var item = mapItem.split('\t');
                    if (item[1] > focusXmin*maxX && item[1] < focusXmax*maxX && item[2] > focusYmin*maxY && item[2] < focusYmax*maxY) { // only render things in the core
                        x = Math.round((item[1] - focusXmin*maxX) * modX * focusScalerX); // -focusXmin*max puts it in the top left corner, prevents rendering out of frame
                        y = Math.round((item[2] - focusYmin*maxY) * modY * focusScalerY);

                        if (item[0] == 's') {
                            if (tactical)
                                [color1, color2, color3] = [0, 100, 70];
                            else
                                [color1, color2, color3] = classToColor[item[6]];

                            if (!parseInt(item[7])) {
                                color2 = color2 / 5;
                                color3 = color3 / 2;
                                tsize = byId("unexploredSize").value;
                            } else { tsize = 1 }

                            size = Math.pow(item[5], 0.1) / 3;

                            context.beginPath();
                            context.fillStyle = "hsl("+color1+","+color2+"%, "+color3+"%)";
                            if (tactical || size < 0.5) {
                                if (byId('circles').checked) {
                                    context.arc(x, y, tsize * Math.max(focusScalerY, focusScalerX), 0, 2 * Math.PI, false);
                                } else {
                                    context.fillRect(x, y, tsize * Math.max(focusScalerY, focusScalerX), tsize * Math.max(focusScalerY, focusScalerX));
                                }
                            } else {
                                context.arc(x, y, size * Math.min(5,Math.max(focusScalerY, focusScalerX)), 0, 2 * Math.PI, false);
                            }
                            if (!tactical) {
                                context.shadowBlur = size;
                                context.shadowColor = "hsl("+color1+","+color2+"%, "+color3+"%)";
                            }
                            context.fill();
                        } else if (item[0] == 'n') { // sensorNet ship
                            console.log(item);
                            if (item[5] == 'global') {
                                x = Math.round((item[1] - focusXmin*maxX) * modX * focusScalerX); // -focusXmin*max puts it in the top left corner, prevents rendering out of frame
                                y = Math.round((item[2] - focusYmin*maxY) * modY * focusScalerY);
                                let xRange = Math.round(item[6] * modX * focusScalerX); // -focusXmin*max puts it in the top left corner, prevents rendering out of frame
                                let yRange = Math.round(item[7] * modY * focusScalerY);
                                console.log([x,y,xRange,yRange]);
                                scannerContext.beginPath();
                                scannerContext.strokeStyle = 'white';
                                scannerContext.fillStyle = "rgba(0,0,0,0)";
                                scannerContext.ellipse(x, y, xRange, yRange, 0, 0, 2 * Math.PI);
                                scannerContext.stroke();
                            }
                        } else {
                            if (item[4]) {
                                if (filterTerm === "" || item[4].startsWith(filterTerm)) {
                                    if (!byId('empireRallyPoints').checked || item[0] != 'r') { // if empire rally points is checked we need to ignore the player rally points
                                        y = checkCoords(renderedCoords, x, y)
                                        renderedCoords.push(String(x)+String(y));
                                        context.strokeStyle = '#000000';
                                        if (item[0] == 'r' || item[0] == 'e') { // red for rally points
                                            //console.log(item);
                                            context.fillStyle = '#ff0000'; //#ff0000 is pure red
                                        } else if (item[0] == 'c') { // green for colonies
                                            context.fillStyle = '#00ff00'; //#00ff00 is pure green
                                        } else if (item[0] == 'f') { // blue for fleets
                                            context.fillStyle = '#0077ff'; //#0000ff is pure blue
                                        } else { // white for possible other stuff
                                            context.fillStyle = '#ffffff';
                                        }
                                        context.strokeText(item[4], x, y + 6);
                                        context.fillText(item[4], x, y + 6);
                                    }
                                }
                            }
                        }
                    }
                });
            } catch(e) {
                console.log(e);
                genericPopup({content: "Your browser does not support this image size. Try a lower resolution."});
                loadingAnimation('mapSettings', 'off');
                return;
            }

            context.drawImage(scannerContext.canvas,0,0);
            var png = canvas.toDataURL('image/png');
            if (png.length < 1024) {
                genericPopup({content: "Your browser does not support this image size. Try a lower resolution."});
            } else {
                var link = document.createElement('a');
                link.href = png;
                link.download = "Atmoburn-"+String(window.location).split("//")[1].split(".")[0]+"-"+byId('galaxy').value+".png";
                link.target = "_blank";
                link.click();
            }
        }

        loadingAnimation('mapSettings', 'off');

    }, requestArguments, "POST");
}

function checkCoords(renderedCoords, x, y) {
    if (renderedCoords.includes(String(x)+String(y))) {
        return checkCoords(renderedCoords, x, y+14)
    } else {
        return y
    }
}

function createButtons() {
    let unexploredSize = document.createElement('div');
    unexploredSize.class = "inlineblock alignright";
    unexploredSize.innerHTML = "Unexplored size <br>";
    unexploredSize.title = "Render size for unexplored systems.\nUse with tactical plot.";
    byId("galaxy").parentElement.append(unexploredSize)

    let unexploredSizeInput = document.createElement('input');
    unexploredSizeInput.type = "text";
    unexploredSizeInput.className = "darkinput";
    unexploredSizeInput.id = "unexploredSize";
    unexploredSizeInput.size = "4";
    unexploredSizeInput.maxlength = "2";
    unexploredSizeInput.value = "2";
    unexploredSize.append(unexploredSizeInput)

    let focusSettings = document.createElement('div');
    focusSettings.class = "inlineblock alignright";
    focusSettings.innerHTML = "Focus Settings X-min, X-max, Y-min, Y-max <br>";
    focusSettings.title = "Settings that allow you to focus a part of the galaxy\nUse with tactical plot.";
    byId("galaxy").parentElement.append(focusSettings)

    let focusXmin = document.createElement('input');
    focusXmin.type = "text";
    focusXmin.className = "darkinput";
    focusXmin.id = "focusXmin";
    focusXmin.size = "4";
    focusXmin.maxlength = "3";
    focusXmin.value = "0";
    focusSettings.append(focusXmin)
    let focusXmax = document.createElement('input');
    focusXmax.type = "text";
    focusXmax.className = "darkinput";
    focusXmax.id = "focusXmax";
    focusXmax.size = "4";
    focusXmax.maxlength = "3";
    focusXmax.value = "100";
    focusSettings.append(focusXmax)

    let focusYmin = document.createElement('input');
    focusYmin.type = "text";
    focusYmin.className = "darkinput";
    focusYmin.id = "focusYmin";
    focusYmin.size = "4";
    focusYmin.maxlength = "3";
    focusYmin.value = "0";
    focusSettings.append(focusYmin)
    let focusYmax = document.createElement('input');
    focusYmax.type = "text";
    focusYmax.className = "darkinput";
    focusYmax.id = "focusYmax";
    focusYmax.size = "4";
    focusYmax.maxlength = "3";
    focusYmax.value = "100";
    focusSettings.append(focusYmax)

    let circlesLabel = document.createElement('label');
    circlesLabel.className = "pointer";

    let circlesQuestion = document.createElement('input');
    circlesQuestion.type = "checkbox";
    circlesQuestion.className = "darkcheckbox";
    circlesQuestion.id = "circles";
    circlesQuestion.value = "1";
    circlesLabel.append(circlesQuestion);
    circlesLabel.append("Circles?");

    byId('colonies').parentElement.parentElement.append(circlesLabel);

    let higherRes = document.createElement('option');
    higherRes.value = 12960;
    higherRes.innerHTML = "24k";
    byId('resolution').append(higherRes);

    let altRender = document.createElement('button');
    altRender.innerHTML = "Better Tactical Render";
    altRender.id = "altRender";
    altRender.className = "darkbutton";
    altRender.addEventListener("click", renderMap2);
    altRender.title = "Makes it CLEAR which systems are unexplored.";
    document.getElementsByClassName("block padding5 light aligncenter")[0].append(altRender);

    let filterSettings = document.createElement('div');
    filterSettings.class = "inlineblock alignright";
    filterSettings.innerHTML = "Filter Settings<br>";
    filterSettings.title = "Settings that allow you only render names starting with the input value.";
    byId("galaxy").parentElement.append(filterSettings)

    let filterNames = document.createElement('input');
    filterNames.type = "text";
    filterNames.className = "darkinput";
    filterNames.id = "filterID";
    filterNames.size = "7";
    filterNames.maxlength = "7";
    filterNames.value = "";
    filterNames.style.marginLeft = "5px";
    filterSettings.append(filterNames);
}