Waypoints [Sploop.io]

Easily traverse the world of Sploop.io with Waypoints!

Tendrás que instalar una extensión para tu navegador como Tampermonkey, Greasemonkey o Violentmonkey si quieres utilizar este script.

Necesitarás instalar una extensión como Tampermonkey o Violentmonkey para instalar este script.

Necesitarás instalar una extensión como Tampermonkey o Violentmonkey para instalar este script.

Necesitarás instalar una extensión como Tampermonkey o Userscripts para instalar este script.

Necesitará instalar una extensión como Tampermonkey para instalar este script.

Necesitarás instalar una extensión para administrar scripts de usuario si quieres instalar este script.

(Ya tengo un administrador de scripts de usuario, déjame instalarlo)

Necesitará instalar una extensión como Stylus para instalar este estilo.

Necesitará instalar una extensión como Stylus para instalar este estilo.

Necesitará instalar una extensión como Stylus para instalar este estilo.

Necesitará instalar una extensión del gestor de estilos de usuario para instalar este estilo.

Necesitará instalar una extensión del gestor de estilos de usuario para instalar este estilo.

Necesitará instalar una extensión del gestor de estilos de usuario para instalar este estilo.

(Ya tengo un administrador de estilos de usuario, déjame instalarlo)

// ==UserScript==
// @name         Waypoints [Sploop.io]
// @name:zh      航点 [Sploop.io]
// @name:tr      Yolu Göstergeleri [Sploop.io]
// @name:ru      Точки маршрута [Sploop.io]
// @name:vi      Điểm chỉ đường [Sploop.io]
// @name:ar      علامات الطريق [Sploop.io]
// @description  Easily traverse the world of Sploop.io with Waypoints!
// @description:zh 轻松通过航点在Sploop.io世界中穿行!
// @description:tr  Kolayca Sploop.io dünyasında yol göstergeleri ile gezin!
// @description:ru  Легко путешествуйте по миру Sploop.io с помощью точек маршрута!
// @description:vi  Dễ dàng di chuyển trong thế giới Sploop.io với các điểm chỉ đường!
// @description:ar  اجتاز عالم Sploop.io بسهولة باستخدام علامات الطريق!
// @version      2.0.0
// @author       Hori + viper
// @match        https://sploop.io/
// @icon         https://i.ibb.co/ym4LXJF7/Waypoints-3-removebg-preview.png
// @grant        none
// @run-at       document-start
// @license      MIT
// @namespace https://greasyfork.org/users/1441649
// ==/UserScript==

/**
 *  Authors: horizion, viper
 *
 *  Menu Keybind : ESCAPE
 *
 *  (!) - You are able to modify the script in any way, as long as it abides by the in-game rules.
 *  (!) - Contact Horizion if the script does not work.
 *
 *  Discord: horizon2025.#0000 | <@880415818142347297>
 *
 *  Official Sploop.io Discord Server: https://discord.com/invite/CYadgpyv78
 */

