MiniClient - Syringe

Enhance your MiniBlox experience

您需要先安裝使用者腳本管理器擴展,如 TampermonkeyGreasemonkeyViolentmonkey 之後才能安裝該腳本。

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyViolentmonkey 後才能安裝該腳本。

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyViolentmonkey 後才能安裝該腳本。

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyUserscripts 後才能安裝該腳本。

你需要先安裝一款使用者腳本管理器擴展,比如 Tampermonkey,才能安裝此腳本

您需要先安裝使用者腳本管理器擴充功能後才能安裝該腳本。

(我已經安裝了使用者腳本管理器,讓我安裝!)

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

(我已經安裝了使用者樣式管理器,讓我安裝!)

// ==UserScript==
// @name         MiniClient - Syringe
// @namespace    http://tampermonkey.net/
// @version      1.1
// @description  Enhance your MiniBlox experience
// @author       Syringe (Drapinqs)
// @match        https://miniblox.io/*
// @icon         https://cdn.discordapp.com/attachments/741464074986192918/1266539478781136958/miniclient75.png?ex=66a5844e&is=66a432ce&hm=8b9f2b3837225050260a044829102506f44cc6fd6b8ffa706052de573e626feb&
// @grant        GM_setValue
// @grant        GM_getValue
// @grant        GM_addStyle
// @run-at       document-start
// ==/UserScript==

