RBX Customize

Customize Roblox with animated backgrounds and fonts.

За да инсталирате този скрипт, трябва да имате инсталирано разширение като Tampermonkey, Greasemonkey или Violentmonkey.

За да инсталирате този скрипт, трябва да инсталирате разширение, като например Tampermonkey .

За да инсталирате този скрипт, трябва да имате инсталирано разширение като Tampermonkey или Violentmonkey.

За да инсталирате този скрипт, трябва да имате инсталирано разширение като Tampermonkey или Userscripts.

За да инсталирате скрипта, трябва да инсталирате разширение като Tampermonkey.

За да инсталирате този скрипт, трябва да имате инсталиран скриптов мениджър.

(Вече имам скриптов мениджър, искам да го инсталирам!)

За да инсталирате този стил, трябва да инсталирате разширение като Stylus.

За да инсталирате този стил, трябва да инсталирате разширение като Stylus.

За да инсталирате този стил, трябва да инсталирате разширение като Stylus.

За да инсталирате този стил, трябва да имате инсталиран мениджър на потребителски стилове.

За да инсталирате този стил, трябва да имате инсталиран мениджър на потребителски стилове.

За да инсталирате този стил, трябва да имате инсталиран мениджър на потребителски стилове.

(Вече имам инсталиран мениджър на стиловете, искам да го инсталирам!)

// ==UserScript==
// @name         RBX Customize
// @namespace    http://tampermonkey.net/
// @version      1.0
// @description  Customize Roblox with animated backgrounds and fonts.
// @author       Find
// @match        https://*.roblox.com/*
// @grant        GM_setValue
// @grant        GM_getValue
// @run-at       document-end
// @license      MIT
// ==/UserScript==

