IC Auto Crafter

Automatically craft by inputting a lineage.

目前為 2025-01-19 提交的版本,檢視 最新版本

您需要先安裝使用者腳本管理器擴展,如 TampermonkeyGreasemonkeyViolentmonkey 之後才能安裝該腳本。

You will need to install an extension such as Tampermonkey to install this script.

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyViolentmonkey 後才能安裝該腳本。

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyUserscripts 後才能安裝該腳本。

你需要先安裝一款使用者腳本管理器擴展,比如 Tampermonkey,才能安裝此腳本

您需要先安裝使用者腳本管理器擴充功能後才能安裝該腳本。

(我已經安裝了使用者腳本管理器,讓我安裝!)

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

(我已經安裝了使用者樣式管理器,讓我安裝!)

// ==UserScript==
// @name         IC Auto Crafter
// @namespace    http://tampermonkey.net/
// @version      1.5
// @license      MIT
// @description  Automatically craft by inputting a lineage.
// @icon         https://i.imgur.com/WlkWOkU.png
// @author       @activetutorial on discord
// @match        https://neal.fun/infinite-craft/
// @run-at       document-end
// @grant        none
// ==/UserScript==

(function() {
    "use strict";

    (window.AT ||= {}).autocrafterdata = {
        iconthing: "data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iaXNvLTg4NTktMSI/Pg0KPCEtLSBVcGxvYWRlZCB0bzogU1ZHIFJlcG8sIHd3dy5zdmdyZXBvLmNvbSwgR2VuZXJhdG9yOiBTVkcgUmVwbyBNaXhlciBUb29scyAtLT4NCjwhRE9DVFlQRSBzdmcgUFVCTElDICItLy9XM0MvL0RURCBTVkcgMS4xLy9FTiIgImh0dHA6Ly93d3cudzMub3JnL0dyYXBoaWNzL1NWRy8xLjEvRFREL3N2ZzExLmR0ZCI+DQo8c3ZnIGZpbGw9IiMwMDAwMDAiIHZlcnNpb249IjEuMSIgaWQ9IkNhcGFfMSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiB4bWxuczp4bGluaz0iaHR0cDovL3d3dy53My5vcmcvMTk5OS94bGluayIgDQoJIHdpZHRoPSI4MDBweCIgaGVpZ2h0PSI4MDBweCIgdmlld0JveD0iMCAwIDQ1LjM2MyA0NS4zNjMiDQoJIHhtbDpzcGFjZT0icHJlc2VydmUiPg0KPGc+DQoJPGc+DQoJCTxwYXRoIGQ9Ik0xLjc4OCwxNi45NDVjMC4zODgsMC4zODUsMC45MTMsMC42MDEsMS40NTksMC42MDFsMjcuNDkzLTAuMDM1djMuODMxYzAuMDAzLDAuODM2LDAuNTU2LDEuNTg2LDEuMzI5LDEuOTA0DQoJCQljMC43NzEsMC4zMTQsMS42NTgsMC4xMzUsMi4yNDYtMC40NTlsOS4wOTEtOS4xOGMxLjA2Mi0xLjA3MSwxLjA2LTIuODAxLTAuMDA5LTMuODY4bC05LjEzNy05LjEzNA0KCQkJYy0wLjU5LTAuNTkxLTEuNDc5LTAuNzY4LTIuMjUtMC40NDZjLTAuNzcsMC4zMTktMS4yNzEsMS4wNzQtMS4yNywxLjkwOEwzMC43NCw1LjlMMy4yMTksNS45MzcNCgkJCUMyLjA4LDUuOTQsMS4xNjEsNi44NjQsMS4xNjMsOC4wMDRsMC4wMTgsNy40ODNDMS4xODIsMTYuMDM0LDEuNDAxLDE2LjU2LDEuNzg4LDE2Ljk0NXoiLz4NCgkJPHBhdGggZD0iTTQyLjE0NiwyNy45MDFsLTI3LjUyMi0wLjAzNWwtMC4wMDEtMy44MzRjMC4wMDItMC44MzUtMC41LTEuNTg3LTEuMjctMS45MDdjLTAuNzcxLTAuMzIxLTEuNjYtMC4xNDYtMi4yNSwwLjQ0NQ0KCQkJbC05LjEzNiw5LjEzNWMtMS4wNjcsMS4wNjQtMS4wNzEsMi43OTYtMC4wMDksMy44NjZsOS4wOSw5LjE4MWMwLjU4OCwwLjU5NiwxLjQ3NSwwLjc3MiwyLjI0NywwLjQ1OA0KCQkJYzAuNzcyLTAuMzE2LDEuMzI2LTEuMDY2LDEuMzI5LTEuOTA0di0zLjgzbDI3LjQ5MywwLjAzNWMwLjU0NywwLDEuMDcyLTAuMjE2LDEuNDU5LTAuNjAyczAuNjA1LTAuOTEsMC42MDctMS40NTZMNDQuMiwyOS45Nw0KCQkJQzQ0LjIwMywyOC44Myw0My4yODQsMjcuOTAzLDQyLjE0NiwyNy45MDF6Ii8+DQoJPC9nPg0KPC9nPg0KPC9zdmc+",
        infinitecraft: null,
        autocraftButton: null,
        anticheat: true,
        saveInfo: true, // Stores how you crafted something
        isCrafting: false,
        popupHTML: `
            <style>
                body {
                    margin: 0;
                    font-family: Arial, sans-serif;
                    background-color: #1e1e1e;
                    color: #f0f0f0;
                    display: flex;
                    flex-direction: column;
                    align-items: center;
                    justify-content: center;
                    height: 100%;
                }
                textarea {
                    width: 90%;
                    height: 100px;
                    margin-bottom: 10px;
                    background-color: #2d2d2d;
                    color: #f0f0f0;
                    border: 1px solid #555;
                    border-radius: 5px;
                    padding: 10px;
                }
                label {
                    font-size: 14px;
                }
                input[type="checkbox"] {
                    margin-right: 5px;
                }
                hr {
                    width: 90%;
                    border: none;
                    border-top: 1px solid #555;
                    margin: 15px 0;
                }
                .highlight {
                    font-weight: bold;
                    color: #ffffff; /* Plain white */
                    margin-bottom: 20px; /* Increased spacing */
                }
                button {
                    color: #ffffff; /* White text */
                    font-weight: bold;
                    text-shadow: 1px 1px 2px #000000; /* Black outline for text */
                    border: none;
                    border-radius: 5px;
                    padding: 10px 20px;
                    cursor: pointer;
                    font-size: 16px;
                    margin-top: 20px; /* Increased spacing */
                }
                button#submitButton {
                    background-color: #4caf50;
                }
                button#submitButton:hover {
                    filter: brightness(0.9);
                }
                button#autoCraftButton {
                    background-color: #d4b106;
                }
                button#autoCraftButton:hover {
                    filter: brightness(0.9);
                }
            </style>
            <textarea id="userInput" placeholder="Enter your lineage..."></textarea>
            <button id="submitButton">Craft</button>
            <hr>
            <div class="highlight">Cheating section:</div>
            <label>
                <input type="checkbox" id="anticheatToggle" checked> Enable Anti-Cheat
            </label>
            <button id="autoCraftButton"></button>`,
        processLineage: async function () {
            const userInput = await this.getUserInput();
            if (typeof userInput.autoCraft === "boolean"){
                console.log("Auto craft started/stopped", this.isCrafting);
                this.craftSomething();
                return;
            }
            const { lineage, anticheatEnabled } = userInput;
            this.anticheat = anticheatEnabled; // Update anticheat option
            const recipes = this.parseRecipes(lineage);

            for (let i = 0; i < recipes.length; i++) {
                const [string1, string2] = recipes[i];
                // Anti cheat
                const element1Exists = this.infinitecraft.elements.some(el => el.text?.toLowerCase() === string1?.toLowerCase());
                const element2Exists = this.infinitecraft.elements.some(el => el.text?.toLowerCase() === string2?.toLowerCase());
                if (!this.anticheat || (element1Exists && element2Exists)) {
                    await this.infinitecraft.craft({text: string1}, {text: string2});
                    this.saveInfo && (this.infinitecraft.elements.at(-1).autocraft = (
                        (element1Exists && element2Exists) ? "lineage" : "cheatlineage") // add craft info
                    );
                    this.infinitecraft.instances.pop();
                } else {
                    await this.showToast(`Skipping craft: You don"t have one of these elements: "${string1}", "${string2}"`);
                }
            }
        },
        parseRecipes: function (lineage) {
            const recipes = [];
            const lines = lineage.split("\n").map(line => line.trim());
            const isNumberedLineage = lines[0].match(/^\d+\.\s*/);

            lines.forEach(line => {
                if (!line || !line.includes(" = ")) return;

                if (isNumberedLineage) {
                    line = line.replace(/^\d+\.\s*/, ""); // Account for numbered lineages
                }

                const [ingredients, result] = line.split(" = ");
                if (!ingredients || !result) return;
                const ingredientList = ingredients.trim().split(" + ");
                if (ingredientList.length < 2) return;
                recipes.push(ingredientList.map(ingredient => ingredient.trim()));
            });

            return recipes;
        },
        craftSomething: async function () { // randomly craft 2 items
            while (this.isCrafting) {
                await this.infinitecraft.craft({
                    text: this.infinitecraft.elements.at(this.infinitecraft.elements.length * Math.random()).text
                }, {
                    text: this.infinitecraft.elements.at(this.infinitecraft.elements.length * Math.random()).text
                });
                this.saveInfo && (this.infinitecraft.elements.at(-1).autocraft = "random"); // set craft info
                this.infinitecraft.instances = [];
                await new Promise(resolve => setTimeout(resolve, 200));
            }
        },
        getUserInput: async function () {
            return new Promise((resolve) => {
                const screenWidth = window.screen.width;
                const screenHeight = window.screen.height;
                const popupWidth = 400;
                const popupHeight = 350;
                const left = (screenWidth - popupWidth) / 2;
                const top = (screenHeight - popupHeight) / 2;
                let popup = window.open("", "", `width=${popupWidth},height=${popupHeight},top=${top},left=${left}`);
                popup.document.write(this.popupHTML); // Open popup
                const autoCraftButton = popup.document.getElementById("autoCraftButton");
                if (this.isCrafting) { // Set auto craft button
                    autoCraftButton.textContent = "Stop Auto Craft";
                    autoCraftButton.style.backgroundColor = "#ff0000";
                } else {
                    autoCraftButton.textContent = "Start Auto Craft";
                    autoCraftButton.style.backgroundColor = "#d4b106";
                }
                autoCraftButton.addEventListener("click", () => {
                    this.isCrafting = !this.isCrafting;
                    popup.close();
                    resolve({ autoCraft: this.isCrafting });
                });
                popup.document.getElementById("submitButton").addEventListener("click", function () {
                    let userInput = popup.document.getElementById("userInput").value;
                    let anticheatEnabled = popup.document.getElementById("anticheatToggle").checked;
                    popup.close();
                    resolve({ lineage: userInput, anticheatEnabled: anticheatEnabled });
                });
            });
        },
        showToast: function(message) {
            const toast = document.createElement("div");
            toast.textContent = message;
            Object.assign(toast.style, {
                position: "fixed",
                left: "50%",
                transform: "translateX(-50%)",
                padding: "10px 20px",
                backgroundColor: "#333",
                color: "#fff",
                borderRadius: "5px",
                fontSize: "16px",
                opacity: "0",
                transition: "opacity 0.5s ease, bottom 0.3s ease",
                marginTop: "10px"
            });
            const existingToasts = document.querySelectorAll(".toast");
            const offset = existingToasts.length * (toast.offsetHeight + 40);
            toast.style.bottom = `${30 + offset}px`;
            toast.classList.add("toast");
            document.body.appendChild(toast);
            (async () => {
                await new Promise(resolve => setTimeout(resolve, 10));
                toast.style.opacity = "1";
                await new Promise(resolve => setTimeout(resolve, 3000));
                toast.style.opacity = "0";
                await new Promise(resolve => setTimeout(resolve, 500));
                toast.remove();
                const remainingToasts = document.querySelectorAll(".toast");
                remainingToasts.forEach((t, index) => {
                    t.style.bottom = `${30 + index * (toast.offsetHeight + 40)}px`;
                });
            })();
            return new Promise(resolve => setTimeout(resolve, 50)); // 0.05 delay
        },
        addUiOption: function () {
            this.autocraftButton = document.createElement("div");
            this.autocraftButton.classList.add("setting");
            this.autocraftButton.textContent = "Autocrafter";
            const img = document.createElement("img");
            img.src = this.iconthing;
            this.autocraftButton.appendChild(img);
            this.autocraftButton.onclick = function () {
                window.AT.autocrafterdata.processLineage();
            };

            document.querySelector(".settings-content").appendChild(this.autocraftButton);
            return true;
        },
        updateInstance: function (instance) {
            const element = this.infinitecraft.elements.find(
                (element) =>
                element.text.toLowerCase() === instance.text.toLowerCase()
            );
            element.autocraft &&
            instance.elem.setAttribute("autocraft", element.autocraft);
        },
        start: function () {
            if (document.querySelector(".settings-content")) { // Wait for IC Helper
                this.infinitecraft = document.querySelector(".container").__vue__;
                this.addUiOption();
                const setInstanceZIndex = this.infinitecraft.setInstanceZIndex;
                this.infinitecraft.setInstanceZIndex = ((instance, instanceID) => {
                    setTimeout(() => {
                        this.updateInstance(instance);
                    }, 0);
                    return setInstanceZIndex(instance, instanceID);
                });
            } else {
                setTimeout(this.start.bind(this), 200);
            }
        }
    };
    window.AT.autocrafterdata.start();

})();