(function () {
	"use strict";
	const win = window;
	const log = console.log;
	const version = `2.0.0`;

	const ARROW_DISTANCE_FROM_PLAYER = 210;
	const COLORS = [
		"red",
		"orange",
		"yellow",
		"green",
		"dark_blue",
		"light_blue",
		"purple",
		"violet",
		"pink",
		"rose",
	];
	const MARKER_IMAGES = [
		"https://i.ibb.co/20h3vpRG/map-cross-red.png",
		"https://i.ibb.co/VWZCzbgK/map-cross-orange.png",
		"https://i.ibb.co/x8jT4J4x/map-cross-yellow.png",
		"https://i.ibb.co/dstqTPQP/map-cross-darkgreen.png",
		"https://i.ibb.co/dsSZtg0Q/map-cross-darkblue.png",
		"https://i.ibb.co/4RPBRMH7/map-cross-lightblue.png",
		"https://i.ibb.co/84KYxdNd/map-cross-purple.png",
		"https://i.ibb.co/8LJ4FbbW/map-cross-indigo.png",
		"https://i.ibb.co/bgNv5NB7/map-cross-pink.png",
		"https://i.ibb.co/5WMsX1f1/map-cross-main.png",
	];
	const ARROW_IMAGES = [
		"https://i.ibb.co/chgGndnF/arrow-red.png",
		"https://i.ibb.co/LdkChynd/arrow-orange.png",
		"https://i.ibb.co/v6x76XN6/arrow-yellow.png",
		"https://i.ibb.co/2YYvqNHj/arrow-darkgreen.png",
		"https://i.ibb.co/6RrD7MvR/arrow-darkblue.png",
		"https://i.ibb.co/9kqtG2k4/arrow-lightblue.png",
		"https://i.ibb.co/ZzjZhK0g/arrow-purple.png",
		"https://i.ibb.co/sdgDvP0Y/arrow-indigo.png",
		"https://i.ibb.co/yFFq6jBN/arrow-pink.png",
		"https://i.ibb.co/zT2rYdFT/arrow-main.png",
	];
	const MARKER_COLOR_IMAGES = {
		red: "https://i.ibb.co/ksRyM6FY/circle-red.png",
		orange: "https://i.ibb.co/4gNKXnp6/circle-orange.png",
		yellow: "https://i.ibb.co/60rHrBWP/circle-yellow.png",
		green: "https://i.ibb.co/kVGgcVKT/circle-green.png",
		dark_blue: "https://i.ibb.co/9mbLDCLq/circle-darkblue.png",
		light_blue: "https://i.ibb.co/LhNv7JXx/circle-lightblue.png",
		purple: "https://i.ibb.co/WvddsXyk/circle-purple.png",
		violet: "https://i.ibb.co/PZKjyN2g/circle-indigo.png",
		pink: "https://i.ibb.co/bf2LzgM/circle-pink.png",
		rose: "https://i.ibb.co/0pMvXrMS/circle-main.png",
	};

	let waypointID = 0;
	let waypoints = [];
	let cards = [];

	win.showWaypointArrows = 1;

	//** Styling DOM */

	const style = document.createElement("style");
	style.type = "text/css";
	style.textContent = `
        :root {
            --main: #de5978;
            --secondary: rgb(43, 37, 37, 1);
        }
        button:focus {
            border: none;
            outline: none;
        }
        input:focus {
            outline: none;
            border: none;
        }

        /* Making scroll bar more pro */

        ::-webkit-scrollbar {
            width: 7px;
        }

        ::-webkit-scrollbar-thumb {
            background-color: var(--main);
            border-top-right-radius: 10px;
            border-bottom-right-radius: 10px;
            border-top-left-radius: 0;
            border-bottom-left-radius: 0;
        }

        ::-webkit-scrollbar-track {
            background-color: var(--secondary);
            border-top-right-radius: 10px;
            border-bottom-right-radius: 10px;
            border-top-left-radius: 0;
            border-bottom-left-radius: 0;
        }

        /* Main menu styling */

        #wayMenu {
            display: none;
            position: fixed;
            top: 160px;
            left: 293px;
            z-index: 99999;
            padding: 20px;
            background: var(--secondary);
            border-radius: 10px;
            max-height: 60%;
            overflow-y: auto;
            width: 400px;
            border: 1.5px solid var(--main);
        }
        .wayHeader {
            display: flex;
            justify-content: space-between;
            align-items: center;
            margin-bottom: 10px;
        }
        .wayTitle {
            font-size: 20px;
            color: var(--main);
        }
        .version {
            font-size: 15px;
            color: rgba(100, 100, 100,1);
        }
        .wayArrowToggle {
            display: flex;
            align-items: center;
            gap: 5px;
            opacity: 0;
            transition: all 0.3s ease-in-out;
            pointer-events: none;
        }
        .wayArrowToggle i {
            color: var(--main);
            font-size: 18px;
        }

        .switch {
            position: relative;
            display: inline-block;
            width: 40px;
            height: 20px;
        }

        .switch input {
            opacity: 0;
            width: 0;
            height: 0;
        }

        .slider {
            position: absolute;
            cursor: pointer;
            top: 0;
            left: 0;
            right: 0;
            bottom: 0;
            background-color: rgb(41, 41, 41);
            transition: 0.3s;
            border-radius: 20px;
            border: 1px solid rgb(112, 53, 74);
        }

        .slider:before {
            position: absolute;
            content: "";
            height: 14px;
            width: 14px;
            left: 3px;
            background-color: var(--main);
            bottom: 3px;
            transition: 0.3s;
            border-radius: 50%;
        }

        input:checked + .slider {
            background-color: rgba(187, 75, 101,0.6);
        }

        input:checked + .slider:before {
            transform: translateX(20px);
        }
        .wayCreateNewButton {
            margin-top: 25px;
            border: none;
            color: white;
            width: 250px;
            text-align: center;
            border-radius: 10px;
            padding: 7px;
            background: var(--main);
            font-size: 18px;
            cursor: pointer;
            font-weight: 990;
            height:35px;
            transition: transform 0.4s ease;
            border: 1px solid var(--secondary);
        }
        .wayCreateNewButton:hover {
            transform: translateY(-1px);
        }
        .wayCreateNewButton:disabled {
            background:rgb(187, 75, 101);
            color:rgba(156, 156, 156, 0.99);
            cursor: not-allowed;
            opacity: 0.85;
            transform: none;
        }
        .wayNotFound {
            text-align: center;
            display: block;
            font-size: 15px;
            color: white;
            margin-top: 20px;
        }
        .wayCreate {
        text-align: center;
        }

        /* popup styling */

        #wayPopMenu {
            display: block;
            position: fixed;
            top: 50%;
            left: 50%;
            transform: translate(-50%, -50%);
            padding: 25px;
            background: var(--secondary);
            border-radius: 10px;
            max-height: 50%;
            overflow-y: auto;
            text-align: center;
        }
        .popupHeader {
            position: absolute;
            top: 0;
            right: 8px;
            margin-bottom: 8px;
        }
        .closePopup {
            font-size: 24px;
            font-weight: bold;
            cursor: pointer;
            color: #fff;
        }
        .popupHeader:hover {
            transform: translateY(-1px);
        }
        #markerName {
            border-radius: 10px;
            height:35px;
            text-align: center;
            font-size: 16px;
            border: none;
            width: 200px;
        }
        #wayColors {
            padding: 7px;
            height:35px;
            width: 200px;
            border: none;
            border-radius: 10px;
            font-size: 16px;
            margin-bottom: 16px;
            background: #fff;
            color: #333;
            -webkit-appearance: none;
            -moz-appearance: none;
            appearance: none;
            text-align: center;
            font-weight: 800;
            cursor: pointer;
        }
        .wayCreateButton {
            border: none;
            color: white;
            width: 200px;
            text-align: center;
            border-radius: 10px;
            padding: 7px;
            background: var(--main);
            cursor: pointer;
            font-weight: 560;
            font-size: 16px;
            height:35px;
            transition: transform 0.4s ease;
        }
        .wayCreateButton:focus{
            border:none;
        }
        .wayCreateButton:hover {
            transform: translateY(-1px);
        }

        /* Card Styling */

        .card {
            display: flex;
            align-items: center;
            background:rgb(41, 41, 41);
            border-radius: 10px;
            padding: 10px;
            margin-top: 10px;
            color: white;
            border: 1px solid rgb(112, 53, 74);
        }

        .card img {
            width: 22px;
            height: 22px;
            margin-right: 10px;
            border-radius: 50%;
        }

        .card .card-name {
            flex: 4;
            font-size: 16px;
            margin-right: 10px;
            max-width: 145px;
        }

        .card .card-coords {
            flex: 3;
            font-size: 16px;
            margin-right: 10px;
            color: rgba(100, 100, 100,1);
        }

        .card .card-remove {
            background: #e43939;
            border: none;
            color: white;
            padding: 5px 10px;
            border-radius: 5px;
            cursor: pointer;
        }
        .card .card-remove:hover {
            transform: translateY(-1px);
        }
        #trash:hover {
            cursor: pointer;
        }
    `;
	document.head.appendChild(style);

	//** Main */

	document.addEventListener("DOMContentLoaded", () => {
		log(
			`%cWaypoints %cVersion ${version}\n%cCreators: %cviper + Hori`,
			"font-size: 90px; font-weight: 900; color: #de5978;",
			"font-size: 20px; color: gray;",
			"font-size: 20px; font-weight: 900; color:rgb(255, 255, 255); margin-top: 8px; margin-bottom: 10px;",
			"font-size: 20px; color:rgb(235, 187, 97); margin-top: 8px; margin-bottom: 10px;"
		);

		const wayMenu = document.createElement("div");
		wayMenu.id = "wayMenu";
		wayMenu.innerHTML = `
            <div class="wayHeader">
                <span class="wayTitle">Waypoints <span class="version">- v${version}</span></span>
                <div class="wayArrowToggle">
                    <i class="fa-solid fa-location-arrow"></i>
                    <label class="switch">
                        <input type="checkbox" id="waypointToggle" checked>
                        <span class="slider"></span>
                    </label>
                </div>
            </div>
            <div class="wayContent">
                <div id="wayNotFound" class="wayNotFound">
                    No Waypoints Found
                    <i class="fa-solid fa-magnifying-glass"></i>
                </div>
            </div>
            <div class="wayCreate">
                <button id="wayCreateNewButton" class="wayCreateNewButton" disabled>+</button>
            </div>
            <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.3.0/css/all.min.css" integrity="sha512-SzlrxWUlpfuzQ+pcUCosxcglQRNAq/DZjVsC0lE40xsADsfeQoEypE+enwcOiGjk/bSuGGKHEyjSoQ1zVisanQ==" crossorigin="anonymous" referrerpolicy="no-referrer" />
        `;
		document.body.append(wayMenu);

		let isDragging = false,
			displayToggled = false,
			offsetX,
			offsetY;

		wayMenu.addEventListener("mousedown", function (event) {
			isDragging = true;
			offsetX = event.clientX - wayMenu.getBoundingClientRect().left;
			offsetY = event.clientY - wayMenu.getBoundingClientRect().top;
		});
		document.addEventListener("mousemove", function (event) {
			if (isDragging) {
				const newX = event.clientX - offsetX;
				const newY = event.clientY - offsetY;
				wayMenu.style.left = newX + "px";
				wayMenu.style.top = newY + "px";
			}
		});
		document.addEventListener("mouseup", () => {
			isDragging = false;
		});

		document
			.getElementById("wayCreateNewButton")
			.addEventListener("click", () => {
				if (!document.getElementById("wayPopMenu")) {
					createPopup();
				}
			});

		document.addEventListener("keydown", (event) => {
			if (event.key === "Escape" && !isChatWrapperVisible()) {
				wayMenu.style.display = displayToggled ? "none" : "block";
				displayToggled = !displayToggled;
			}
		});

		document
			.getElementById("waypointToggle")
			.addEventListener("change", function () {
				if (this.checked) {
					win.showWaypointArrows = 1;
				} else {
					win.showWaypointArrows = 0;
				}
			});

		const observer = new MutationObserver(() => {
			updateCreateButton();
		});
		const homepage = document.getElementById("homepage");
		if (homepage) {
			observer.observe(homepage, {
				attributes: true,
				attributeFilter: ["style"],
			});
		}
	});

	const addWaypoint = new Proxy(waypoints, {
		set(target, property, value, receiver) {
			const result = Reflect.set(target, property, value, receiver);
			if (!isNaN(property)) {
				createCard(value);
			}
			return result;
		},
	});

	function createPopup() {
		let wayPopMenu = document.createElement("div");
		wayPopMenu.id = "wayPopMenu";
		wayPopMenu.innerHTML = `
            <div class="popupHeader">
                <span id="closePopup" class="closePopup">x</span>
            </div>
            <div class="inputContainer">
                <input type="text" id="markerName"placeholder="New Waypoint" maxLength="13"><br><br>
            </div>
            <div class="wayColor">
                <select name="colors" id="wayColors">
                    <option value="0">Red</option>
                    <option value="1">Orange</option>
                    <option value="2">Yellow</option>
                    <option value="3">Green</option>
                    <option value="4">Dark Blue</option>
                    <option value="5">Light Blue</option>
                    <option value="6">Purple</option>
                    <option value="7">Violet</option>
                    <option value="8">Pink</option>
                    <option value="9">Rose</option>
                </select>
            </div>
            <div class="wayCreate">
                <button id="createButton" class="wayCreateButton">Create</button>
            </div>
        `;
		document.body.appendChild(wayPopMenu);

		document
			.getElementById("markerName")
			.addEventListener("keydown", function (event) {
				event.stopPropagation();
			});
		document.getElementById("closePopup").addEventListener("click", () => {
			wayPopMenu.remove();
		});
		document
			.getElementById("createButton")
			.addEventListener("click", getOption);
	}

	function createCard(waypoint) {
		cards.push({
			name: waypoint.name,
			id: waypoint.id,
		});

		const card = document.createElement("div");
		card.className = "card";
		card.innerHTML = `
            <img src="${MARKER_COLOR_IMAGES[waypoint.color]}" alt="color">
            <div class="card-name">${waypoint.name}</div>
            <div class="card-coords">[${Math.round(waypoint.x)},${Math.round(waypoint.y)}]</div>
            <button class="card-remove"><i class="fa-solid fa-trash" id="trash"></i></button>
        `;

		document.querySelector(".wayContent").appendChild(card);

		updateArrowToggleAlpha();

		const removeButton = card.querySelector(".card-remove");

		removeButton.addEventListener("click", function () {
			const waypointIndex = waypoints.findIndex(
				(way) => way.id === waypoint.id
			);
			if (waypointIndex > -1) {
				waypoints.splice(waypointIndex, 1);
			}
			const cardIndex = cards.findIndex(
				(card) => card.id === waypoint.id
			);
			if (cardIndex > -1) {
				cards.splice(cardIndex, 1);
			}
			card.remove();
			if (cards.length === 0) {
				document.getElementById("wayNotFound").style.display = "block";
			}
			updateArrowToggleAlpha();
		});
	}

	function createWaypoint(name, color) {
		const chosenMarker = MARKER_IMAGES[color];
		const chosenArrow = ARROW_IMAGES[color];

		const markerImg = createImage(chosenMarker);
		const arrowImg = createImage(chosenArrow);

		const newWaypoint = {
			name,
			id: waypointID++,
			arrow: arrowImg,
			marker: markerImg,
			x: win.coords.x,
			y: win.coords.y,
			color: COLORS[color],
		};

		addWaypoint.push(newWaypoint);
	}
	function getOption() {
		const markerName = document.getElementById("markerName").value;
		const wayColor = document.getElementById("wayColors");
		const color = wayColor.value;
		if (markerName !== "") {
			createWaypoint(markerName, color);
			document.getElementById("wayNotFound").style.display = "none";
			document.getElementById("wayPopMenu").remove();
		}
	}
	function updateCreateButton() {
		const button = document.getElementById("wayCreateNewButton");
		const homepage = document.getElementById("homepage");

		if (homepage && homepage.style.display === "flex") {
			button.disabled = true;
		} else {
			button.disabled = false;
		}
	}
	function updateArrowToggleAlpha() {
		const toggle = document.querySelector(".wayArrowToggle");
		if (toggle) {
			toggle.style.opacity = cards.length === 0 ? "0" : "1";
			toggle.style.pointerEvents = cards.length === 0 ? "none" : "auto";
		}
	}
	function createImage(URL) {
		const image = new Image();
		image.src = URL;
		return image;
	}
	function isChatWrapperVisible() {
		return (
			document.getElementById("chat-wrapper").style.display === "block"
		);
	}

	//** Draw Waypoints, Arrows, Names */

	class Drawing {
		arrows(context, arrow, angle, x, y) {
			context.save();
			context.globalAlpha = 0.52;
			context.translate(x, y);
			context.rotate(angle);
			context.drawImage(arrow, -25 / 2, -25 / 2, 25, 25);
			context.restore();
		}
		name(context, waypoint) {
			const x = waypoint.x;
			const y = waypoint.y - 42;

			context.font = "24px Baloo Paaji";
			context.textAlign = "center";
			context.lineWidth = 8;
			context.strokeStyle = "#414141";
			context.lineJoin = "round";
			context.strokeText(waypoint.name, x, y);
			context.fillStyle = "white";
			context.fillText(waypoint.name, x, y);
		}
		waypoint(context, waypoint) {
			context.drawImage(
				waypoint.marker,
				waypoint.x - 70 / 2,
				waypoint.y - 70 / 2,
				70,
				70
			);
		}
	}
	const Draw = new Drawing();

	win.draw = (context) => {
		waypoints.forEach((waypoint) => {
			const distance = Math.hypot(
				waypoint.x - win.coords.x,
				waypoint.y - win.coords.y
			);

			if (distance < 1200) {
				Draw.waypoint(context, waypoint);
				Draw.name(context, waypoint);
			}
			if (win.showWaypointArrows && distance > 450) {
				const angle = Math.atan2(
					waypoint.y - win.coords.y,
					waypoint.x - win.coords.x
				);
				let radius = ARROW_DISTANCE_FROM_PLAYER;

				const x = win.coords.x + Math.cos(angle) * radius;
				const y = win.coords.y + Math.sin(angle) * radius;

				Draw.arrows(context, waypoint.arrow, angle, x, y);
			}
		});
	};

	//** Hooking onto the game */

	const TYPEOF = (value) =>
		Object.prototype.toString.call(value).slice(8, -1).toLowerCase();
	const NumberSystem = [
		{
			radix: 2,
			prefix: "0b0*",
		},
		{
			radix: 8,
			prefix: "0+",
		},
		{
			radix: 10,
			prefix: "",
		},
		{
			radix: 16,
			prefix: "0x0*",
		},
	];
	class Regex {
		constructor(code, unicode, namespace) {
			if (!namespace)
				namespace =
					"new_script_" + Math.random().toString(36).substr(2, 8);
			this.code = code;
			this.COPY_CODE = code;
			this.unicode = unicode || false;
			this.hooks = {};
			this.namespace = namespace;
			this.totalHooks = 0;
		}

		static parseValue(value) {
			try {
				return Function(`return (${value})`)();
			} catch (err) {
				return null;
			}
		}

		isRegexp(value) {
			return TYPEOF(value) === "regexp";
		}

		generateNumberSystem(int) {
			const template = NumberSystem.map(
				({ prefix, radix }) => prefix + int.toString(radix)
			);
			return `(?:${template.join("|")})`;
		}

		parseVariables(regex) {
			regex = regex.replace(/\{VAR\}/g, "(?:let|var|const)");
			regex = regex.replace(/\{QUOTE\}/g, "['\"`]");
			regex = regex.replace(/ARGS\{(\d+)\}/g, (...args) => {
				let count = Number(args[1]),
					arr = [];
				while (count--) arr.push("\\w+");
				return arr.join("\\s*,\\s*");
			});
			regex = regex.replace(/NUMBER\{(\d+)\}/g, (...args) => {
				const int = Number(args[1]);
				return this.generateNumberSystem(int);
			});
			return regex;
		}

		_hookName(name) {
			return `${this.namespace}:${name}`;
		}

		format(name, inputRegex, flags) {
			this.totalHooks++;
			let regex = "";
			if (Array.isArray(inputRegex)) {
				regex = inputRegex
					.map((exp) => (this.isRegexp(exp) ? exp.source : exp))
					.join("\\s*");
			} else if (this.isRegexp(inputRegex)) {
				regex = inputRegex.source;
			}
			regex = this.parseVariables(regex);
			if (this.unicode) {
				regex = regex.replace(/\\w/g, "(?:[^\\x00-\\x7F-]|\\$|\\w)");
			}
			const expression = new RegExp(
				regex.replace(/\{INSERT\}/, ""),
				flags
			);
			return regex.includes("{INSERT}")
				? new RegExp(regex, flags)
				: expression;
		}

		template(type, name, regex, substr) {
			const hookName = this._hookName(name);
			const expression = new RegExp(
				`(${this.format(hookName, regex).source})`
			);
			const match = this.code.match(expression) || [];
			this.code = this.code.replace(
				expression,
				type === 0 ? "$1" + substr : substr + "$1"
			);
			this.hooks[hookName] = { expression, match };
			return match;
		}

		logHooks() {
			log(
				"%cApplied Hooks:",
				"font-weight: bold; font-size: 24px; color:rgb(255, 255, 255); margin-top: 20px;"
			);
			let index = 1;
			for (const hookName in this.hooks) {
				const hook = this.hooks[hookName];
				const matches = Array.isArray(hook.match)
					? hook.match
					: [hook.match];
				const status =
					matches && matches.length > 0
						? "successful"
						: "unsuccessful";
				const style =
					matches && matches.length > 0
						? "color:rgb(134, 204, 125); font-size: 14px;"
						: "color:rgb(221, 94, 113); font-size: 14px;";
				log(`%c${index}. ${hookName} | ${status}`, style);
				index++;
			}
			log(
				"%c-----------------",
				"font-weight: bold; color: gray; margin-bottom: 5px; margin-top: 20px;"
			);
		}

		match(name, regex, flags) {
			const hookName = this._hookName(name);
			const expression = this.format(hookName, regex, flags);
			const match = this.code.match(expression) || [];
			this.hooks[hookName] = { expression, match };
			return match;
		}

		replace(name, regex, substr, flags) {
			const hookName = this._hookName(name);
			const expression = this.format(hookName, regex, flags);
			const preMatch = this.code.match(expression) || [];
			this.code = this.code.replace(expression, substr);
			this.hooks[hookName] = {
				expression,
				match: preMatch,
				replaced: preMatch.length > 0,
			};
			return this.code.match(expression) || [];
		}

		append(name, regex, substr) {
			return this.template(0, name, regex, substr);
		}

		prepend(name, regex, substr) {
			return this.template(1, name, regex, substr);
		}
	}

	const applyHooks = (code) => {
		const namespace = `waypoints_v${version}`;
		const Hook = new Regex(window.HOOKED_CODE || code, true, namespace);

		window.COPY_CODE = (Hook.COPY_CODE.match(
			/^(\(function \w+\(\w+\)\{.+)\(.+?\);$/
		) || [])[1];
		// log(Hook.COPY_CODE);

		if (!window.HOOKED_CODE)
			Hook.append(
				"EXTERNAL fix",
				/\(function (\w+)\(\w+\)\{/,
				"let $2 = eval(`(() => ${COPY_CODE})()`); delete window.COPY_CODE;"
			);
		Hook.replace("LOADER", /Loading Sploop.io/, `Loading...`);
		Hook.append(
			"COORDS",
			/,this\.\w{2}=0\},this\.\w{2}=\w+\((\w),(\w),\w\)\{/,
			`window.coords = { x: $2, y: $3 };`
		);
		Hook.prepend("DRAW",/\w=\w\[\w\(\)\.\w+\],\w=\w.{7,8};for\(let \w=\d;\w\W\w;\w\+\+\)!\(\w\[\w\]\.\w+&\w\(\)\.\w+\)&&\w+\(\w\[\w\],(\w),\w\);/,"/*55306335646D457A5457645A4D3070735756685362467044516D6C6C553046335A555657616D4649546C5A50626B70365756453950513D3D*/window.draw($2);");

		// log(Hook.code);
		window.HOOKED_CODE = Hook.code;

		Hook.logHooks();
		return Hook.code;
	};
	window.eval = new Proxy(window.eval, {
		apply(target, _this, args) {
			const code = args[0];
			if (code.length > 1e5) {
				args[0] = applyHooks(code);
				window.eval = target;
				target.apply(_this, args);
				return;
			}
			return target.apply(_this, args);
		},
	});
})();