Better Bloxd (broken)

Toggleable left/right autoclicker with rebindable hotkeys and separate CPS for each button. Menu on Right Shift by default.

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

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

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

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

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

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

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

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

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

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

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

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

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

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

// ==UserScript==
// @name         Better Bloxd (broken)
// @namespace    https://greasyfork.org/users/yourname
// @version      1.0.0
// @description  Toggleable left/right autoclicker with rebindable hotkeys and separate CPS for each button. Menu on Right Shift by default.
// @author       You
// @match        *://*/*
// @grant        none
// ==/UserScript==

(function() {
    'use strict';

    // ===========================
    //  SETTINGS & LOCALSTORAGE
    // ===========================
    const STORAGE_PREFIX = 'prism_autoclicker_';
    const LS_KEYS = {
        leftKey: STORAGE_PREFIX + 'left_key',
        rightKey: STORAGE_PREFIX + 'right_key',
        menuKey: STORAGE_PREFIX + 'menu_key',
        leftCPS: STORAGE_PREFIX + 'left_cps',
        rightCPS: STORAGE_PREFIX + 'right_cps',
        leftEnabled: STORAGE_PREFIX + 'left_enabled',
        rightEnabled: STORAGE_PREFIX + 'right_enabled'
    };

    const DEFAULTS = {
        leftKeyCode: 'KeyR',
        rightKeyCode: 'KeyF',
        menuKeyCode: 'ShiftRight',
        leftCPS: 20,
        rightCPS: 20,
        leftEnabled: false,
        rightEnabled: false
    };

    function loadSetting(key, fallback) {
        const v = localStorage.getItem(key);
        if (v === null || v === undefined) return fallback;
        if (v === 'true') return true;
        if (v === 'false') return false;
        const n = Number(v);
        return Number.isNaN(n) ? v : n;
    }

    function saveSetting(key, value) {
        localStorage.setItem(key, String(value));
    }

    // Current settings
    let leftKeyCode = loadSetting(LS_KEYS.leftKey, DEFAULTS.leftKeyCode);
    let rightKeyCode = loadSetting(LS_KEYS.rightKey, DEFAULTS.rightKeyCode);
    let menuKeyCode = loadSetting(LS_KEYS.menuKey, DEFAULTS.menuKeyCode);
    let leftCPS = clamp(loadSetting(LS_KEYS.leftCPS, DEFAULTS.leftCPS), 1, 100);
    let rightCPS = clamp(loadSetting(LS_KEYS.rightCPS, DEFAULTS.rightCPS), 1, 100);
    let leftEnabled = !!loadSetting(LS_KEYS.leftEnabled, DEFAULTS.leftEnabled);
    let rightEnabled = !!loadSetting(LS_KEYS.rightEnabled, DEFAULTS.rightEnabled);

    function clamp(v, min, max) {
        return Math.max(min, Math.min(max, v));
    }

    // ===========================
    //  CURSOR TRACKING
    // ===========================
    let cursorX = window.innerWidth / 2;
    let cursorY = window.innerHeight / 2;

    document.addEventListener('mousemove', function(e) {
        cursorX = e.clientX;
        cursorY = e.clientY;
    }, { passive: true });

    document.addEventListener('touchmove', function(e) {
        const t = e.touches[0];
        if (!t) return;
        cursorX = t.clientX;
        cursorY = t.clientY;
    }, { passive: true });

    document.addEventListener('touchstart', function(e) {
        const t = e.touches[0];
        if (!t) return;
        cursorX = t.clientX;
        cursorY = t.clientY;
    }, { passive: true });

    // ===========================
    //  AUTCLICK LOGIC
    // ===========================
    let leftIntervalId = null;
    let rightIntervalId = null;

    function getTargetElement() {
        const el = document.elementFromPoint(cursorX, cursorY);
        if (!el) return null;
        // Avoid clicking inside our own UI
        if (uiContainer && uiContainer.contains(el)) return null;
        return el;
    }

    function dispatchClick(target, button) {
        if (!target) return;
        const clientX = cursorX;
        const clientY = cursorY;

        const opts = {
            bubbles: true,
            cancelable: true,
            view: window,
            clientX,
            clientY,
            button,
            buttons: 1 << button
        };

        const down = new MouseEvent('mousedown', opts);
        const up = new MouseEvent('mouseup', opts);
        target.dispatchEvent(down);
        target.dispatchEvent(up);

        // For left-click, also send a 'click' event
        if (button === 0) {
            const click = new MouseEvent('click', opts);
            target.dispatchEvent(click);
        }

        // For right-click, send contextmenu as well
        if (button === 2) {
            const ctx = new MouseEvent('contextmenu', opts);
            target.dispatchEvent(ctx);
        }
    }

    function setLeftEnabled(on) {
        leftEnabled = !!on;
        saveSetting(LS_KEYS.leftEnabled, leftEnabled);
        updateUIStatus();
        restartLeftInterval();
    }

    function setRightEnabled(on) {
        rightEnabled = !!on;
        saveSetting(LS_KEYS.rightEnabled, rightEnabled);
        updateUIStatus();
        restartRightInterval();
    }

    function restartLeftInterval() {
        if (leftIntervalId !== null) {
            clearInterval(leftIntervalId);
            leftIntervalId = null;
        }
        if (!leftEnabled) return;

        const interval = 1000 / clamp(leftCPS, 1, 100);
        leftIntervalId = setInterval(() => {
            if (!leftEnabled) return;
            if (isMenuOpen) return;
            const target = getTargetElement();
            if (!target) return;
            dispatchClick(target, 0);
        }, interval);
    }

    function restartRightInterval() {
        if (rightIntervalId !== null) {
            clearInterval(rightIntervalId);
            rightIntervalId = null;
        }
        if (!rightEnabled) return;

        const interval = 1000 / clamp(rightCPS, 1, 100);
        rightIntervalId = setInterval(() => {
            if (!rightEnabled) return;
            if (isMenuOpen) return;
            const target = getTargetElement();
            if (!target) return;
            dispatchClick(target, 2);
        }, interval);
    }

    // Start intervals if enabled from last session
    restartLeftInterval();
    restartRightInterval();

    // ===========================
    //  UI CREATION
    // ===========================
    let uiContainer = null;
    let isMenuOpen = false;
    let waitingForKey = null; // 'left' | 'right' | 'menu' | null

    function createUI() {
        if (uiContainer) return;

        const style = document.createElement('style');
        style.textContent = `
.prism-ac-container {
    position: fixed;
    top: 20px;
    right: 20px;
    z-index: 999999;
    font-family: system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
    color: #f5f5f5;
    background: rgba(10, 10, 20, 0.95);
    border-radius: 10px;
    border: 1px solid rgba(120, 200, 255, 0.6);
    box-shadow: 0 0 20px rgba(0, 150, 255, 0.4);
    padding: 10px 12px;
    min-width: 260px;
    max-width: 320px;
    backdrop-filter: blur(8px);
    display: none;
}

.prism-ac-header {
    display: flex;
    justify-content: space-between;
    align-items: center;
    margin-bottom: 6px;
    font-size: 13px;
}

.prism-ac-title {
    font-weight: 600;
    letter-spacing: 0.04em;
    text-transform: uppercase;
    color: #9ae6ff;
}

.prism-ac-close {
    cursor: pointer;
    padding: 2px 6px;
    border-radius: 4px;
    font-size: 12px;
    color: #ccc;
}

.prism-ac-close:hover {
    background: rgba(255, 255, 255, 0.08);
}

.prism-ac-section-title {
    margin: 6px 0 4px 0;
    font-size: 12px;
    text-transform: uppercase;
    letter-spacing: 0.08em;
    color: #9fb3ff;
}

.prism-ac-row {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 6px;
    margin-bottom: 6px;
    font-size: 12px;
}

.prism-ac-label {
    flex: 1 1 auto;
    font-size: 12px;
    color: #e0e7ff;
}

.prism-ac-status {
    font-size: 11px;
    padding: 2px 6px;
    border-radius: 999px;
    border: 1px solid rgba(255, 255, 255, 0.15);
    min-width: 60px;
    text-align: center;
}

.prism-ac-status.on {
    color: #c6ffdd;
    border-color: rgba(0, 255, 170, 0.7);
    box-shadow: 0 0 8px rgba(0, 255, 170, 0.4);
}

.prism-ac-status.off {
    color: #ffcccc;
    border-color: rgba(255, 120, 120, 0.6);
}

.prism-ac-slider-row {
    display: flex;
    align-items: center;
    gap: 6px;
    margin-bottom: 4px;
}

.prism-ac-slider-row input[type="range"] {
    flex: 1 1 auto;
}

.prism-ac-slider-value {
    width: 32px;
    text-align: right;
    font-size: 11px;
    color: #cbd5f5;
}

.prism-ac-key-btn {
    font-size: 11px;
    padding: 3px 6px;
    border-radius: 6px;
    border: 1px solid rgba(130, 200, 255, 0.7);
    background: rgba(15, 23, 42, 0.9);
    color: #dbeafe;
    cursor: pointer;
    min-width: 86px;
    text-align: center;
}

.prism-ac-key-btn.waiting {
    border-style: dashed;
    border-color: rgba(255, 200, 90, 0.9);
    color: #fde68a;
    box-shadow: 0 0 10px rgba(251, 191, 36, 0.4);
}

.prism-ac-key-btn:hover {
    background: rgba(30, 64, 175, 0.8);
}

.prism-ac-hint {
    font-size: 11px;
    color: #a5b4fc;
    opacity: 0.9;
    margin-top: 4px;
}

.prism-ac-divider {
    border-top: 1px solid rgba(148, 163, 184, 0.4);
    margin: 4px 0 6px 0;
}
        `;
        document.documentElement.appendChild(style);

        uiContainer = document.createElement('div');
        uiContainer.className = 'prism-ac-container';
        uiContainer.innerHTML = `
<div class="prism-ac-header">
  <div class="prism-ac-title">Prism AutoClicker</div>
  <div class="prism-ac-close" title="Hide menu">✕</div>
</div>

<div class="prism-ac-section-title">Status</div>
<div class="prism-ac-row">
  <div class="prism-ac-label">Left click</div>
  <div class="prism-ac-status" id="prism-ac-left-status"></div>
</div>
<div class="prism-ac-row">
  <div class="prism-ac-label">Right click</div>
  <div class="prism-ac-status" id="prism-ac-right-status"></div>
</div>

<div class="prism-ac-divider"></div>

<div class="prism-ac-section-title">CPS (Clicks per second)</div>
<div class="prism-ac-slider-row">
  <div class="prism-ac-label">Left</div>
  <input type="range" min="1" max="100" step="1" id="prism-ac-left-cps-slider">
  <div class="prism-ac-slider-value" id="prism-ac-left-cps-value"></div>
</div>
<div class="prism-ac-slider-row">
  <div class="prism-ac-label">Right</div>
  <input type="range" min="1" max="100" step="1" id="prism-ac-right-cps-slider">
  <div class="prism-ac-slider-value" id="prism-ac-right-cps-value"></div>
</div>

<div class="prism-ac-divider"></div>

<div class="prism-ac-section-title">Hotkeys</div>
<div class="prism-ac-row">
  <div class="prism-ac-label">Toggle left</div>
  <button class="prism-ac-key-btn" id="prism-ac-left-key-btn"></button>
</div>
<div class="prism-ac-row">
  <div class="prism-ac-label">Toggle right</div>
  <button class="prism-ac-key-btn" id="prism-ac-right-key-btn"></button>
</div>
<div class="prism-ac-row">
  <div class="prism-ac-label">Toggle menu</div>
  <button class="prism-ac-key-btn" id="prism-ac-menu-key-btn"></button>
</div>

<div class="prism-ac-hint">
  • Default: Left = R, Right = F, Menu = Right Shift<br>
  • Press the buttons above, then press any key to rebind.
</div>
        `;
        document.body.appendChild(uiContainer);

        const closeBtn = uiContainer.querySelector('.prism-ac-close');
        const leftStatusEl = document.getElementById('prism-ac-left-status');
        const rightStatusEl = document.getElementById('prism-ac-right-status');
        const leftSlider = document.getElementById('prism-ac-left-cps-slider');
        const rightSlider = document.getElementById('prism-ac-right-cps-slider');
        const leftValue = document.getElementById('prism-ac-left-cps-value');
        const rightValue = document.getElementById('prism-ac-right-cps-value');
        const leftKeyBtn = document.getElementById('prism-ac-left-key-btn');
        const rightKeyBtn = document.getElementById('prism-ac-right-key-btn');
        const menuKeyBtn = document.getElementById('prism-ac-menu-key-btn');

        function codeToLabel(code) {
            if (!code) return '?';
            if (code.startsWith('Key') && code.length === 4) return code.slice(3);
            if (code.startsWith('Digit') && code.length === 6) return code.slice(5);
            if (code === 'ShiftRight') return 'Right Shift';
            if (code === 'ShiftLeft') return 'Left Shift';
            if (code === 'ControlLeft') return 'Left Ctrl';
            if (code === 'ControlRight') return 'Right Ctrl';
            if (code === 'AltLeft') return 'Left Alt';
            if (code === 'AltRight') return 'Right Alt';
            if (code === 'Space') return 'Space';
            if (code === 'Enter') return 'Enter';
            if (code === 'Escape') return 'Esc';
            if (code === 'Tab') return 'Tab';
            return code;
        }

        function updateKeyButtons() {
            leftKeyBtn.textContent = (waitingForKey === 'left')
                ? 'Press a key...'
                : codeToLabel(leftKeyCode);
            rightKeyBtn.textContent = (waitingForKey === 'right')
                ? 'Press a key...'
                : codeToLabel(rightKeyCode);
            menuKeyBtn.textContent = (waitingForKey === 'menu')
                ? 'Press a key...'
                : codeToLabel(menuKeyCode);

            leftKeyBtn.classList.toggle('waiting', waitingForKey === 'left');
            rightKeyBtn.classList.toggle('waiting', waitingForKey === 'right');
            menuKeyBtn.classList.toggle('waiting', waitingForKey === 'menu');
        }

        function updateStatus() {
            if (!leftStatusEl || !rightStatusEl) return;
            leftStatusEl.textContent = leftEnabled ? 'ON' : 'OFF';
            rightStatusEl.textContent = rightEnabled ? 'ON' : 'OFF';
            leftStatusEl.classList.toggle('on', leftEnabled);
            leftStatusEl.classList.toggle('off', !leftEnabled);
            rightStatusEl.classList.toggle('on', rightEnabled);
            rightStatusEl.classList.toggle('off', !rightEnabled);
        }

        // Expose to outer scope
        updateUIStatus = updateStatus;

        // Init CPS controls
        leftSlider.value = leftCPS;
        rightSlider.value = rightCPS;
        leftValue.textContent = leftCPS;
        rightValue.textContent = rightCPS;

        leftSlider.addEventListener('input', () => {
            leftCPS = clamp(Number(leftSlider.value), 1, 100);
            saveSetting(LS_KEYS.leftCPS, leftCPS);
            leftValue.textContent = leftCPS;
            restartLeftInterval();
        });

        rightSlider.addEventListener('input', () => {
            rightCPS = clamp(Number(rightSlider.value), 1, 100);
            saveSetting(LS_KEYS.rightCPS, rightCPS);
            rightValue.textContent = rightCPS;
            restartRightInterval();
        });

        // Key rebind buttons
        leftKeyBtn.addEventListener('click', () => {
            waitingForKey = (waitingForKey === 'left') ? null : 'left';
            updateKeyButtons();
        });

        rightKeyBtn.addEventListener('click', () => {
            waitingForKey = (waitingForKey === 'right') ? null : 'right';
            updateKeyButtons();
        });

        menuKeyBtn.addEventListener('click', () => {
            waitingForKey = (waitingForKey === 'menu') ? null : 'menu';
            updateKeyButtons();
        });

        closeBtn.addEventListener('click', () => {
            setMenuOpen(false);
        });

        updateKeyButtons();
        updateStatus();
    }

    // Placeholder, will be replaced in createUI
    let updateUIStatus = function() {};

    function setMenuOpen(open) {
        isMenuOpen = !!open;
        if (!uiContainer) return;
        uiContainer.style.display = isMenuOpen ? 'block' : 'none';
        if (!isMenuOpen) {
            waitingForKey = null;
            const keyButtons = uiContainer.querySelectorAll('.prism-ac-key-btn');
            keyButtons.forEach(btn => btn.classList.remove('waiting'));
        }
    }

    function toggleMenu() {
        if (!uiContainer) createUI();
        setMenuOpen(!isMenuOpen);
    }

    // ===========================
    //  KEYBOARD HANDLING
    // ===========================
    document.addEventListener('keydown', function(e) {
        // Ignore in input/textarea/contentEditable unless we're rebinding
        const target = e.target;
        const tag = (target && target.tagName) ? target.tagName.toLowerCase() : '';
        const isTypingField = tag === 'input' || tag === 'textarea' || (target && target.isContentEditable);
        if (!waitingForKey && isTypingField) return;

        // Rebinding logic
        if (waitingForKey) {
            e.preventDefault();
            e.stopPropagation();

            const code = e.code || '';
            if (!code) return;

            if (waitingForKey === 'left') {
                leftKeyCode = code;
                saveSetting(LS_KEYS.leftKey, leftKeyCode);
            } else if (waitingForKey === 'right') {
                rightKeyCode = code;
                saveSetting(LS_KEYS.rightKey, rightKeyCode);
            } else if (waitingForKey === 'menu') {
                menuKeyCode = code;
                saveSetting(LS_KEYS.menuKey, menuKeyCode);
            }

            waitingForKey = null;
            if (uiContainer) {
                const leftKeyBtn = document.getElementById('prism-ac-left-key-btn');
                const rightKeyBtn = document.getElementById('prism-ac-right-key-btn');
                const menuKeyBtn = document.getElementById('prism-ac-menu-key-btn');
                const all = [leftKeyBtn, rightKeyBtn, menuKeyBtn];
                all.forEach(btn => btn && btn.classList.remove('waiting'));
                // Update labels
                const evt = new Event('click'); // dummy, we won't actually use it
                // Safer: call update via function inside createUI if needed
            }
            // Rebuild labels
            createUI(); // ensures key text refresh
            return;
        }

        if (e.repeat) return;

        // Normal key handling
        const code = e.code || '';
        if (!code) return;

        if (code === menuKeyCode) {
            e.preventDefault();
            toggleMenu();
            return;
        }

        if (code === leftKeyCode) {
            e.preventDefault();
            setLeftEnabled(!leftEnabled);
            return;
        }

        if (code === rightKeyCode) {
            e.preventDefault();
            setRightEnabled(!rightEnabled);
            return;
        }
    }, true);

    // Create UI lazily on first toggle/menu open
    // But if you want it to show immediately:
    createUI();
})();