Developer-like Shop Script + Antibush update!

Read in game instructions! + Antibush update!

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

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

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

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

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

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

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

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

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

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

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

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

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

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

// ==UserScript==
// @name         Developer-like Shop Script + Antibush update!
// @namespace    http://tampermonkey.net/
// @version      4.0
// @description  Read in game instructions! + Antibush update!
// @author       StarryGirl
// @match        *://*.modd.io/play/*
// @match        *://*.us.braains.io/*
// @run-at       document-start
// @grant        none
// ==/UserScript==

(function() {
    'use strict';

    const originalTilesheet = "1763501136378_tilesheet_complete.png";
    const customTilesheet = "https://cache.modd.io/asset/spriteImage/1772476785111_image-1.png.png";

    const originalSrcDescriptor = Object.getOwnPropertyDescriptor(Image.prototype, 'src');

    Object.defineProperty(Image.prototype, 'src', {
        set: function(url) {
            if (url && url.includes(originalTilesheet)) {
                url = customTilesheet;
            }
            originalSrcDescriptor.set.call(this, url);
        },
        get: function() {
            return originalSrcDescriptor.get.call(this);
        },
        configurable: true,
        enumerable: true
    });

    const initShopScript = () => {
        let bKeyDisabled = false;
        let safetyPromptShown = false;

        const fontLink = document.createElement('link');
        fontLink.href = 'https://fonts.googleapis.com/css2?family=Lato:wght@400;700&display=swap';
        fontLink.rel = 'stylesheet';
        document.head.appendChild(fontLink);

        const shopItems = [
            { key: 'r', id: 'g2XpxVVdEp', name: 'Tonic Z-Potion', cost: 500 },
            { key: '2', id: '9jYPyEYzU4', name: 'Dynamite', cost: 750 },
            { key: '3', id: 'Z6cZdmeRd8', name: 'Grenade (x4)', cost: 750 },
            { key: '4', id: '7PnVjGFlfv', name: 'Stacked Dynamite', cost: 1000 },
            { key: '5', id: 'NUQnpXAIsR', name: 'Glock (x100)', cost: 1000 },
            { key: '6', id: 'QZadOkit0E', name: 'Crossbow (x75)', cost: 1000 },
            { key: '7', id: 'oI7IYepEHl', name: 'Spear', cost: 1000 },
            { key: '8', id: 'nl7XMFfFN8', name: 'Slingshot (x100)', cost: 2000 },
            { key: '9', id: '9w6WpNtbMG', name: 'Mystic Crowbar', cost: 3000 },
            { key: '0', id: 'oQOSkx7Oe6', name: 'Broom', cost: 5000, confirm: true },
            { key: '-', id: 'OX7kaD3N28', name: 'Rare Spear', cost: 7500, confirm: true },
            { key: '=', id: 'PyrFdp103w', name: 'Magic Cloak', cost: 10000, confirm: true }
        ];

        const style = document.createElement('style');
        style.innerHTML = `
            @keyframes pulsePop {
                0% { transform: scale(1); }
                35% { transform: scale(1.3); color: #4ade80; }
                70% { transform: scale(0.95); }
                100% { transform: scale(1); }
            }
            @keyframes springIn {
                0% { transform: translateX(-50%) scale(0.5); opacity: 0; }
                100% { transform: translateX(-50%) scale(1); opacity: 1; }
            }
            @keyframes springOut {
                0% { transform: translateX(-50%) scale(1); opacity: 1; }
                100% { transform: translateX(-50%) scale(0.5); opacity: 0; }
            }
            .pulse-active {
                animation: pulsePop 0.29s cubic-bezier(0.34, 1.56, 0.64, 1);
                display: inline-block;
                transform-origin: center;
            }
            .ui-base {
                position: fixed; left: 50%; transform: translateX(-50%);
                background: rgba(255, 255, 255, 0.85); backdrop-filter: blur(4px); -webkit-backdrop-filter: blur(4px);
                border-radius: 6px; font-family: 'Lato', sans-serif; z-index: 1000000;
                box-shadow: 0 10px 25px rgba(0,0,0,0.15);
                animation: springIn 0.35s cubic-bezier(0.34, 1.56, 0.64, 1) forwards;
            }
            .ui-exit { animation: springOut 0.2s ease-in forwards !important; }
            .error-popup { top: 25%; padding: 8px 16px; color: #b91c1c; font-weight: 700; font-size: 15px; display: flex; align-items: center; gap: 6px; pointer-events: none; }
            .confirm-btn {
                cursor: pointer; border: none; padding: 6px 14px; font-family: 'Lato', sans-serif; font-weight: 700; font-size: 13px;
                display: flex; align-items: center; gap: 6px; border-radius: 4px; color: white;
                transition: transform 0.2s cubic-bezier(0.2, 0, 0, 1), filter 0.2s;
            }
            .confirm-btn:hover { transform: scale(1.07); filter: brightness(1.05); }
            .confirm-btn:active { transform: scale(0.95); }
        `;
        document.head.appendChild(style);

        function triggerExit(element, callback) {
            element.classList.add('ui-exit');
            setTimeout(() => {
                element.remove();
                if(callback) callback();
            }, 180);
        }

        function showError() {
            if (document.querySelector('.error-popup')) return;
            const err = document.createElement('div');
            err.className = 'ui-base error-popup';
            err.innerHTML = `<span>⚠️</span> Juke-shop not active yet!`;
            document.body.appendChild(err);
            setTimeout(() => triggerExit(err), 1500);
        }

        function createSafetyLockUI() {
            if (document.getElementById('safety-overlay')) return;
            const overlay = document.createElement('div');
            overlay.id = 'safety-overlay';
            overlay.className = 'ui-base';
            Object.assign(overlay.style, {
                top: '20%', padding: '20px', pointerEvents: 'auto',
                display: 'flex', flexDirection: 'column', alignItems: 'center', gap: '12px', width: '450px'
            });

            overlay.innerHTML = `
                <div style="font-weight: 700; font-size: 18px; color: #1e293b; border-bottom: 2px solid #e2e8f0; padding-bottom: 8px; width: 100%; text-align: center;">
                    ⚠️ Juke-only shop loaded
                </div>
                <div style="font-size: 14px; color: #1e293b; text-align: left; width: 100%; line-height: 1.6;">
                    <div style="margin-bottom: 8px;">✅ Please confirm you have over 5,000 score.</div>
                    <div style="margin-bottom: 8px;">⚠️ The B key will be disabled to prevent opening any other shops!</div>
                    <div style="color: #64748b; font-size: 13px; font-style: italic;">
                        ⚠️ Confirming will disable B key, declining will keep your B key but there will be a risk of accidentally opening regular shop!
                    </div>
                </div>
                <div style="display: flex; gap: 15px; margin-top: 10px;">
                    <button id="safety-confirm" class="confirm-btn" style="background: #10b981; padding: 10px 25px;">Confirm</button>
                    <button id="safety-decline" class="confirm-btn" style="background: #ef4444; padding: 10px 25px;">Decline</button>
                </div>
            `;

            document.body.appendChild(overlay);
            document.getElementById('safety-confirm').onclick = () => { bKeyDisabled = true; triggerExit(overlay); };
            document.getElementById('safety-decline').onclick = () => { bKeyDisabled = false; triggerExit(overlay); };
        }

        function createConfirmBox(itemName, onConfirm) {
            if (document.getElementById('confirm-overlay')) return;
            const overlay = document.createElement('div');
            overlay.id = 'confirm-overlay';
            overlay.className = 'ui-base';
            Object.assign(overlay.style, {
                top: '15%', padding: '12px', pointerEvents: 'auto',
                display: 'flex', flexDirection: 'column', alignItems: 'center', gap: '10px', width: '300px'
            });

            overlay.innerHTML = `
                <div style="font-weight: 700; font-size: 15px; color: #1e293b; padding: 6px;">💰 Expensive item! Confirm purchase?</div>
                <div style="font-size: 14px; color: #475569;">Item: <b style="color:#059669">${itemName}</b></div>
                <div style="display: flex; gap: 10px; margin-top: 5px;">
                    <button id="btn-confirm" class="confirm-btn" style="background: #10b981;">Confirm</button>
                    <button id="btn-cancel" class="confirm-btn" style="background: #ef4444;">Don't confirm</button>
                </div>
            `;

            document.body.appendChild(overlay);
            document.getElementById('btn-confirm').onclick = () => triggerExit(overlay, onConfirm);
            document.getElementById('btn-cancel').onclick = () => triggerExit(overlay);
        }

        function refreshUI() {
            const wrapper = document.getElementById('play-game-button-wrapper');
            const playBtn = document.getElementById('play-game-button');
            let anyActive = false;

            if (wrapper && playBtn) {
                if (!document.getElementById('script-guide-overlay')) wrapper.prepend(guideBox);
                if (playBtn.parentElement !== guideBox) guideBox.appendChild(playBtn);
            }

            shopItems.forEach(item => {
                const shopAction = document.getElementById(item.id);
                const uiElement = document.getElementById(`ui-item-${item.id}`);
                if (uiElement) {
                    if (shopAction) { uiElement.style.opacity = '1'; anyActive = true; }
                    else { uiElement.style.opacity = '0.35'; }
                }
            });

            if (anyActive && !safetyPromptShown) {
                safetyPromptShown = true;
                createSafetyLockUI();
            } else if (!anyActive) {
                safetyPromptShown = false;
            }

            statusHeader.innerText = anyActive ? "Juke-only shop loaded, have fun!" : "Waiting for Juke-only shop!";
            statusHeader.style.color = anyActive ? "#4ade80" : "#ffffff";
            statusHeader.style.opacity = anyActive ? "1" : "0.35";
        }

        const guideBox = document.createElement('div');
        guideBox.id = 'script-guide-overlay';
        Object.assign(guideBox.style, {
            background: 'rgba(255, 255, 255, 0.8)', backdropFilter: 'blur(4px)', WebkitBackdropFilter: 'blur(4px)',
            color: '#1e293b', padding: '15px', fontFamily: '"Lato", sans-serif', borderRadius: '8px',
            marginBottom: '10px', display: 'flex', flexDirection: 'column', alignItems: 'center',
            width: '420px', zIndex: '9999', border: '1px solid rgba(255, 255, 255, 0.4)',
            boxShadow: '0 10px 15px -3px rgba(0, 0, 0, 0.1)'
        });

        guideBox.innerHTML = `
            <div style="width: 100%;">
                <h2 style="margin:4px 0 10px 0; color:#059669; font-size:20px; font-weight:700; text-align:center;">Script usage guide:</h2>
                <ul style="font-size:14px; margin:0; padding-left:25px; color:#1e293b; line-height:1.7; font-weight:400;">
                    <li>Wait for a <b> Juke-only </b> round to spawn</li>
                    <li>Open <b>Juke-Only</b> wizard shop. <b>⚠️ MAKE SURE you have enough score</b>!</li>
                    <li><b> B </b> key will be disabled if confirmed to protect script state.</li>
                    <li>Pills cannot be bought; ask around or bot them in.</li>
                    <li>Don't abuse too hard, and have fun! 😊👌</li> <br>
                    <i> by StarryGirl ;)</i>
                </ul>
            </div>
        `;

        const bottomContainer = document.createElement('div');
        Object.assign(bottomContainer.style, { position: 'fixed', bottom: '20px', left: '50%', transform: 'translateX(-50%)', display: 'flex', flexDirection: 'column', alignItems: 'center', pointerEvents: 'none', zIndex: '999999' });

        const statusHeader = document.createElement('div');
        statusHeader.id = 'shop-status-text';
        Object.assign(statusHeader.style, { color: '#ffffff', fontFamily: '"Lato", sans-serif', fontSize: '18px', fontWeight: '700', marginBottom: '10px', textShadow: '2px 2px 4px rgba(0,0,0,0.8)', transition: 'all 0.3s ease', opacity: '0.35' });
        statusHeader.innerText = "Waiting for Juke-only shop!";

        const bottomUI = document.createElement('div');
        Object.assign(bottomUI.style, { backgroundColor: 'transparent', color: '#ffffff', fontFamily: '"Lato", sans-serif', fontSize: '16.25px', display: 'grid', gridTemplateColumns: 'repeat(4, 1fr)', gap: '12px 25px', textShadow: '1px 1px 3px rgba(0,0,0,0.9)' });

        shopItems.forEach(item => {
            const itemRow = document.createElement('div');
            itemRow.id = `ui-item-${item.id}`;
            itemRow.style.transition = 'opacity 0.3s ease';
            itemRow.style.opacity = '0.35';
            itemRow.innerHTML = `<strong style="color: #4ade80;">[${item.key.toUpperCase()}]</strong> <span>${item.name}</span>`;
            bottomUI.appendChild(itemRow);
        });

        bottomContainer.appendChild(statusHeader);
        bottomContainer.appendChild(bottomUI);
        document.body.appendChild(bottomContainer);

        setInterval(refreshUI, 1000);

        document.addEventListener('keydown', (event) => {
            if (['INPUT', 'TEXTAREA'].includes(document.activeElement.tagName) || document.activeElement.isContentEditable) return;
            const key = event.key.toLowerCase();

            if (key === 'b' && bKeyDisabled) {
                event.preventDefault();
                event.stopImmediatePropagation();
                return;
            }

            const item = shopItems.find(i => i.key === key);
            if (item) {
                const uiElement = document.getElementById(`ui-item-${item.id}`);
                if (uiElement) {
                    uiElement.classList.remove('pulse-active');
                    void uiElement.offsetWidth;
                    uiElement.classList.add('pulse-active');
                }
                const btn = document.getElementById(item.id);
                if (item.confirm) {
                    createConfirmBox(item.name, () => { if (btn) btn.click(); else showError(); });
                } else {
                    if (btn) btn.click(); else showError();
                }
            }
        }, true);
    };

    if (document.readyState === "loading") {
        document.addEventListener("DOMContentLoaded", initShopScript);
    } else {
        initShopScript();
    }

})();