OWOP Export+

Makes it easy to export large areas

이 스크립트를 설치하려면 Tampermonkey, Greasemonkey 또는 Violentmonkey와 같은 확장 프로그램이 필요합니다.

이 스크립트를 설치하려면 Tampermonkey와 같은 확장 프로그램을 설치해야 합니다.

이 스크립트를 설치하려면 Tampermonkey 또는 Violentmonkey와 같은 확장 프로그램이 필요합니다.

이 스크립트를 설치하려면 Tampermonkey 또는 Userscripts와 같은 확장 프로그램이 필요합니다.

이 스크립트를 설치하려면 Tampermonkey와 같은 확장 프로그램이 필요합니다.

이 스크립트를 설치하려면 유저 스크립트 관리자 확장 프로그램이 필요합니다.

(이미 유저 스크립트 관리자가 설치되어 있습니다. 설치를 진행합니다!)

이 스타일을 설치하려면 Stylus와 같은 확장 프로그램이 필요합니다.

이 스타일을 설치하려면 Stylus와 같은 확장 프로그램이 필요합니다.

이 스타일을 설치하려면 Stylus와 같은 확장 프로그램이 필요합니다.

이 스타일을 설치하려면 유저 스타일 관리자 확장 프로그램이 필요합니다.

이 스타일을 설치하려면 유저 스타일 관리자 확장 프로그램이 필요합니다.

이 스타일을 설치하려면 유저 스타일 관리자 확장 프로그램이 필요합니다.

(이미 유저 스타일 관리자가 설치되어 있습니다. 설치를 진행합니다!)

// ==UserScript==
// @name         OWOP Export+
// @namespace    https://greasyfork.org/en/users/1502179/
// @version      1.0
// @description  Makes it easy to export large areas
// @author       NothingHere7759
// @match        https://ourworldofpixels.com/*
// @exclude      https://ourworldofpixels.com/api*
// @icon         data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAKAAAACgCAYAAACLz2ctAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAAZdEVYdFNvZnR3YXJlAFBhaW50Lk5FVCA1LjEuMTITAUd0AAAAuGVYSWZJSSoACAAAAAUAGgEFAAEAAABKAAAAGwEFAAEAAABSAAAAKAEDAAEAAAADAAAAMQECABEAAABaAAAAaYcEAAEAAABsAAAAAAAAAKOTAADoAwAAo5MAAOgDAABQYWludC5ORVQgNS4xLjEyAAADAACQBwAEAAAAMDIzMAGgAwABAAAAAQAAAAWgBAABAAAAlgAAAAAAAAACAAEAAgAEAAAAUjk4AAIABwAEAAAAMDEwMAAAAAAHd36etszaFAAAArVJREFUeF7t27uRE1EQQFGJLLBIAIc0sAgVa9PA2QSwNozFn4KqVm2jq8855hiaGenWM7pL59OD+P756/vx2iN7eXs9H6/do0/HC3BNAiQlQFICJCVAUgIkJUBSAiQlQFLZNL3aXPz49uV46ab8/PX7eOkqqs2KE5CUAEkJkJQASQmQlABJCZCUAEkJkNT69Ht7w/Fsm4vt991+vu2NiROQlABJCZCUAEkJkJQASQmQlABJCZDUeKpdbTi2J/nT+07d+vNNbb/HdGPiBCQlQFICJCVAUgIkJUBSAiQlQFICJDWaVp8u2IRMJ/nbk/dt0/eoTL+/7feY3tcmhLsgQFICJCVAUgIkJUBSAiQlQFICJHWebjimppP36US9Mn2PZ7P9uzkBSQmQlABJCZCUAEkJkJQASQmQlABJjTch1WZge/K+/R7bz7dt+32npt+LE5CUAEkJkJQASQmQlABJCZCUAEkJkNTNb0Iq00n+o9j+faffnxOQlABJCZCUAEkJkJQASQmQlABJCZDU+iZkOgGfmt53avv5ns3095h+z05AUgIkJUBSAiQlQFICJCVAUgIkJUBSAiQlQFICJCVAUgIkJUBSAiQlQFICJCVAUuP/hExN/zNQmf5XgetwApISICkBkhIgKQGSEiApAZISICkBkjofL/zLdGOyvQmZbi6m951+Hh/z8vY6assJSEqApARISoCkBEhKgKQESEqApARIajStPl2wCZmabi4qNiYfYxPCXRAgKQGSEiApAZISICkBkhIgKQGSGk2rL2Fj8timG44pJyApAZISICkBkhIgKQGSEiApAZISIKnVqfYltjcmUzYrf7e94ZhyApISICkBkhIgKQGSEiApAZISICkBkkqm3/9DtVmpVJuLbU5AUgIkJUBSAiQlQFICJCVAUgIkJUBSfwBZPnv4nnRz4wAAAABJRU5ErkJggg==
// @grant        none
// @license      MIT
// ==/UserScript==

const waitUntil = (probe, cb, t = 200) => {
    const id = setInterval(() => { try { if (probe()) { clearInterval(id); cb(); } } catch { } }, t);
};
waitUntil(() => window.OWOP && OWOP.camera?.zoom && OWOP.camera?.centerCameraTo && OWOP.options?.unloadDistance && OWOP.misc?.world?.getChunkAt && OWOP.windowSys?.addWindow && OWOP.windowSys.class?.window && OWOP.windowSys.centerWindow, install);

