您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Adds draggable combo log for better ad overlay, autocrafting, lineage tracing, and combo search for Infinite Craft
// ==UserScript== // @name InfiniteCraft: Autocraft + Recipe Search // @namespace http://tampermonkey.net/ // @version 6.2.1 // @description Adds draggable combo log for better ad overlay, autocrafting, lineage tracing, and combo search for Infinite Craft // @author Gasclu // @match https://neal.fun/infinite-craft/ // @match https://beta.neal.fun/infinite-craft/ // @grant none // @run-at document-end // @license MIT // ==/UserScript== (function () { "use strict"; const lineageKey = "autocrafter_lineages"; const AT = { infinitecraft: null, isCrafting: false, getLineages() { return JSON.parse(localStorage.getItem(lineageKey) || "{}"); }, saveLineage(a, b, result) { const lineages = AT.getLineages(); lineages[result] = [a, b]; localStorage.setItem(lineageKey, JSON.stringify(lineages)); }, traceSteps(result) { const lineages = AT.getLineages(); const steps = []; const visited = new Set(); function walk(item) { if (visited.has(item)) return; visited.add(item); const inputs = lineages[item]; if (!inputs) return; const [a, b] = inputs; walk(a); walk(b); steps.push({ a, b, result: item }); } walk(result); return steps; }, showLineagePanel(result) { const steps = AT.traceSteps(result); if (steps.length === 0) return AT.showToast(`No lineage for "${result}"`); const container = document.createElement("div"); container.id = "lineage-panel"; container.style = ` position: fixed; top: 10%; left: 50%; transform: translateX(-50%); background: #1e1e1e; color: white; padding: 20px; border-radius: 10px; max-width: 500px; max-height: 70%; overflow-y: auto; z-index: 99999; box-shadow: 0 0 15px #000; `; const lineageHTML = steps.map(({ a, b, result }) => ` <li class="lineage-step" data-result="${result}"> <span class="clickable-element" data-name="${a}">${a}</span> + <span class="clickable-element" data-name="${b}">${b}</span> → <strong class="clickable-element" data-name="${result}">${result}</strong> </li> `).join(""); container.innerHTML = ` <h2 style="margin-top: 0;">📘 How to craft <span style="color:#0f0">${result}</span></h2> <ol style="line-height: 1.6;" id="lineage-steps"> ${lineageHTML} </ol> <button style="margin-top:10px;" onclick="this.parentElement.remove()">Close</button> `; document.body.appendChild(container); const allSteps = Array.from(container.querySelectorAll(".lineage-step")); function highlightLineage(target) { const relatedResults = new Set(AT.traceSteps(target).map(s => s.result)); allSteps.forEach(step => { const r = step.dataset.result; const isRelevant = relatedResults.has(r); step.style.background = isRelevant ? "#003300" : "transparent"; step.style.color = isRelevant ? "lime" : "white"; step.querySelectorAll(".clickable-element").forEach(el => { el.style.color = (isRelevant && el.textContent === target) ? "lime" : (isRelevant ? "#0f0" : "white"); }); }); } container.querySelectorAll('.clickable-element').forEach(el => { el.style.cursor = 'pointer'; el.style.color = '#0f0'; el.onclick = () => { highlightLineage(el.dataset.name); }; }); }, showComboSearch() { const div = document.createElement("div"); div.id = "combo-search"; div.innerHTML = ` <div style=" position: fixed; top: 30%; left: 50%; transform: translateX(-50%); background: linear-gradient(to bottom right, #3c763d, #2e4d2e); color: white; padding: 20px; border-radius: 10px; text-align: center; box-shadow: 0 0 20px #000; z-index: 99999; width: 320px; "> <h3>🔍 Combo Search</h3> <input id="combo-input" type="text" placeholder="Enter element..." style=" width: 90%; padding: 8px; border-radius: 5px; border: none; margin-bottom: 10px; background: #f0fff0; color: black; text-align: center; " /> <div id="combo-results" style="text-align:left; max-height:150px; overflow-y:auto; background:#f0fff0; color:black; padding:5px; border-radius:5px;"></div> <button onclick="document.getElementById('combo-search').remove()">Close</button> </div> `; document.body.appendChild(div); const input = div.querySelector("#combo-input"); const results = div.querySelector("#combo-results"); input.addEventListener("input", () => { const q = input.value.toLowerCase(); results.innerHTML = ""; const matches = []; const lineage = AT.getLineages(); for (let [result, [a, b]] of Object.entries(lineage)) { if (a.toLowerCase().includes(q) || b.toLowerCase().includes(q)) { matches.push(`${a} + ${b} → <strong>${result}</strong>`); } } results.innerHTML = matches.length === 0 ? "<i>No matches found</i>" : "<ul style='padding-left:20px'>" + matches.map(m => `<li>${m}</li>`).join("") + "</ul>"; }); }, showSearchModal() { if (document.getElementById("earth-modal")) return; const div = document.createElement("div"); div.id = "earth-modal"; div.innerHTML = ` <div style=" position: fixed; top: 30%; left: 50%; transform: translateX(-50%); background: linear-gradient(to bottom right, #3c763d, #2e4d2e); color: white; padding: 20px; border-radius: 10px; text-align: center; box-shadow: 0 0 20px #000; z-index: 99999; width: 300px; "> <h3>🌍 Lineage Search</h3> <input id="lineage-input" type="text" autocomplete="off" placeholder="Enter element..." style=" width: 90%; padding: 8px; border-radius: 5px; border: none; margin-bottom: 10px; background: #f0fff0; color: black; text-align: center; " /> <ul id="autocomplete-list" style=" list-style: none; padding: 0; margin: 0 auto 10px; max-height: 100px; overflow-y: auto; background: #f0fff0; color: black; border-radius: 5px; width: 90%; text-align: left; font-size: 14px; "></ul> <button id="lineage-show">Show</button> <button onclick="document.getElementById('earth-modal').remove()">Cancel</button> </div> `; document.body.appendChild(div); const input = document.getElementById("lineage-input"); const list = document.getElementById("autocomplete-list"); input.addEventListener("input", () => { const val = input.value.toLowerCase(); list.innerHTML = ""; if (!val) return; const matches = AT.infinitecraft.items .map(i => i.text) .filter(name => name.toLowerCase().includes(val)) .sort((a, b) => a.toLowerCase().indexOf(val) - b.toLowerCase().indexOf(val)) .slice(0, 8); for (let name of matches) { const li = document.createElement("li"); li.textContent = name; li.style.padding = "5px 10px"; li.style.cursor = "pointer"; li.onclick = () => { input.value = name; list.innerHTML = ""; }; list.appendChild(li); } }); document.getElementById("lineage-show").onclick = () => { const name = input.value.trim(); if (name) AT.showLineagePanel(name); div.remove(); }; }, initComboLog() { let log = document.getElementById("combo-log"); if (!log) { log = document.createElement("div"); log.id = "combo-log"; log.style = ` position: fixed; bottom: 0; left: 50%; transform: translateX(-50%); background: rgba(0,0,0,0.95); color: white; padding: 10px; max-height: 200px; overflow-y: auto; font-family: monospace; font-size: 13px; border-radius: 8px 8px 0 0; z-index: 99999; width: 336px; cursor: move; `; log.innerHTML = `<div id="combo-log-content" style="max-height: 180px; overflow-y: auto;"></div>`; document.body.appendChild(log); // Draggable support let isDragging = false, startX = 0, startLeft = 0; log.addEventListener("mousedown", e => { isDragging = true; startX = e.clientX; startLeft = log.offsetLeft; e.preventDefault(); }); document.addEventListener("mousemove", e => { if (isDragging) { const deltaX = e.clientX - startX; log.style.left = `${startLeft + deltaX}px`; log.style.transform = "translateX(0)"; } }); document.addEventListener("mouseup", () => { isDragging = false; }); } }, logCombo(a, b, result, success) { AT.initComboLog(); const div = document.createElement("div"); div.innerHTML = `${a} + ${b} → <strong>${result || "Nothing"}</strong>`; div.style.color = success ? "lime" : "red"; document.getElementById("combo-log-content").appendChild(div); const log = document.getElementById("combo-log-content"); log.scrollTop = log.scrollHeight; }, showToast(msg) { const div = document.createElement("div"); div.textContent = msg; Object.assign(div.style, { position: "fixed", bottom: "20px", left: "50%", transform: "translateX(-50%)", background: "#444", color: "white", padding: "10px 20px", borderRadius: "6px", zIndex: 10000, opacity: 0, transition: "opacity 0.3s ease" }); document.body.appendChild(div); setTimeout(() => div.style.opacity = 1, 10); setTimeout(() => { div.style.opacity = 0; setTimeout(() => div.remove(), 300); }, 2500); }, monitorCrafts() { const orig = AT.infinitecraft.craft; AT.infinitecraft.craft = async (a, b) => { const result = await orig.call(AT.infinitecraft, a, b); const last = AT.infinitecraft.items.at(-1); if (last && last.text) { AT.saveLineage(a.text, b.text, last.text); } return result; }; }, startAutoCraft() { const loop = async () => { while (AT.isCrafting) { const items = AT.infinitecraft.items; const a = items[Math.floor(Math.random() * items.length)].text; const b = items[Math.floor(Math.random() * items.length)].text; const before = items.length; await AT.infinitecraft.craft({ text: a }, { text: b }); const after = AT.infinitecraft.items.length; const result = AT.infinitecraft.items.at(-1); const success = after > before; if (success && result?.text) { AT.saveLineage(a, b, result.text); } AT.logCombo(a, b, success ? result?.text : null, success); AT.infinitecraft.instances = []; await new Promise(r => setTimeout(r, 200)); } }; loop(); }, addButtons() { const controls = document.querySelector(".side-controls"); if (!controls) return setTimeout(AT.addButtons, 200); const addBtn = (text, action) => { const btn = document.createElement("div"); btn.textContent = text; btn.className = "tool-icon"; btn.style.cssText = ` font-size: 13px; padding: 6px 10px; background: #3a5f3a; color: white; border-radius: 6px; margin-top: 8px; text-align: center; cursor: pointer; `; btn.onclick = action; controls.appendChild(btn); }; const icon = document.createElement("img"); icon.src = "https://i.imgur.com/WlkWOkU.png"; icon.classList.add("tool-icon"); icon.style.width = "32px"; icon.style.height = "32px"; icon.style.filter = "brightness(1)"; icon.title = "Auto-Crafter"; icon.onclick = () => { AT.isCrafting = !AT.isCrafting; icon.style.filter = AT.isCrafting ? "drop-shadow(0 0 5px lime) brightness(1.5)" : "brightness(1)"; if (AT.isCrafting) AT.startAutoCraft(); }; controls.appendChild(icon); addBtn("🌍 Lineage Search", AT.showSearchModal); addBtn("🔍 Combo Search", AT.showComboSearch); }, start() { const vue = document.querySelector(".container")?.__vue__; if (!vue) return setTimeout(AT.start, 200); AT.infinitecraft = vue; AT.addButtons(); AT.monitorCrafts(); console.log("✅ InfiniteCraft v5.9 Loaded with draggable combo log!"); } }; window.AT = AT; AT.start(); })(); ;