QuickCoords

Sends coordinates to chat when the binding key is pressed. HUD (F10 toggle). msg mode.

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

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

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

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

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

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

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

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

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

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

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

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

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

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

// ==UserScript==
// @name        QuickCoords
// @namespace   Violentmonkey Scripts
// @match       *://cavegame.io/*
// @match       *://mineroyale.io/*
// @grant       GM_getValue
// @grant       GM_setValue
// @grant       GM_deleteValue
// @version     2.0
// @author      Drik
// @description Sends coordinates to chat when the binding key is pressed. HUD (F10 toggle). msg mode.
// @run-at      document-start
// @license     MIT
// ==/UserScript==

(async function() {
    'use strict';

    const SK = 'cfg';
    const SEL = '.player-position.no-select';
    const CHAT = '#chat-input';
    const DEF = {
        k: 'KeyR',
        kl: 'R',
        msg: false,
        n: []
    };

    async function save(x) {
        await GM_setValue(SK, JSON.stringify(x));
        cfg = x;
    }
    async function load() {
        const v = await GM_getValue(SK);
        return v ? JSON.parse(v) : DEF;
    }

    let cfg = await load();
    cfg.n = Array.isArray(cfg.n) ? cfg.n.slice(0, 2) : [];

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

    function openChat() {
        document.dispatchEvent(new KeyboardEvent('keydown', {
            key: 't',
            code: 'KeyT',
            keyCode: 84,
            which: 84,
            bubbles: true,
            cancelable: true
        }));
    }

    async function sendRaw(txt) {
        const el = document.querySelector(CHAT);
        if (!el) return;
        el.focus();
        el.value = txt;
        el.dispatchEvent(new InputEvent('input', {
            bubbles: true
        }));
        await sleep(69);
        el.dispatchEvent(new KeyboardEvent('keydown', {
            key: 'Enter',
            code: 'Enter',
            keyCode: 13,
            which: 13,
            bubbles: true,
            cancelable: true
        }));
    }

    async function sendAll(txt) {
        if (!txt) return;
        const list = Array.isArray(cfg.n) ? cfg.n.slice(0, 2) : [];
        if (list.length === 0) {
            openChat();
            await sleep(69);
            await sendRaw(txt);
            return;
        }
        for (let i = 0; i < list.length; i++) {
            openChat();
            await sleep(69);
            await sendRaw(`/msg ${list[i]} ${txt}`);
            await sleep(200);
        }
    }

    function getCoords() {
        const e = document.querySelector(SEL);
        return e ? e.textContent.trim() : null;
    }

    function pretty(k) {
        if (!k) return '?';
        if (/^Key[A-Z]$/.test(k)) return k.slice(3);
        if (/^Digit[0-9]$/.test(k)) return k.slice(5);
        return k;
    }

    function esc(s) {
        return String(s).replace(/[&<>"]/g, m => ({
            '&': '&amp;',
            '<': '&lt;',
            '>': '&gt;',
            '"': '&quot;'
        } [m]));
    }

    function onKey(ev) {
        if (ev.code === 'F10') {
            toggleHUD();
            ev.preventDefault();
            ev.stopPropagation();
            return;
        }
        const a = document.activeElement;
        if (a && (a.tagName === 'INPUT' || a.tagName === 'TEXTAREA' || a.isContentEditable)) return;
        if (ev.code === cfg.k) {
            const c = getCoords();
            sendAll(c);
        }
    }
    window.addEventListener('keydown', onKey, true);

    function UI() {
        if (document.getElementById('chud')) return;
        const s = document.createElement('style');
        s.textContent = `
#chud{position:fixed;left:12px;top:12px;z-index:2147483646;font-family:Inter,system-ui,Arial,sans-serif;user-select:none;background:linear-gradient(180deg,rgba(16,10,30,0.96),rgba(12,7,22,0.94));color:#f2eefc;border-radius:12px;padding:12px;min-width:260px;box-shadow:0 14px 30px rgba(2,2,8,0.7);backdrop-filter:blur(6px);font-size:13px;overflow:hidden;transition:transform .18s ease,opacity .18s ease}
#chud.hidden{transform:translateY(-10px) scale(.98);opacity:0;pointer-events:none}
#chud .row{display:flex;gap:10px;align-items:center;margin:10px 0}
.btn{cursor:pointer;padding:8px 12px;border-radius:10px;background:linear-gradient(90deg,#6f5bd6,#8f7df0);border:1px solid rgba(255,255,255,0.08);font-size:13px;color:#fff;transition:transform .12s ease,box-shadow .12s ease}
.btn:hover{transform:translateY(-3px);box-shadow:0 10px 30px rgba(111,91,214,0.18)}
#kd{font-weight:700;padding:8px 12px;border-radius:12px;background:linear-gradient(90deg,rgba(150,110,230,0.22),rgba(110,80,200,0.14));border:1px solid rgba(150,110,230,0.18);color:#fff}
#names{margin-top:10px;display:flex;flex-direction:column;gap:8px;max-height:240px;overflow:auto}
.nm{display:flex;justify-content:space-between;align-items:center;padding:8px 10px;border-radius:10px;background:linear-gradient(180deg,rgba(255,255,255,0.02),rgba(255,255,255,0.01));border:1px solid rgba(255,255,255,0.02)}
.nm .del{cursor:pointer;padding:6px;border-radius:8px;background:rgba(255,255,255,0.03)}
#nin{flex:1;padding:8px 10px;border-radius:10px;background:transparent;border:1px solid rgba(255,255,255,0.06);color:inherit}
#msgWrap{max-height:0;opacity:0;overflow:hidden;transition:max-height .32s cubic-bezier(.2,.9,.2,1),opacity .2s ease}
#msgWrap.show{max-height:260px;opacity:1}
.tip{font-size:12px;opacity:0.86}
@keyframes shake{0%{transform:translateX(0)}20%{transform:translateX(-6px)}40%{transform:translateX(6px)}60%{transform:translateX(-4px)}80%{transform:translateX(4px)}100%{transform:translateX(0)}}
.shake{animation:shake .45s}
`;
        document.head.appendChild(s);

        const h = document.createElement('div');
        h.id = 'chud';
        h.innerHTML = `
<div style="display:flex;justify-content:space-between;align-items:center"><div style="font-weight:700">CoordsHelper</div></div>
<div class="row">
  <div style="flex:1">
    <div style="font-size:12px;opacity:0.9">Key</div>
    <div style="display:flex;gap:10px;margin-top:8px;align-items:center">
      <div id="kd">${esc(cfg.kl || pretty(cfg.k))}</div>
      <button class="btn" id="chg">Change</button>
      <button class="btn" id="rst" title="Reset">Reset</button>
    </div>
  </div>
</div>
<div class="row"><label><input type="checkbox" id="msgc" ${cfg.msg ? 'checked' : ''}/> Msg</label></div>
<div id="msgWrap" class="${cfg.msg ? 'show' : ''}">
  <div id="names"></div>
  <div style="display:flex;gap:10px;margin-top:10px">
    <input id="nin" placeholder="add nick (max 2)" maxlength="32" />
    <button class="btn" id="add">Add</button>
  </div>
</div>
<div class="tip" style="margin-top:10px">Press Change then press the key. F10 toggles HUD (not bindable)</div>
`;
        document.body.appendChild(h);

        document.getElementById('msgc').addEventListener('change', async e => {
            cfg.msg = e.target.checked;
            const w = document.getElementById('msgWrap');
            if (w) w.classList.toggle('show', cfg.msg);
            await save(cfg);
        });

        document.getElementById('add').addEventListener('click', async () => {
            const ip = document.getElementById('nin');
            if (!ip) return;
            const v = ip.value.trim();
            if (!v) return;
            cfg.n = cfg.n || [];
            if (cfg.n.length >= 2) {
                flashShake();
                ip.value = '';
                return;
            }
            const safe = v.replace(/[\n\r]/g, '').slice(0, 32);
            cfg.n.push(safe);
            ip.value = '';
            cfg.n = cfg.n.slice(0, 2);
            await save(cfg);
            refreshNames();
        });

        document.getElementById('nin').addEventListener('keydown', ev => {
            if (ev.key === 'Enter') document.getElementById('add').click();
        });

        document.getElementById('chg').addEventListener('click', startCap);
        document.getElementById('rst').addEventListener('click', async () => {
            cfg.k = DEF.k;
            cfg.kl = DEF.kl;
            updKD();
            await save(cfg);
        });

        refreshNames();
    }

    function refreshNames() {
        const c = document.getElementById('names');
        if (!c) return;
        c.innerHTML = '';
        (cfg.n || []).forEach((nm, i) => {
            const w = document.createElement('div');
            w.className = 'nm';
            w.innerHTML = `<div style="overflow:hidden;text-overflow:ellipsis">${esc(nm)}</div><div style="display:flex;gap:10px;align-items:center"><div style="font-size:11px;opacity:.8">#${i+1}</div><div class="del" data-i="${i}">✕</div></div>`;
            c.appendChild(w);
            w.querySelector('.del').addEventListener('click', async (ev) => {
                const idx = Number(ev.currentTarget.getAttribute('data-i'));
                cfg.n.splice(idx, 1);
                cfg.n = cfg.n.slice(0, 2);
                await save(cfg);
                refreshNames();
            });
        });
    }

    function flashShake() {
        const el = document.getElementById('chud');
        if (!el) return;
        el.classList.add('shake');
        setTimeout(() => el.classList.remove('shake'), 480);
    }

    let cap = false;

    function startCap() {
        if (cap) return;
        cap = true;
        const wait = document.getElementById('kd');
        wait.textContent = '...';

        function kh(e) {
            const a = document.activeElement;
            if (a && (a.tagName === 'INPUT' || a.tagName === 'TEXTAREA' || a.isContentEditable)) return;
            const code = e.code || ('Key' + (e.key || '').toUpperCase());
            if (!code) return;
            if (code === 'F10') {
                updKD();
                cap = false;
                window.removeEventListener('keydown', kh, true);
                flashShake();
                return;
            }
            cfg.k = code;
            cfg.kl = (e.key && e.key.length === 1) ? e.key.toUpperCase() : pretty(code);
            updKD();
            save(cfg);
            cap = false;
            window.removeEventListener('keydown', kh, true);
            e.preventDefault();
            e.stopPropagation();
        }
        window.addEventListener('keydown', kh, true);
    }

    function updKD() {
        const el = document.getElementById('kd');
        if (el) el.textContent = cfg.kl || pretty(cfg.k);
    }

    function toggleHUD() {
        const el = document.getElementById('chud');
        if (!el) return;
        el.classList.toggle('hidden');
    }

    async function init() {
        if (!document.body) {
            requestAnimationFrame(init);
            return;
        }
        UI();
        updKD();
        cfg.n = cfg.n.slice(0, 2);
        await save(cfg);
    }

    init();

})();