(function() {
    'use strict';

    const config = {
        preset: 'matrix',
        transparency: 0.2,
        fontName: '',
        btnPos: { top: 'auto', bottom: '20px', left: 'auto', right: '20px' },
        firstTime: true
    };

    let settings = { ...config, ...GM_getValue('rbx_custom_settings', {}) };
    if (!settings.btnPos) settings.btnPos = config.btnPos;

    let loopId = null;

    const ui = {
        btn: null,
        panel: null,
        overlay: null,
        loader: null
    };

    function start() {
        if (document.getElementById('rbx-custom-btn')) return;

        buildBtn();
        buildMenu();
        render();

        new MutationObserver(() => {
            if (document.body && !document.getElementById('rbx-custom-btn')) {
                start();
            }
        }).observe(document.body, { childList: true, subtree: true });
    }

    function buildBtn() {
        ui.btn = document.createElement('div');
        ui.btn.id = 'rbx-custom-btn';
        ui.btn.innerText = 'RBX Customize';

        Object.assign(ui.btn.style, {
            position: 'fixed',
            zIndex: '10001',
            background: '#111',
            color: '#00ffaa',
            border: '2px solid #00ffaa',
            borderRadius: '8px',
            cursor: 'pointer',
            fontFamily: 'sans-serif',
            fontWeight: 'bold',
            boxShadow: '0 0 10px rgba(0, 255, 170, 0.3)',
            transition: 'transform 0.1s, top 0.3s, left 0.3s, right 0.3s, bottom 0.3s',
            userSelect: 'none',
            textAlign: 'center',
            fontSize: '14px',
            width: '120px',
            height: '40px',
            display: 'flex',
            alignItems: 'center',
            justifyContent: 'center',
            boxSizing: 'border-box',
            ...settings.btnPos
        });

        document.body.appendChild(ui.btn);
        initDrag(ui.btn);

        if (settings.firstTime) {
            tooltip();
            settings.firstTime = false;
            GM_setValue('rbx_custom_settings', settings);
        }
    }

    function buildMenu() {
        ui.panel = document.createElement('div');
        ui.panel.id = 'rbx-custom-panel';

        Object.assign(ui.panel.style, {
            position: 'fixed',
            top: '50%', left: '50%',
            transform: 'translate(-50%, -50%)',
            zIndex: '10002',
            background: 'rgba(10, 10, 10, 0.95)',
            color: 'white',
            padding: '20px',
            borderRadius: '12px',
            border: '1px solid #333',
            display: 'none',
            width: '280px',
            fontFamily: 'sans-serif',
            backdropFilter: 'blur(15px)',
            boxShadow: '0 10px 30px rgba(0,0,0,0.8)'
        });

        ui.panel.innerHTML = `
            <h3 style="margin:0 0 10px 0; border-bottom:1px solid #333; padding-bottom:10px; color:#00ffaa; font-size:16px;">RBX Customize</h3>
            <div style="font-size:10px; color:#888; margin-bottom:15px; margin-top:-5px;">Click and hold the RBX customize button to drag it.</div>
            <div style="margin-bottom: 15px;">
                <label style="font-weight:bold; color:#aaa; font-size:11px;">ANIMATION</label>
                <select id="presetSelect" style="width:100%; margin-top:5px; padding:10px; background:#222; color:#fff; border:1px solid #444; border-radius: 4px; cursor:pointer;">
                    <option value="matrix" ${settings.preset === 'matrix' ? 'selected' : ''}>💻 Matrix Rain</option>
                    <option value="fireworks" ${settings.preset === 'fireworks' ? 'selected' : ''}>🎆 Fireworks</option>
                    <option value="storm" ${settings.preset === 'storm' ? 'selected' : ''}>⚡ Thunderstorm</option>
                    <option value="neural" ${settings.preset === 'neural' ? 'selected' : ''}>🧠 Neural Link</option>
                    <option value="snow" ${settings.preset === 'snow' ? 'selected' : ''}>❄️ Night Snow</option>
                </select>
            </div>
            <div style="margin-bottom: 15px;">
                <label style="font-weight:bold; color:#aaa; font-size:11px;">FONT (Google Fonts)</label>
                <input type="text" id="fontInput" value="${settings.fontName}" placeholder="e.g. Orbitron" style="width: 100%; padding: 8px; margin-top:5px; background: #222; color: #fff; border: 1px solid #444; border-radius: 4px;">
            </div>
            <div style="margin-bottom: 20px;">
                 <label style="font-weight:bold; font-size:11px; color:#aaa;">TRANSPARENCY</label>
                 <input type="range" id="transparencyInput" min="0" max="1" step="0.05" value="${settings.transparency}" style="width: 100%; margin-top:5px;">
            </div>
            <div style="display:flex; gap: 10px;">
                <button id="saveBtn" style="flex:1; padding: 10px; background: #00ffaa; color: #000; border: none; border-radius: 6px; font-weight: bold; cursor: pointer;">APPLY</button>
                <button id="closeBtn" style="padding: 10px 15px; background: #333; color: #fff; border: none; border-radius: 6px; cursor: pointer;">X</button>
            </div>
        `;

        document.body.appendChild(ui.panel);

        document.getElementById('closeBtn').onclick = () => ui.panel.style.display = 'none';

        document.getElementById('saveBtn').onclick = () => {
            const btn = document.getElementById('saveBtn');
            settings.preset = document.getElementById('presetSelect').value;
            settings.transparency = document.getElementById('transparencyInput').value;
            settings.fontName = document.getElementById('fontInput').value.trim();
            settings.btnPos = {
                top: ui.btn.style.top, bottom: ui.btn.style.bottom,
                left: ui.btn.style.left, right: ui.btn.style.right
            };

            GM_setValue('rbx_custom_settings', settings);
            render();

            btn.innerText = "SAVED!";
            setTimeout(() => btn.innerText = "APPLY", 2000);
        };
    }

    function buildLoader() {
        if (ui.loader) ui.loader.remove();

        ui.loader = document.createElement('div');
        ui.loader.id = 'rbx-drag-loader';
        Object.assign(ui.loader.style, {
            position: 'absolute', top: '50%', left: '50%',
            transform: 'translate(-50%, -50%)',
            width: '24px', height: '24px',
            display: 'none', pointerEvents: 'none'
        });

        ui.loader.innerHTML = `
            <svg width="24" height="24" viewBox="0 0 24 24" style="transform: rotate(-90deg);">
                <circle cx="12" cy="12" r="10" stroke="rgba(255,255,255,0.2)" stroke-width="3" fill="none"></circle>
                <circle id="rbx-progress-ring" cx="12" cy="12" r="10" stroke="#ffffff" stroke-width="3" fill="none"
                 stroke-dasharray="63" stroke-dashoffset="63" style="transition: stroke-dashoffset 0.05s linear;"></circle>
            </svg>
        `;

        ui.btn.appendChild(ui.loader);
    }

    function initDrag(el) {
        let dragging = false;
        let holding = false;
        let t1 = null;
        let t2 = null;
        let start = { x: 0, y: 0 };
        let offset = { l: 0, t: 0 };
        let time = 0;

        buildLoader();

        el.onmousedown = (e) => {
            if (e.button !== 0) return;

            const ring = document.getElementById('rbx-progress-ring');
            if (ring) {
                ring.style.transition = 'none';
                ring.style.strokeDashoffset = '63';
                void ring.offsetWidth;
                ring.style.transition = 'stroke-dashoffset 0.05s linear';
            }

            holding = true;
            start = { x: e.clientX, y: e.clientY };
            const rect = el.getBoundingClientRect();
            offset = { l: rect.left, t: rect.top };
            time = Date.now();

            t1 = setTimeout(() => {
                if (!holding) return;

                ui.loader.style.display = 'block';
                el.style.color = 'transparent';

                t2 = setInterval(() => {
                    if (!holding) {
                         clearInterval(t2);
                         return;
                    }
                    const diff = Date.now() - time;
                    const pct = Math.min(100, Math.max(0, ((diff - 200) / 800) * 100));

                    if (ring) {
                        const val = 63 - (63 * pct / 100);
                        ring.style.strokeDashoffset = val;
                    }

                    if (pct >= 100) {
                        clearInterval(t2);
                        dragging = true;
                        ui.panel.style.display = 'none';
                        el.style.transition = 'none';
                        el.style.cursor = 'move';
                        ui.loader.style.display = 'none';
                        el.style.color = '#00ffaa';
                        overlay(true);
                        const t = document.getElementById('rbx-drag-tip');
                        if(t) t.remove();
                    }
                }, 16);

            }, 200);
        };

        document.onmousemove = (e) => {
            if (!dragging) return;
            el.style.left = (offset.l + (e.clientX - start.x)) + 'px';
            el.style.top = (offset.t + (e.clientY - start.y)) + 'px';
            el.style.right = 'auto';
            el.style.bottom = 'auto';
        };

        document.onmouseup = () => {
            if (t1) clearTimeout(t1);
            if (t2) clearInterval(t2);

            if (ui.loader) ui.loader.style.display = 'none';
            ui.btn.style.color = '#00ffaa';

            if (dragging) {
                dragging = false;
                holding = false;
                overlay(false);
                el.style.transition = 'top 0.3s, left 0.3s, right 0.3s, bottom 0.3s';
                el.style.cursor = 'pointer';
                snap(el);
            }
            else if (holding) {
                holding = false;
                ui.panel.style.display = ui.panel.style.display === 'none' ? 'block' : 'none';
            }
        };
    }

    function overlay(active) {
        if (active) {
            ui.overlay = document.createElement('div');
            Object.assign(ui.overlay.style, {
                position: 'fixed', top: 0, left: 0, width: '100%', height: '100%',
                background: 'rgba(0, 0, 0, 0.7)',
                zIndex: '10000', pointerEvents: 'none'
            });

            const zones = [
                { top: '20px', left: '20px' },
                { top: '20px', right: '20px' },
                { bottom: '20px', left: '20px' },
                { bottom: '20px', right: '20px' }
            ];

            zones.forEach(pos => {
                const z = document.createElement('div');
                Object.assign(z.style, {
                    position: 'absolute', width: '120px', height: '40px',
                    border: '2px dashed rgba(255,255,255,0.5)',
                    borderRadius: '8px', boxSizing: 'border-box',
                    ...pos
                });
                ui.overlay.appendChild(z);
            });

            document.body.appendChild(ui.overlay);
        } else {
            if (ui.overlay) ui.overlay.remove();
        }
    }

    function snap(el) {
        const r = el.getBoundingClientRect();
        const w = window.innerWidth;
        const h = window.innerHeight;

        const d = [
            Math.hypot(r.left, r.top),
            Math.hypot(w - r.right, r.top),
            Math.hypot(r.left, h - r.bottom),
            Math.hypot(w - r.right, h - r.bottom)
        ];

        const m = Math.min(...d);
        let p = {};

        if (m === d[0]) p = { top: '20px', left: '20px', bottom: 'auto', right: 'auto' };
        else if (m === d[1]) p = { top: '20px', right: '20px', bottom: 'auto', left: 'auto' };
        else if (m === d[2]) p = { bottom: '20px', left: '20px', top: 'auto', right: 'auto' };
        else p = { bottom: '20px', right: '20px', top: 'auto', left: 'auto' };

        Object.assign(el.style, p);
        settings.btnPos = p;
        GM_setValue('rbx_custom_settings', settings);
    }

    function tooltip() {
        const t = document.createElement('div');
        t.id = 'rbx-drag-tip';
        t.innerText = 'Drag me!';
        Object.assign(t.style, {
            position: 'absolute', top: '-45px', left: '50%', transform: 'translateX(-50%)',
            background: 'white', color: 'black', padding: '8px 12px', borderRadius: '6px',
            fontSize: '14px', fontWeight: 'bold', pointerEvents: 'none', whiteSpace: 'nowrap',
            boxShadow: '0 2px 10px rgba(0,0,0,0.5)'
        });

        const a = document.createElement('div');
        Object.assign(a.style, {
            position: 'absolute', bottom: '-5px', left: '50%', marginLeft: '-5px',
            borderLeft: '5px solid transparent', borderRight: '5px solid transparent',
            borderTop: '5px solid white'
        });

        t.appendChild(a);
        ui.btn.appendChild(t);
        setTimeout(() => { if(t) t.remove(); }, 5000);
    }

    function render() {
        if (loopId) clearInterval(loopId);
        const old = document.getElementById('rbx-canvas');
        if (old) old.remove();

        switch (settings.preset) {
            case 'matrix': matrix(); break;
            case 'fireworks': fireworks(); break;
            case 'storm': storm(); break;
            case 'neural': neural(); break;
            case 'snow': snow(); break;
        }

        const fid = 'rbx-font-link';
        let fl = document.getElementById(fid);
        if (fl) fl.remove();

        let fcss = '';
        if (settings.fontName) {
            fl = document.createElement('link');
            fl.id = fid;
            fl.rel = 'stylesheet';
            fl.href = `https://fonts.googleapis.com/css2?family=${settings.fontName.replace(/ /g, '+')}&display=swap`;
            document.head.appendChild(fl);
            fcss = `* { font-family: '${settings.fontName}', sans-serif !important; }`;
        }

        const sid = 'rbx-injected-css';
        let st = document.getElementById(sid);
        if (!st) {
            st = document.createElement('style');
            st.id = sid;
            document.head.appendChild(st);
        }

        st.innerHTML = `
            ${fcss}
            body, .dark-theme, .light-theme, .container-main, #Container { background: transparent !important; background-image: none !important; }
            .content, .container-footer, .footer, .rbx-header, #wrap.wrap,
            .game-home-page-container, .section-content, .rbx-tab-content,
            .profile-header, .rbx-left-col, .rbx-right-col, .rbx-game-status,
            .chat-container, .chat-main, .dialog-container, .navbar-search {
                background-color: rgba(20, 20, 20, ${settings.transparency}) !important; border: none !important; box-shadow: none !important;
            }
            .text-footer-nav, .text-copyright, .text-link, h1, h2, h3, p, div { color: #fff !important; text-shadow: 1px 1px 2px rgba(0,0,0,0.8); }
            .rbx-header { backdrop-filter: blur(5px); background-color: rgba(0, 0, 0, 0.6) !important; }
            .ad-container, .gutter-ads, #Skyscraper-Adp-Right, #Skyscraper-Adp-Left { display: none !important; }
        `;
    }

    function canvas(c) {
        const el = document.createElement('canvas');
        el.id = 'rbx-canvas';
        Object.assign(el.style, { position: 'fixed', top: 0, left: 0, width: '100%', height: '100%', zIndex: '-1', background: c });
        el.width = window.innerWidth;
        el.height = window.innerHeight;
        document.body.appendChild(el);
        return el;
    }

    function matrix() {
        const c = canvas('black');
        const ctx = c.getContext('2d');
        const set = 'アァカサタナハマヤャラワガザダバパイィキシチニヒミリBg0123456789'.split('');
        const arr = Array(Math.floor(c.width / 16)).fill(1);

        loopId = setInterval(() => {
            ctx.fillStyle = 'rgba(0, 0, 0, 0.05)';
            ctx.fillRect(0, 0, c.width, c.height);
            ctx.fillStyle = '#0F0';
            ctx.font = '16px monospace';

            arr.forEach((y, i) => {
                const t = set[Math.floor(Math.random() * set.length)];
                ctx.fillText(t, i * 16, y * 16);
                if (y * 16 > c.height && Math.random() > 0.975) arr[i] = 0;
                arr[i]++;
            });
        }, 33);
    }

    function fireworks() {
        const c = canvas('#050505');
        const ctx = c.getContext('2d');
        let f = [], p = [];

        loopId = setInterval(() => {
            ctx.globalCompositeOperation = 'source-over';
            ctx.fillStyle = 'rgba(0, 0, 0, 0.3)';
            ctx.fillRect(0, 0, c.width, c.height);
            ctx.globalCompositeOperation = 'lighter';

            if (Math.random() < 0.04) {
                f.push({ x: Math.random() * c.width, y: c.height, tx: Math.random() * c.width, ty: Math.random() * c.height / 2, h: Math.random() * 360 });
            }

            for (let i = f.length - 1; i >= 0; i--) {
                let r = f[i];
                r.x += (r.tx - r.x) / 15;
                r.y += (r.ty - r.y) / 15;
                ctx.beginPath(); ctx.arc(r.x, r.y, 3, 0, Math.PI * 2);
                ctx.fillStyle = `hsl(${r.h}, 100%, 50%)`; ctx.fill();
                if (Math.hypot(r.tx - r.x, r.ty - r.y) < 15) {
                    for(let j=0; j<40; j++) p.push({ x: r.x, y: r.y, vx: Math.cos(j*10)*Math.random()*6, vy: Math.sin(j*10)*Math.random()*6, h: r.h, a: 1 });
                    f.splice(i, 1);
                }
            }

            for (let i = p.length - 1; i >= 0; i--) {
                let pt = p[i];
                pt.x += pt.vx; pt.y += pt.vy; pt.vy += 0.08; pt.a -= 0.08;
                if (pt.a <= 0) p.splice(i, 1);
                else { ctx.beginPath(); ctx.arc(pt.x, pt.y, 2, 0, Math.PI * 2); ctx.fillStyle = `hsla(${pt.h}, 100%, 50%, ${pt.a})`; ctx.fill(); }
            }
        }, 25);
    }

    function storm() {
        const c = canvas('#050505');
        const ctx = c.getContext('2d');
        const r = Array(300).fill().map(() => ({ x: Math.random()*c.width, y: Math.random()*c.height, l: Math.random()*20+10, v: Math.random()*10+10 }));
        let fl = { on: false, op: 0, s: [] };

        loopId = setInterval(() => {
            if (fl.op > 0) {
                ctx.fillStyle = `rgba(20, 20, 30, ${fl.op * 0.3})`;
                ctx.fillRect(0,0,c.width, c.height);
                fl.op -= 0.05;
            } else {
                ctx.fillStyle = 'rgba(5, 5, 5, 0.4)';
                ctx.fillRect(0,0,c.width, c.height);
                if (Math.random() > 0.985) {
                    fl.on = true; fl.op = 1;
                    let sx = Math.random() * c.width, sy = 0;
                    fl.s = [{x:sx, y:sy}];
                    while(sy < c.height) { sx += (Math.random()-0.5)*50; sy += Math.random()*20+10; fl.s.push({x:sx, y:sy}); }
                } else fl.on = false;
            }

            if (fl.on || fl.op > 0.5) {
                ctx.beginPath(); ctx.strokeStyle = `rgba(255, 255, 255, ${fl.op})`; ctx.lineWidth = 2;
                ctx.shadowBlur = 15; ctx.shadowColor = "white";
                fl.s.forEach((pt, i) => i === 0 ? ctx.moveTo(pt.x, pt.y) : ctx.lineTo(pt.x, pt.y));
                ctx.stroke(); ctx.shadowBlur = 0;
            }

            ctx.strokeStyle = 'rgba(174, 194, 224, 0.5)'; ctx.lineWidth = 1; ctx.beginPath();
            r.forEach(d => {
                ctx.moveTo(d.x, d.y); ctx.lineTo(d.x, d.y + d.l);
                d.y += d.v;
                if (d.y > c.height) { d.y = -20; d.x = Math.random() * c.width; }
            });
            ctx.stroke();
        }, 30);
    }

    function neural() {
        const c = canvas('#111');
        const ctx = c.getContext('2d');
        const pt = Array(80).fill().map(() => ({ x: Math.random()*c.width, y: Math.random()*c.height, vx: (Math.random()-0.5)*2, vy: (Math.random()-0.5)*2 }));

        loopId = setInterval(() => {
            ctx.clearRect(0, 0, c.width, c.height);
            ctx.fillStyle = '#00ffaa'; ctx.strokeStyle = '#00ffaa';

            pt.forEach((p, i) => {
                p.x += p.vx; p.y += p.vy;
                if (p.x < 0 || p.x > c.width) p.vx *= -1;
                if (p.y < 0 || p.y > c.height) p.vy *= -1;
                ctx.beginPath(); ctx.arc(p.x, p.y, 3, 0, Math.PI*2); ctx.fill();

                for (let j = i + 1; j < pt.length; j++) {
                    let p2 = pt[j];
                    let d = Math.hypot(p.x - p2.x, p.y - p2.y);
                    if (d < 150) {
                        ctx.beginPath(); ctx.lineWidth = 1 - (d/150);
                        ctx.moveTo(p.x, p.y); ctx.lineTo(p2.x, p2.y); ctx.stroke();
                    }
                }
            });
        }, 30);
    }

    function snow() {
        const c = canvas('radial-gradient(ellipse at bottom, #1b2735 0%, #090a0f 100%)');
        const ctx = c.getContext('2d');
        const fl = Array(100).fill().map(() => ({ x: Math.random()*c.width, y: Math.random()*c.height, r: Math.random()*3+1, d: Math.random()*100 }));

        loopId = setInterval(() => {
            ctx.clearRect(0, 0, c.width, c.height);
            ctx.fillStyle = "white"; ctx.beginPath();
            fl.forEach(f => {
                ctx.moveTo(f.x, f.y); ctx.arc(f.x, f.y, f.r, 0, Math.PI*2, true);
                f.y += Math.cos(f.d) + 1 + f.r/2;
                f.x += Math.sin(f.d) * 2;
                if (f.x > c.width + 5 || f.x < -5 || f.y > c.height) { f.x = Math.random()*c.width; f.y = -10; }
            });
            ctx.fill();
        }, 33);
    }

    if (document.readyState === 'loading') {
        document.addEventListener('DOMContentLoaded', start);
    } else {
        start();
    }

})();