(function() {
    'use strict';

    // Initial settings
    if (!GM_getValue("settings")) {
        GM_setValue("settings", JSON.stringify({
            inverted: false,
            showFps: true,  // Set default to true to show FPS by default
            showCrosshair: false,
            crosshairUrl: '',
            imageUrl: ''
        }));
    }

    // Add styles
    GM_addStyle(`
        .mbclient-settings {
            display: flex;
            padding: 10px;
            flex-direction: column;
            width: 95%;
            color: white;
            text-shadow: 1px 1px 0 black;
            background-color: rgba(31, 31, 31, 0.9);
            border: 3px solid rgb(174, 0, 255);
            border-radius: 10px;
            cursor: move;
        }

        .mbclient-setting {
            display: flex;
            justify-content: space-between;
            align-items: center;
            width: full;
            padding: 0.25rem;
        }

        .checkbox-round {
            width: 1.3em;
            height: 1.3em;
            background-color: rgb(255, 87, 87);
            border-radius: 20%;
            vertical-align: middle;
            border: 1px solid #ddd;
            appearance: none;
            -webkit-appearance: none;
            outline: none;
            cursor: pointer;
            transition: all ease 0.3s;
        }

        .checkbox-round:checked {
            background-color: rgb(134, 255, 78);
        }

        #fps-container {
            background-color: rgba(0, 0, 0, 0.7);
            color: white;
            padding: 5px;
            border: 3px solid rgb(174, 0, 255);
            border-radius: 5px;
            font-size: 14px;
            position: fixed;
            top: 4px;
            left: 918px;
            z-index: 9999;
            cursor: move;
            display: block; /* Set to block by default */
        }

        /* Inverted colors */
        .inverted {
            filter: invert(1) hue-rotate(180deg);
        }

        /* Crosshair */
        #crosshair {
            position: fixed;
            top: 50%;
            left: 50%;
            transform: translate(-50%, -50%);
            pointer-events: none;
            z-index: 9999;
            display: none;
        }
    `);

    // Function to create settings panel
    function createSettingsPanel() {
        const settingsHTML = `
            <div id="settings" class="mbclient-settings">
                <p style="font-weight: bold; text-align: center; line-height: 95%; font-size: 140%;">MiniClient<br>by Syringe</p>
                <p style="text-align: center; font-size: 100%; color: rgb(219, 60, 48);"><i>use F8 to show/hide</i></p>
                <div class="mbclient-setting">
                    <label for="image-url">Custom Background</label>
                    <input style="width: 40%; border: 2px solid rgb(174, 0, 255); border-radius: 10px;" id="image-url" type="text" placeholder=" Enter image URL" />
                </div>
                <div class="mbclient-setting">
                    <label for="crosshair-url">Custom Crosshair</label>
                    <input style="width: 40%; border: 2px solid rgb(174, 0, 255); border-radius: 10px;" id="crosshair-url" type="text" placeholder=" Enter image URL" />
                </div>
                <div class="mbclient-setting">
                    <label for="inverted">Inverted Colors</label>
                    <input id="inverted" data-setting="inverted" type="checkbox" class="checkbox-round" value="false" />
                </div>
                <div class="mbclient-setting">
                    <label for="show-fps">Show FPS</label>
                    <input id="show-fps" data-setting="showFps" type="checkbox" class="checkbox-round" />
                </div>
            </div>
        `;
        const settingsElement = document.createElement('div');
        settingsElement.innerHTML = settingsHTML;
        settingsElement.style.position = "fixed";
        settingsElement.style.zIndex = "9999";
        settingsElement.style.top = "1rem";
        settingsElement.style.left = "1rem";
        document.body.appendChild(settingsElement);

        // Make sure settingsElement exists before trying to make it draggable
        if (settingsElement) {
            makeDraggable(settingsElement, "settingsTop", "settingsLeft");
        }

        // Add event listener for toggling the settings panel
        document.addEventListener('keydown', (event) => {
            if (event.key === "F8") {
                settingsElement.style.display = settingsElement.style.display === "none" ? "block" : "none";
            }
        });

        // Handle image URL input
        handleImageUrlInput();
        // Handle crosshair URL input
        handleCrosshairUrlInput();
        // Handle inverted colors
        handleInvertedColors();
        // Handle Show FPS
        handleShowFps();
    }

    // Function to make an element draggable
    function makeDraggable(element, localStorageKeyTop, localStorageKeyLeft) {
        let isDragging = false;
        let offsetX, offsetY;

        element.addEventListener('mousedown', (e) => {
            isDragging = true;
            offsetX = e.clientX - element.getBoundingClientRect().left;
            offsetY = e.clientY - element.getBoundingClientRect().top;
            element.style.cursor = 'grabbing';
        });

        document.addEventListener('mousemove', (e) => {
            if (isDragging) {
                const newX = e.clientX - offsetX;
                const newY = e.clientY - offsetY;
                const rect = element.getBoundingClientRect();
                const parentRect = document.documentElement.getBoundingClientRect();

                // Prevent the element from being dragged out of the viewport
                const maxX = parentRect.width - rect.width;
                const maxY = parentRect.height - rect.height;

                const clampedX = Math.max(0, Math.min(newX, maxX));
                const clampedY = Math.max(0, Math.min(newY, maxY));

                element.style.left = `${clampedX}px`;
                element.style.top = `${clampedY}px`;

                // Save the position in GM storage
                GM_setValue(localStorageKeyTop, `${clampedY}px`);
                GM_setValue(localStorageKeyLeft, `${clampedX}px`);
            }
        });

        document.addEventListener('mouseup', () => {
            isDragging = false;
            element.style.cursor = 'move';
        });

        // Restore position from GM storage
        const savedTop = GM_getValue(localStorageKeyTop, '4px');
        const savedLeft = GM_getValue(localStorageKeyLeft, '918px');
        element.style.top = savedTop;
        element.style.left = savedLeft;
    }

    // Function to handle image URL input
    function handleImageUrlInput() {
        const urlInput = document.getElementById('image-url');
        const defaultImage = 'https://cdn.discordapp.com/attachments/741464074986192918/1266326290475122791/default-92b37f60.png';

        if (urlInput) {
            const settings = JSON.parse(GM_getValue('settings'));
            urlInput.value = settings.imageUrl || '';

            urlInput.addEventListener('input', () => {
                const newUrl = urlInput.value.trim();
                settings.imageUrl = newUrl || defaultImage;
                GM_setValue('settings', JSON.stringify(settings));
                replaceImageSource(settings.imageUrl);
            });

            if (settings.imageUrl && settings.imageUrl !== defaultImage) {
                replaceImageSource(settings.imageUrl);
            }
        }
    }

    // Function to replace image source
    function replaceImageSource(newUrl) {
        const imgElements = document.querySelectorAll('img.chakra-image');
        imgElements.forEach(img => {
            if (img.src.includes('/assets/default-92b37f60.png')) {
                img.src = newUrl;
            }
        });
    }

    // Function to handle crosshair URL input
    function handleCrosshairUrlInput() {
        const crosshairInput = document.getElementById('crosshair-url');
        const settings = JSON.parse(GM_getValue('settings'));

        if (crosshairInput) {
            crosshairInput.value = settings.crosshairUrl || '';

            crosshairInput.addEventListener('input', () => {
                const newUrl = crosshairInput.value.trim();
                settings.crosshairUrl = newUrl;
                GM_setValue('settings', JSON.stringify(settings));
                updateCrosshair(newUrl);
            });

            if (settings.crosshairUrl) {
                updateCrosshair(settings.crosshairUrl);
            }
        }
    }

    // Function to update crosshair
    function updateCrosshair(url) {
        let crosshair = document.getElementById('crosshair');

        if (!crosshair) {
            crosshair = document.createElement('img');
            crosshair.id = 'crosshair';
            document.body.appendChild(crosshair);
        }

        crosshair.src = url;
        crosshair.style.display = url ? 'block' : 'none';
    }

    // Function to handle color inversion
    function handleInvertedColors() {
        const invertedCheckbox = document.getElementById('inverted');
        const settings = JSON.parse(GM_getValue('settings'));

        if (invertedCheckbox) {
            invertedCheckbox.checked = settings.inverted;

            invertedCheckbox.addEventListener('change', () => {
                settings.inverted = invertedCheckbox.checked;
                GM_setValue('settings', JSON.stringify(settings));
                document.body.classList.toggle('inverted', settings.inverted);
            });

            // Apply initial state
            if (settings.inverted) {
                document.body.classList.add('inverted');
            }
        }
    }

    // Function to handle Show FPS checkbox
    function handleShowFps() {
        const showFpsCheckbox = document.getElementById('show-fps');
        const settings = JSON.parse(GM_getValue('settings'));

        if (showFpsCheckbox) {
            showFpsCheckbox.checked = settings.showFps;

            showFpsCheckbox.addEventListener('change', () => {
                settings.showFps = showFpsCheckbox.checked;
                GM_setValue('settings', JSON.stringify(settings));
                toggleFpsDisplay(settings.showFps);
            });

            // Apply initial state
            if (settings.showFps) {
                toggleFpsDisplay(true);
            }
        }
    }

    // Function to show or hide FPS display
    function toggleFpsDisplay(show) {
        let fpsContainer = document.getElementById('fps-container');
        if (show) {
            if (!fpsContainer) {
                fpsContainer = document.createElement('div');
                fpsContainer.id = 'fps-container';
                fpsContainer.style.position = 'fixed';
                fpsContainer.style.top = '4px';
                fpsContainer.style.left = '918px';
                fpsContainer.style.zIndex = '9999';
                fpsContainer.style.cursor = 'move';
                fpsContainer.style.backgroundColor = 'rgba(0, 0, 0, 0.7)';
                fpsContainer.style.color = 'white';
                fpsContainer.style.padding = '5px';
                fpsContainer.style.border = '3px solid rgb(174, 0, 255)';
                fpsContainer.style.borderRadius = '5px';
                fpsContainer.style.fontSize = '14px';
                document.body.appendChild(fpsContainer);

                // Make the fpsContainer draggable
                makeDraggable(fpsContainer, 'fpsTop', 'fpsLeft');
            }
            fpsContainer.style.display = 'block';
            showFps(); // Start FPS monitoring
        } else {
            if (fpsContainer) {
                fpsContainer.style.display = 'none';
            }
        }
    }

    // Function to show FPS
    function showFps() {
        const fpsContainer = document.getElementById('fps-container');
        if (!fpsContainer) return;

        let lastTime = performance.now();
        let frameCount = 0;
        let fps = 0;

        // Update FPS display
        const updateFpsDisplay = () => {
            fpsContainer.innerHTML = `<span id="fps">${fps}</span> FPS`;
        };

        // Calculate FPS based on frame count and time elapsed
        const calculateFps = () => {
            const now = performance.now();
            const deltaTime = now - lastTime;

            if (deltaTime > 0) {
                fps = Math.round((frameCount * 1000) / deltaTime);
                lastTime = now;
                frameCount = 0;
                updateFpsDisplay();
            }
        };

        // Request animation frame callback
        const frameCallback = () => {
            frameCount++;
            requestAnimationFrame(frameCallback);
        };

        // Start counting frames
        requestAnimationFrame(frameCallback);

        // Update FPS every 500 milliseconds
        setInterval(calculateFps, 500);
    }

    // Check settings and initialize
    function init() {
        document.addEventListener('DOMContentLoaded', () => {
            setTimeout(() => {
                const settings = JSON.parse(GM_getValue('settings'));
                console.log('Loaded settings:', settings); // Debug

                // Initialize settings panel
                createSettingsPanel();

                // Apply initial settings
                handleImageUrlInput();
                handleCrosshairUrlInput();
                handleInvertedColors();
                handleShowFps();
            }, 1000); // Apply settings after 1 second
        });
    }

    init();
})();