// ==UserScript== // @name MergeCiv 修改器 (压缩版,解决剪贴板限制) // @namespace http://tampermonkey.net/ // @version 3.8 // @description 终极杀器:底层时间欺骗,无视一切闭包与框架防御 (含压缩导出) // @author ZMM // @match https://gityxs.github.io/merge-civ/* // @grant none // @run-at document-start // ==/UserScript== (function () { 'use strict'; /* ========== 配置持久化 ========== */ const CFG_KEY = '__mc_mod_cfg__'; const defaultCfg = { enabled: true, food: '1e170', wood: '1e170', ore: '1e170', metal: '1e170', gold: '1e170', research: '1e170', syncLimits: true, unlockAll: false, culturePoints: '0', magicCrystals: '0', timeCheat: false, timeMultiplier: '2' }; let cfg = Object.assign({}, defaultCfg); try { cfg = Object.assign(cfg, JSON.parse(localStorage.getItem(CFG_KEY) || '{}')); } catch (e) { localStorage.removeItem(CFG_KEY); } function saveCfg() { localStorage.setItem(CFG_KEY, JSON.stringify(cfg)); } /* ========== 核心 0:抓取游戏存档 ========== */ let caughtSave = { key: 'mergeCivSave', rawData: null }; window.addEventListener('load', function() { const saveData = localStorage.getItem(caughtSave.key); if (saveData) { caughtSave.rawData = saveData; } }); /* ========== 核心 1:完美的时间欺骗引擎 ========== */ let timeEngineLastReal = performance.now(); let timeEngineFakeTime = timeEngineLastReal; const OrigPerfNow = performance.now.bind(performance); const OrigDateNow = Date.now; performance.now = function() { const realNow = OrigPerfNow(); const realDelta = realNow - timeEngineLastReal; timeEngineLastReal = realNow; const multiplier = cfg.timeCheat ? (Number(cfg.timeMultiplier) || 2) : 1; timeEngineFakeTime += realDelta * multiplier; return timeEngineFakeTime; }; Date.now = function() { return Math.floor(timeEngineFakeTime); }; /* ========== 核心 2:JSON.parse (一次性覆盖,保留原有功能) ========== */ const origParse = JSON.parse; JSON.parse = function (text, reviver) { try { const result = origParse.call(this, text, reviver); if (result && typeof result === 'object' && result.resources && 'food' in result.resources) { if (cfg.enabled) { const resKeys = ['food', 'wood', 'ore', 'metal', 'gold', 'research']; for (const k of resKeys) { if (k in result.resources && cfg[k]) try { result.resources[k] = Number(cfg[k]); } catch (e) {} } if (cfg.syncLimits && result.limits) { for (const k of resKeys) { if (k in result.limits && cfg[k]) try { result.limits[k] = Number(cfg[k]); } catch (e) {} } } if (cfg.unlockAll && result.upgrades) { for (const k in result.upgrades) { if (typeof result.upgrades[k] === 'boolean') result.upgrades[k] = true; } } if (cfg.culturePoints && Number(cfg.culturePoints) > 0 && result.empire) result.empire.culturePoints = Number(cfg.culturePoints); if (cfg.magicCrystals && Number(cfg.magicCrystals) > 0 && result.empire) { result.empire.magicCrystals = Number(cfg.magicCrystals); result.empire.assignedCrystals = Number(cfg.magicCrystals); } } } return result; } catch(e) { return origParse.call(this, text, reviver); } }; /* ========== 注入 UI ========== */ function injectUI() { try { // 添加 LZString 压缩库(内置) const lzString = ` var LZString = { compressToBase64: function (input) { if (input == null) return ""; var output = "", i, val; for (i = 0; i < input.length; i += 3) { val = input.charCodeAt(i) << 16; if (i + 1 < input.length) val |= input.charCodeAt(i + 1) << 8; if (i + 2 < input.length) val |= input.charCodeAt(i + 2); for (var j = 0; j < 4; j++) { if (i * 8 + j * 6 > input.length * 8) output += "="; else output += "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/".charAt((val >>> 6 * (3 - j)) & 0x3F); } } return output; }, decompressFromBase64: function (input) { if (input == null) return ""; var output = "", ol = 0, outputLength = 0, bitBuffer = 0, bitsInBuffer = 0, i = 0, val; for (i = 0; i < input.length; i++) { val = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/".indexOf(input.charAt(i)); if (val >= 0) { bitBuffer = (bitBuffer << 6) | val; bitsInBuffer += 6; if (bitsInBuffer >= 8) { output += String.fromCharCode((bitBuffer >>> (bitsInBuffer -= 8)) & 0xFF); outputLength++; } } } return output; } }; `; const script = document.createElement('script'); script.textContent = lzString; document.head.appendChild(script); const css = document.createElement('style'); css.textContent = ` #mc-mod-btn{ position:fixed; z-index:2147483647; width:42px; height:42px; border-radius:50%; border:2px solid #c9a84c; background:linear-gradient(135deg,#3d2e1a 0%,#5a432a 100%); color:#c9a84c; font:bold 18px/42px sans-serif; text-align:center; cursor:pointer; user-select:none; box-shadow:0 2px 12px rgba(0,0,0,.6); transition:transform .15s,box-shadow .15s; right:16px; top:16px; touch-action:none; } #mc-mod-btn:hover{ transform:scale(1.12); box-shadow:0 0 18px rgba(201,168,76,.5); } #mc-mod-panel{ position:fixed; z-index:2147483646; right:70px; top:16px; width:320px; max-height:90vh; overflow-y:auto; background:rgba(30,24,18,.96); border:1.5px solid #c9a84c; border-radius:10px; box-shadow:0 4px 30px rgba(0,0,0,.7); font:13px/1.5 'Segoe UI',sans-serif; color:#e0d5c3; transition:opacity .2s,transform .2s; transform-origin:top right; } #mc-mod-panel.mc-hidden{ opacity:0; transform:scale(.92); pointer-events:none; } .mc-hd{ display:flex; align-items:center; justify-content:space-between; padding:10px 14px; background:rgba(201,168,76,.12); border-bottom:1px solid rgba(201,168,76,.25); cursor:move; border-radius:10px 10px 0 0; user-select:none; } .mc-hd span{ color:#c9a84c; font-weight:700; font-size:14px; letter-spacing:.5px; } .mc-close{ background:none; border:none; color:#c9a84c; font-size:20px; cursor:pointer; line-height:1; } .mc-close:hover{ color:#fff; } .mc-body{ padding:12px 14px 16px; } .mc-row{ display:flex; align-items:center; justify-content:space-between; margin-bottom:8px; } .mc-row label{ flex:1; color:#bbb; font-size:12px; } .mc-row input[type=number]{ width:130px; padding:4px 8px; border:1px solid rgba(201,168,76,.35); border-radius:5px; background:rgba(0,0,0,.35); color:#e0d5c3; font:13px monospace; outline:none; text-align:right; } .mc-row input[type=number]:focus{ border-color:#c9a84c; box-shadow:0 0 0 2px rgba(201,168,76,.2); } .mc-toggle{ display:flex; align-items:center; gap:8px; margin-bottom:10px; cursor:pointer; } .mc-toggle input{ display:none; } .mc-sw{ position:relative; width:36px; height:20px; border-radius:10px; background:rgba(255,255,255,.12); transition:background .2s; flex-shrink:0; } .mc-sw::after{ content:''; position:absolute; top:2px; left:2px; width:16px; height:16px; border-radius:50%; background:#888; transition:transform .2s,background .2s; } .mc-toggle input:checked+.mc-sw{ background:rgba(201,168,76,.35); } .mc-toggle input:checked+.mc-sw::after{ transform:translateX(16px); background:#c9a84c; } .mc-toggle span:last-child{ font-size:12px; color:#bbb; } .mc-sep{ height:1px; background:rgba(201,168,76,.18); margin:10px 0; } .mc-apply{ display:block; width:100%; padding:9px 0; margin-top:6px; border:none; border-radius:6px; cursor:pointer; background:linear-gradient(135deg,#c9a84c,#a68a3a); color:#1a1510; font:bold 13px sans-serif; letter-spacing:.5px; transition:filter .15s,transform .1s; } .mc-apply:hover{ filter:brightness(1.15); } .mc-apply:active{ transform:scale(.97); } .mc-io-row{ display:flex; gap:8px; margin-top:6px; } .mc-io-btn{ flex:1; padding:7px 0; border:1px solid rgba(201,168,76,.35); border-radius:6px; cursor:pointer; background:rgba(201,168,76,.08); color:#c9a84c; font:bold 12px sans-serif; transition:filter .15s, background .15s; } .mc-io-btn:hover{ background:rgba(201,168,76,.2); } .mc-io-btn:active{ transform:scale(.97); } .mc-hint{ text-align:center; font-size:11px; color:#776; margin-top:6px; transition:color .2s; min-height: 16px; } `; document.head.appendChild(css); const btn = document.createElement('div'); btn.id = 'mc-mod-btn'; btn.textContent = 'M'; btn.title = 'MergeCiv Mod'; document.body.appendChild(btn); const panel = document.createElement('div'); panel.id = 'mc-mod-panel'; panel.classList.add('mc-hidden'); const resItems = [['food', '食物'], ['wood', '木材'], ['ore', '矿石'], ['metal', '金属'], ['gold', '黄金'], ['research', '研究']]; const extraItems = [['culturePoints', '文化点'], ['magicCrystals', '魔法水晶']]; function buildRows(items) { return items.map(([key, label]) => `
`).join(''); } function checkedAttr(v) { return v ? ' checked' : ''; } panel.innerHTML = `
MergeCiv Mod v3.8
${buildRows(resItems)}
${buildRows(extraItems)}
底层引擎级加速,开启后立即生效
`; document.body.appendChild(panel); /* --- 圆形按钮拖动与点击判断 --- */ (function makeBtnDraggable() { const el = btn; let dragging = false, startX = 0, startY = 0, origLeft = 0, origTop = 0; function onPointerDown(e) { if (e.button !== undefined && e.button !== 0) return; dragging = true; const point = e.touches ? e.touches[0] : e; startX = point.clientX; startY = point.clientY; const rect = el.getBoundingClientRect(); origLeft = rect.left; origTop = rect.top; el.style.right = 'auto'; el.style.left = origLeft + 'px'; el.style.top = origTop + 'px'; e.preventDefault(); } function onPointerMove(e) { if (!dragging) return; const point = e.touches ? e.touches[0] : e; const dx = point.clientX - startX; const dy = point.clientY - startY; el.style.left = (origLeft + dx) + 'px'; el.style.top = (origTop + dy) + 'px'; e.preventDefault(); } function onPointerUp(e) { if (!dragging) return; dragging = false; const point = e.changedTouches ? e.changedTouches[0] : e; const dx = point.clientX - startX; const dy = point.clientY - startY; const distance = Math.sqrt(dx * dx + dy * dy); if (distance < 5) { panel.classList.toggle('mc-hidden'); } } el.addEventListener('mousedown', onPointerDown); document.addEventListener('mousemove', onPointerMove); document.addEventListener('mouseup', onPointerUp); el.addEventListener('touchstart', onPointerDown, { passive: false }); document.addEventListener('touchmove', onPointerMove, { passive: false }); document.addEventListener('touchend', onPointerUp); })(); /* --- 提示语逻辑 --- */ const hintEl = panel.querySelector('.mc-hint'); function showHint(msg, isError = false) { hintEl.textContent = msg; hintEl.style.color = isError ? '#ff6b6b' : '#c9a84c'; setTimeout(() => { hintEl.textContent = "底层引擎级加速,开启后立即生效"; hintEl.style.color = '#776'; }, 3000); } /* --- 导入导出逻辑 (压缩版) --- */ document.getElementById('mc-export').onclick = async function() { if (!caughtSave.rawData) { showHint("❌ 导出失败:游戏存档未加载", true); return; } // 打包数据:配置 + 存档 const exportData = { modCfg: cfg, gameSaveKey: caughtSave.key, gameSaveData: caughtSave.rawData }; const str = JSON.stringify(exportData); // 压缩数据 const compressed = LZString.compressToBase64(str); if (compressed.length > 1000000) { // 检查是否仍过大 showHint("❌ 压缩后仍超过限制,请尝试分块传输", true); return; } try { await navigator.clipboard.writeText(compressed); showHint("✅ 压缩数据已复制到剪贴板!"); } catch (e) { try { const ta = document.createElement('textarea'); ta.value = compressed; ta.style.cssText = 'position:fixed;left:-9999px;'; document.body.appendChild(ta); ta.select(); document.execCommand('copy'); document.body.removeChild(ta); showHint("✅ 压缩数据已复制到剪贴板!"); } catch (err) { showHint("❌ 复制失败,请检查浏览器权限", true); } } }; document.getElementById('mc-import').onclick = async function() { let str = ''; try { str = await navigator.clipboard.readText(); } catch (e) { str = prompt("自动读取失败,请长按此处粘贴压缩数据:"); } if (!str) return; try { // 解压数据 const decompressed = LZString.decompressFromBase64(str); if (!decompressed) { showHint("❌ 解压失败:数据格式错误", true); return; } const importData = JSON.parse(decompressed); if (!importData.modCfg || !importData.gameSaveKey || !importData.gameSaveData) { showHint("❌ 格式错误:不是有效的总进度代码", true); return; } // 恢复配置 Object.assign(cfg, importData.modCfg); saveCfg(); // 恢复存档 localStorage.setItem(importData.gameSaveKey, importData.gameSaveData); showHint("✅ 导入成功,即将刷新生效..."); setTimeout(() => location.reload(), 1000); } catch (err) { showHint("❌ 解析失败:请确认复制的是完整代码", true); } }; /* --- 其他交互 --- */ panel.querySelector('.mc-close').onclick = () => panel.classList.add('mc-hidden'); let dragging = false, dx = 0, dy = 0; const hd = panel.querySelector('.mc-hd'); hd.addEventListener('mousedown', e => { if (e.target.closest('.mc-close')) return; dragging = true; const r = panel.getBoundingClientRect(); dx = e.clientX - r.left; dy = e.clientY - r.top; panel.style.right = 'auto'; panel.style.left = r.left + 'px'; panel.style.top = r.top + 'px'; e.preventDefault(); }); document.addEventListener('mousemove', e => { if (!dragging) return; panel.style.left = (e.clientX - dx) + 'px'; panel.style.top = (e.clientY - dy) + 'px'; }); document.addEventListener('mouseup', () => { dragging = false; }); panel.querySelector('.mc-apply').onclick = () => { cfg.enabled = document.getElementById('mc-enabled').checked; cfg.syncLimits = document.getElementById('mc-sync-limits').checked; cfg.unlockAll = document.getElementById('mc-unlock-all').checked; cfg.timeCheat = document.getElementById('mc-time-cheat').checked; panel.querySelectorAll('input[data-key]').forEach(inp => { cfg[inp.dataset.key] = inp.value; }); saveCfg(); location.reload(); }; panel.querySelectorAll('input[data-key]').forEach(inp => { inp.addEventListener('input', () => { cfg[inp.dataset.key] = inp.value; saveCfg(); }); }); document.getElementById('mc-enabled').addEventListener('change', function () { cfg.enabled = this.checked; saveCfg(); }); document.getElementById('mc-sync-limits').addEventListener('change', function () { cfg.syncLimits = this.checked; saveCfg(); }); document.getElementById('mc-unlock-all').addEventListener('change', function () { cfg.unlockAll = this.checked; saveCfg(); }); document.getElementById('mc-time-cheat').addEventListener('change', function () { cfg.timeCheat = this.checked; saveCfg(); }); } catch(err) { console.error("MC Mod UI Error:", err); } } if (document.readyState === 'loading') { document.addEventListener('DOMContentLoaded', injectUI); } else { injectUI(); } })();