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();

})();