function install() {
    // NH Style
    if (!document.getElementById('nhStyle') || +document.getElementById('nhStyle').getAttribute("version") < 1) {
        document.getElementById('nhStyle')?.remove();
        let nhStyle = document.createElement('style');
        nhStyle.id = 'nhStyle';
        nhStyle.setAttribute("version", "1");
        nhStyle.innerHTML = `.nhCont {
    & input {
		background-color: rgba(0, 0, 0, 0.3);
		color: white;
	}
	& input::placeholder {
	    color: #bfbfbf;
	}
    & > div > input[type="number"],
    & > div > input[type="range"],
    & > div > input[type="text"],
    & > div > select {
        flex-grow: 1;
        flex-basis: 171px;
    }
    & select {
        background-color: #ABA389;
        border: 6px #ABA389 solid;
        border-image: url(/img/small_border..png) 6 repeat;
        border-image-outset: 1px;
    }
    & > div {
        display: flex;
        align-items: center;
    }
    & > div > label {
        text-wrap: nowrap;
    }
}`;
        document.head.appendChild(nhStyle);
    };

    // Export function
    async function exportArea(x1, y1, x2, y2) {
        console.time("export");
        OWOP.camera.zoom = 1;
        OWOP.options.unloadDistance = Infinity;
        x1 = +x1, y1 = +y1, x2 = +x2, y2 = +y2;
        if (x1 > x2) [x1, x2] = [x2, x1];
        if (y1 > y2) [y1, y2] = [y2, y1];
        let w = x2 - x1 + 1, h = y2 - y1 + 1;
        const canvas = document.createElement("canvas");
        canvas.width = w * 16;
        canvas.height = h * 16;
        const ctx = canvas.getContext("2d");
        ctx.imageSmoothingEnabled = false;
        for (let c = 0; c < w * h; c++) {
            let cx = c % w + x1, cy = Math.floor(c / w) + y1;
            const imageData = ctx.createImageData(16, 16);
            const data = imageData.data;
            if (!OWOP.misc.world.getChunkAt(cx, cy)) {
                OWOP.camera.centerCameraTo(cx * 16, cy * 16);
                await new Promise((resolve) => {
                    let checkChunkLoaded = setInterval(() => {
                        if (OWOP.misc.world.getChunkAt(cx, cy)) {
                            resolve();
                            clearInterval(checkChunkLoaded);
                        }
                    }, 10);
                });
            };
            let chunk = OWOP.misc.world.getChunkAt(cx, cy);
            for (let i = 0; i < data.length; i += 4) {
                let p = chunk.get(i / 4 % imageData.width, Math.floor(i / 4 / imageData.width));
                [data[i], data[i + 1], data[i + 2], data[i + 3]] = [p & 255, p >> 8 & 255, p >> 16 & 255, 255];
            };
            ctx.putImageData(imageData, c * 16 % canvas.width, Math.floor(c * 16 / canvas.width) * 16);
        };
        OWOP.options.unloadDistance = 10;
        console.timeEnd("export");
        return canvas.toDataURL('image/png');
    };

    let count = 0; // Counts the number of exports

    let exportWin = OWOP.windowSys.addWindow(new OWOP.windowSys.class.window("Export", { closeable: true }, (exportWin) => {
        exportWin.container.className = "wincontainer nhCont";
        exportWin.container.innerHTML = `<div><label for="esel">Coords measured in: </label><select name="esel" id="esel"><option>Pixels</option><option>Chunks</option></select></div><div><label for="ex1">x1: </label><input type="number" name="ex1" id="ex1"/>&emsp;<label for="ey1">y1: </label><input type="number" name="ey1" id="ey1"/></div>
<div><label for="ex2">x2: </label><input type="number" name="ex2" id="ex2"/>&emsp;<label for="ey2">y2: </label><input type="number" name="ey2" id="ey2"/></div>
<button>Export</button>`;
        exportWin.container.querySelector("#ex1").addEventListener("blur", () => { exportWin.container.querySelector("#ex1").value = Math.floor(exportWin.container.querySelector("#ex1").value) });
        exportWin.container.querySelector("#ex2").addEventListener("blur", () => { exportWin.container.querySelector("#ex2").value = Math.floor(exportWin.container.querySelector("#ex2").value) });
        exportWin.container.querySelector("#ey1").addEventListener("blur", () => { exportWin.container.querySelector("#ey1").value = Math.floor(exportWin.container.querySelector("#ey1").value) });
        exportWin.container.querySelector("#ey2").addEventListener("blur", () => { exportWin.container.querySelector("#ey2").value = Math.floor(exportWin.container.querySelector("#ey2").value) });
        exportWin.container.querySelector("button").addEventListener("click", () => {
            count++;
            let resultingImageWindow = OWOP.windowSys.addWindow(new OWOP.windowSys.class.window(`Resulting Image #${count}`, { closeable: true }, async (resultingWin) => {
                resultingWin.container.className = 'wincontainer centeredChilds';
                let image = document.createElement("img");
                if (exportWin.container.querySelector("#esel").value == "Chunks") {
                    image.src = await exportArea(exportWin.container.querySelector("#ex1").value, exportWin.container.querySelector("#ey1").value, exportWin.container.querySelector("#ex2").value, exportWin.container.querySelector("#ey2").value);
                } else {
                    function pxToChunk(n) {
                        return Math.floor(n / 16);
                    }
                    image.src = await exportArea(pxToChunk(exportWin.container.querySelector("#ex1").value), pxToChunk(exportWin.container.querySelector("#ey1").value), pxToChunk(exportWin.container.querySelector("#ex2").value), pxToChunk(exportWin.container.querySelector("#ey2").value));
                }
                image.onload = () => {
                    if (image.width < image.height) image.height = 500; else image.width = 500;
                    OWOP.windowSys.centerWindow(resultingImageWindow);
                };
                resultingWin.addObj(image);
            }));
            OWOP.windowSys.centerWindow(resultingImageWindow);
        });
    }));
    exportWin.move(innerWidth - exportWin.realw - 10, 60);
}