Sploop.io All Skins Unlocked

Unlocks ALL skins in the game! Select any skins you want!

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Greasemonkey 油猴子Violentmonkey 暴力猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Violentmonkey 暴力猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Userscripts ,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展后才能安装此脚本。

(我已经安装了用户脚本管理器,让我安装!)

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

(我已经安装了用户样式管理器,让我安装!)

// ==UserScript==
// @name Sploop.io All Skins Unlocked
// @author Murka
// @description Unlocks ALL skins in the game! Select any skins you want!
// @icon https://sploop.io/img/ui/favicon.png
// @version 0.3
// @match *://sploop.io/*
// @run-at document-start
// @grant none
// @noframes
// @license MIT
// @namespace https://greasyfork.org/users/919633
// ==/UserScript==
/* jshint esversion:8 */

/*
    Author: Murka
    Github: https://github.com/Murka007
    Discord: https://discord.gg/cPRFdcZkeD
    Greasyfork: https://greasyfork.org/en/users/919633

    Description:
    - Your not purchased skins will not be shown in the `SKINS` tab
    - You can select any skins you want by using `SHOP` tab
    - It is impossible to make selected skins to show everyone
    - Script can stop working after update, if you will have any issues, report about it in my DISCORD
*/

(function() {
    "use strict";

    const log = console.log;

    function createHook(target, prop, setter) {
        if (!window.hooks) {
            window.hooks = {
                setter: [],
                getter: []
            };
        }
        window.hooks.setter.push(setter);

        const symbol = Symbol(prop);
        Object.defineProperty(target, prop, {
            get() {
                return this[symbol];
            },
            set(value) {
                for (const setter of window.hooks.setter) {
                    setter(this, symbol, value);
                }
            },
            configurable: true
        })
    }

    function inGame() {
        const homepage = document.querySelector("#homepage");
        return homepage && homepage.style.display !== "flex";
    }

    function sleep(ms) {
        return new Promise(resolve => setTimeout(resolve, ms));
    }

    const storage = {
        get(key) {
            const value = localStorage.getItem(key);
            return value === null ? null : JSON.parse(value);
        },
        set(key, value) {
            localStorage.setItem(key, JSON.stringify(value));
        }
    };

    // it might change in the next update
    // jn - skin
    // jo - accessory
    // jq - back

    const INDEX = {
        SKIN: 16,
        ACCESSORY: 17,
        BACK: 19
    };

    const KEY = {
        SKIN: null,
        ACCESSORY: null,
        BACK: null
    };

    function defineProperty(target, prop, value) {
        Object.defineProperty(target, prop, {
            get() {
                return value;
            },
            set() {
                return true;
            },
            configurable: true,
            writeable: false
        })
    }

    // Dragon skin, Mask accessory and Dragon Wings by Default, why not?
    const settings = (function() {
        function createSettings() {
            const settings = {};
            settings.skin = 27;
            settings.accessory = 30;
            settings.back = 2;
            return settings;
        }

        const defaultSettings = createSettings();
        const settings = Object.assign({}, defaultSettings, storage.get("selectedSkins"));
        for (const key in settings) {
            if (!defaultSettings.hasOwnProperty(key)) {
                delete settings[key];
            }
        }

        storage.set("selectedSkins", settings);
        return settings;
    })();

    const myPlayer = {
        id: null,
        data: null
    };

    window.WebSocket = new Proxy(WebSocket, {
        construct(target, args) {
            const socket = new target(...args);
            socket.addEventListener("message", function(event) {
                try {
                    const data = JSON.parse(event.data);
                    if (data[0] === 35) {
                        myPlayer.id = data[1];
                    }
                } catch(err) {}
            })
            return socket;
        }
    })

    function getDefault() {
        return {
            skin: storage.get("skin") || 0,
            accessory: storage.get("accessory") || 0,
            back: storage.get("back") || 0
        };
    }

    createHook(Object.prototype, "i", async function(that, symbol, value) {
        that[symbol] = value;
        if (myPlayer.id === value) {
            myPlayer.data = that;
            await sleep(0);

            const skinData = storage.get("selectedSkins");
            const Default = getDefault();

            const keys = Object.keys(that);
            if (!KEY.SKIN) KEY.SKIN = keys[INDEX.SKIN];
            if (!KEY.ACCESSORY) KEY.ACCESSORY = keys[INDEX.ACCESSORY];
            if (!KEY.BACK) KEY.BACK = keys[INDEX.BACK];

            const current = {
                skin: that[KEY.SKIN] || 0,
                accessory: that[KEY.ACCESSORY] || 0,
                back: that[KEY.BACK] || 0
            };

            // Skin will not change if you are not logged into your account
            // And also you can't even select any skins without account, except default one
            defineProperty(that, KEY.SKIN, skinData.skin || Default.skin);
            defineProperty(that, KEY.ACCESSORY, skinData.accessory || Default.accessory);
            defineProperty(that, KEY.BACK, skinData.back || Default.back);
        }
    });

    // We need to reset our skins on death/change server
    function resetSkins() {
        if (myPlayer.data === null) return;
        const { skin, accessory, back } = getDefault();
        defineProperty(myPlayer.data, KEY.SKIN, skin);
        defineProperty(myPlayer.data, KEY.ACCESSORY, accessory);
        defineProperty(myPlayer.data, KEY.BACK, back);
    }

    window.addEventListener("load", function() {

        const changeServer = document.querySelector("#change-server");
        const mainContent = document.querySelector("#main-content");
        const homepage = document.querySelector("#homepage");

        // Attach click event handler to make users select their skins
        new MutationObserver(mutations => {
            for (const mutation of mutations) {
                for (const node of mutation.addedNodes) {
                    try {
                        if (node.classList.contains("skins-line")) {
                            for (const button of node.children) {
                                button.addEventListener("click", function() {
                                    const [ match, type, id ] = button.src.match(/(skin|accessory|back)(\d+)/);
                                    settings[type] = Number(id);
                                    storage.set("selectedSkins", settings);
                                })
                            }
                        }
                    } catch(err){}
                }
            }
        }).observe(mainContent, { childList: true, subtree: true });

        // Reset myPlayer if user has changed server
        changeServer.addEventListener("click", resetSkins);

        // Also reset if player died
        new MutationObserver(mutations => {
            for (const mutation of mutations) {
                if (mutation.target.style.display === "flex") {
                    resetSkins();
                }
            }
        }).observe(homepage, { attributes: true, attributeFilter: ["style"] });
    })

})();