一万亿次免费抽卡加速和自动化 One Trillion Free Draws Accelerator and Automation

同时提供用户控制界面

您需要先安装一个扩展,例如 篡改猴Greasemonkey暴力猴,之后才能安装此脚本。

您需要先安装一个扩展,例如 篡改猴暴力猴,之后才能安装此脚本。

您需要先安装一个扩展,例如 篡改猴暴力猴,之后才能安装此脚本。

您需要先安装一个扩展,例如 篡改猴Userscripts ,之后才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。

您需要先安装用户脚本管理器扩展后才能安装此脚本。

(我已经安装了用户脚本管理器,让我安装!)

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

(我已经安装了用户样式管理器,让我安装!)

// ==UserScript==
// @name         一万亿次免费抽卡加速和自动化 One Trillion Free Draws Accelerator and Automation
// @namespace    xfdz.OTFDAA
// @version      2.0
// @description  同时提供用户控制界面
// @author       Zero(加速), 销锋镝铸(自动化)
// @match        https://duducat.moe/gacha/*
// @match        https://gityxs.github.io/one-trillion-free-draws/*
// @grant        none
// @run-at       document-end
// @license      MIT
// ==/UserScript==

(function () {
    "use strict";
    const defaultOptions = {
        speedMultiplier: 1,
        autoDraw: {
            enabled: false,
            drawOnFull: false
        },
        autoSkill: {
            enabled: false,
            skills: {
                fire: false,
                water: false,
                leaf: false,
                sun: false,
                moon: false
            }
        }
    };
    let options = {};

    let interval = null;
    const intervalActions = new Map();

    let accelerator = null;
    let autoDrawManager = null;
    let autoSkillManager = null;
    let uiManager = null;

    function initialize() {
        try {
            let optionsInLocalStorage = JSON.parse(window.localStorage.getItem("OTFDAAOptions"));
            if (optionsInLocalStorage) {
                try{
                    options = Object.assign({}, defaultOptions, optionsInLocalStorage);
                    console.log("[OTFDAA] 设置已从本地存储中加载");
                }
                catch{
                    options = defaultOptions;
                    console.log("[OTFDAA] 本地存储中的设置存在问题,已初始化设置");
                }
            }
            else {
                options = defaultOptions;
                saveOptions();
                console.log("[OTFDAA] 未找到设置,已初始化设置");
            }
        }
        catch (e) {
            options = defaultOptions;
            console.log("[OTFDAA] 未找到设置,已初始化设置");
        }
        accelerator = new Accelerator();
        autoDrawManager = new AutoDrawManager();
        autoSkillManager = new AutoSkillManager();
        uiManager = new UIManager();
        interval = setInterval(() => {
            for (let action of intervalActions.values()) {
                action();
            }
        }, 500);
        window.addEventListener("beforeunload", () => {
            accelerator.restoreOriginalAPIs();
            clearInterval(interval);
        });
    }

    function saveOptions() {
        window.localStorage.setItem("OTFDAAOptions", JSON.stringify(options));
    }

    class Accelerator {
        originalAPIs;
        performanceNowOverridden = false;
        requestAnimationFrameOverridden = false;
        syncTimeSourcesOverridden = false;

        constructor() {
            this.originalAPIs = {
                raf: window.requestAnimationFrame.bind(window),
                performanceNow: performance.now.bind(performance),
                DateNow: Date.now
            };
            try {
                this.initialize();
                console.log("[OTFDAA] 加速OTFDAA已激活");
            }
            catch (error) {
                console.error("[OTFDAA] 初始化失败:", error);
                setTimeout(this.initialize, 1500); // 重试初始化
            }
        }

        initialize() {
            this.overridePerformanceNow();
            this.overrideRequestAnimationFrame();
            this.syncTimeSources();
        }

        setSpeedMultiplier(multiplier) {
            options.speedMultiplier = multiplier;
            console.log(`[OTFDAA] 已修改游戏速度为 ×${options.speedMultiplier}`);
            saveOptions();
        }

        // 劫持 performance.now
        overridePerformanceNow() {
            if (this.performanceNowOverridden) {
                return;
            }
            Object.defineProperty(performance, "now", {
                value: () => this.originalAPIs.performanceNow() * options.speedMultiplier,
                configurable: true,
                writable: false
            });
            console.log("[OTFDAA] performance.now 已劫持");
            this.performanceNowOverridden = true;
        }

        // 劫持 requestAnimationFrame
        overrideRequestAnimationFrame() {
            if (this.requestAnimationFrameOverridden) {
                return;
            }
            window.requestAnimationFrame = (callback) => {
                return this.originalAPIs.raf((timestamp) => {
                    callback(timestamp * options.speedMultiplier);
                });
            };
            console.log("[OTFDAA] requestAnimationFrame 已劫持");
            this.requestAnimationFrameOverridden = true;
        }

        // 同步时间源
        syncTimeSources() {
            if (this.syncTimeSourcesOverridden) {
                return;
            }
            const baseTime = Date.now();
            Date.now = () => baseTime + (performance.now() - baseTime);
            console.log("[OTFDAA] 时间源已同步");
            this.syncTimeSourcesOverridden = true;
        }

        // 恢复原始 API
        restoreOriginalAPIs() {
            Object.defineProperty(performance, "now", {
                value: this.originalAPIs.performanceNow,
                configurable: true
            });
            window.requestAnimationFrame = this.originalAPIs.raf;
            Date.now = this.originalAPIs.DateNow;
            console.log("[OTFDAA] 原始 API 已恢复");
            this.performanceNowOverridden = false;
            this.requestAnimationFrameOverridden = false;
            this.syncTimeSourcesOverridden = false;
        }
    }

    class AutoDrawManager {

        constructor() {
            this.setAutoDraw(options.autoDraw.enabled, false);
        }

        setAutoDraw(on, save = true) {
            options.autoDraw.enabled = on;
            if (on) {
                if (!intervalActions.has("autoDraw")) {
                    intervalActions.set("autoDraw", this.draw);
                }
            }
            else {
                intervalActions.delete("autoDraw");
            }
            if (save) {
                saveOptions();
            }
        }

        draw() {
            try {
                const drawButton = options.autoDraw.drawOnFull ?
                    document.querySelector("#draw-zone > .currency.f-fire")
                        ?.parentElement
                        ?.querySelector("#draw-button") :
                    document.querySelector("#draw-button");
                if (drawButton) {
                    drawButton.click();
                }
                const cancelButton = document.querySelector(
                    ".card-list.done + .draw-result > button");
                if (cancelButton) {
                    cancelButton.click();
                }
            }
            catch {
            }
        }

        setDrawOnFull(on) {
            options.autoDraw.drawOnFull = on;
            saveOptions();
        }
    }

    class AutoSkillManager {
        constructor() {
            this.setAutoSkillEnabled(options.autoSkill.enabled, false);
        }

        setAutoSkillEnabled(on, save = true) {
            options.autoSkill.enabled = on;
            if (on) {
                if (!intervalActions.has("autoSkill")) {
                    intervalActions.set("autoSkill", this.useSkill);
                }
            }
            else {
                intervalActions.delete("autoSkill");
            }
            if (save) {
                saveOptions();
            }
        }

        useSkill() {
            if (!options.autoSkill.enabled) {
                return;
            }
            const skillHolder = document.querySelector("#draw-options .skill-holder");
            if (!skillHolder) {
                return;
            }
            const skillButtons = skillHolder.children;
            for (let i = 0; i < 5; i++) {
                let skillButton = skillButtons[i];
                if (skillButton.classList.contains("disabled")) {
                    continue;
                }
                for (const [skill, on] of Object.entries(options.autoSkill.skills)) {
                    if (on) {
                        const button = skillHolder.querySelector(`.f-${skill}:not(.disabled)`);
                        if (button) {
                            button.click();
                        }
                    }
                }
            }
        }

        setAutoSkill(skill, on) {
            switch (skill) {
                case "fire":
                    options.autoSkill.skills.fire = on;
                    break;
                case "water":
                    options.autoSkill.skills.water = on;
                    break;
                case "leaf":
                    options.autoSkill.skills.leaf = on;
                    break;
                case "sun":
                    options.autoSkill.skills.sun = on;
                    break;
                case "moon":
                    options.autoSkill.skills.moon = on;
                    break;
            }
            saveOptions();
        }
    }

    class UIManager {
        constructor() {
            this.insertStyle();
            this.createControlPanel();
        }

        insertStyle() {
            const style = document.createElement("style");
            style.textContent = `
#OTFDAAControlPanel {
    display: flex;
    flex-direction: column;
    gap: 8px;
    position: fixed;
    bottom: 40px;
    left: 25px;
    max-width: 248px;
    z-index: 2000001;
}
#OTFDAAControlPanel .OTFDAAOptionContainer {
    display: flex;
    gap: 12px;
    justify-content: space-between;
    align-items: center;
    min-height: 35px;
}
#OTFDAASkillPanel {
    display: flex;
    gap: 2px;
}
#OTFDAASkillPanel button{
    flex: 1;
    aspect-ratio: 1;
}`;
            document.head.appendChild(style);
        }

        createControlPanel() {
            if (document.getElementById("OTFDAAControlPanel")) {
                return;
            }
            const panelContainer = document.createElement("div");
            panelContainer.id = "OTFDAAControlPanel";
            panelContainer.classList.add("opt-container");
            const panelTitle = document.createElement("h3");
            panelTitle.textContent = "OTFDAA 控制台";
            panelTitle.style.marginTop = "10px";
            panelContainer.appendChild(panelTitle);
            this.createCheckbox("自动抽卡", panelContainer, options.autoDraw.enabled, event => {
                autoDrawManager.setAutoDraw(event.target.checked);
            });
            this.createCheckbox("等待批量能量达到上限",
                panelContainer,
                options.autoDraw.drawOnFull,
                event => {
                    autoDrawManager.setDrawOnFull(event.target.checked);
                });
            this.createCheckbox("自动技能", panelContainer, options.autoSkill.enabled, event => {
                autoSkillManager.setAutoSkillEnabled(event.target.checked);
                const skillPanel = document.getElementById("OTFDAASkillPanel");
                if (skillPanel) {
                    skillPanel.style.display = event.target.checked ? "flex" : "none";
                }
            });
            this.createNumberInput("加速倍率",
                panelContainer,
                0.1,
                100,
                0.1,
                options.speedMultiplier,
                event => {
                    let num = event.target.valueAsNumber;
                    if (num < 0.1) {
                        num = 0.1;
                        event.target.value = num;
                    }
                    if (num > 100) {
                        num = 100;
                        event.target.value = num;
                    }
                    accelerator.setSpeedMultiplier(num);
                });
            document.body.appendChild(panelContainer);
            this.createSkillButtons();
        }

        createCheckbox(title, parent, checked, onChange) {
            const container = document.createElement("div");
            container.classList.add("OTFDAAOptionContainer");
            const titleLabel = document.createElement("label");
            titleLabel.textContent = title;
            titleLabel.htmlFor = title;
            const checkbox = document.createElement("input");
            checkbox.type = "checkbox";
            checkbox.id = title;
            checkbox.checked = checked;
            checkbox.addEventListener("change", onChange);
            container.appendChild(titleLabel);
            container.appendChild(checkbox);
            parent.appendChild(container);
        }

        createNumberInput(title, parent, min, max, step, value, onChange) {
            const container = document.createElement("div");
            container.classList.add("OTFDAAOptionContainer");
            const titleLabel = document.createElement("label");
            titleLabel.textContent = title;
            titleLabel.htmlFor = title;
            const input = document.createElement("input");
            input.type = "number";
            input.id = title;
            input.min = min;
            input.max = max;
            input.step = step;
            input.value = value;
            input.addEventListener("change", onChange);
            container.appendChild(titleLabel);
            container.appendChild(input);
            parent.appendChild(container);
        }

        createSlider(title, parent, min, max, step, value, onChange) {
            const container = document.createElement("div");
            container.classList.add("OTFDAAOptionContainer");
            const titleSpan = document.createElement("span");
            titleSpan.textContent = title;
            const sliderContainer = document.createElement("div");
            const sliderInput = document.createElement("input");
            sliderInput.type = "range";
            sliderInput.id = title;
            sliderInput.min = min;
            sliderInput.max = max;
            sliderInput.step = step;
            sliderInput.value = value;
            sliderInput.style.width = "140px";
            const sliderLabel = document.createElement("label");
            sliderLabel.textContent = value.toFixed(1);
            sliderLabel.htmlFor = title;
            sliderInput.addEventListener("change", event => {
                sliderLabel.textContent = "×" + sliderInput.valueAsNumber.toFixed(1);
                onChange(event);
            });
            sliderContainer.appendChild(sliderLabel);
            sliderContainer.appendChild(sliderInput);
            container.appendChild(titleSpan);
            container.appendChild(sliderContainer);
            parent.appendChild(container);
        }

        createSkillButtons() {
            if (document.getElementById("OTFDAASkillPanel")) {
                return;
            }
            const skillHolder = document.querySelector("#draw-options .skill-holder");
            if (!skillHolder) {
                return;
            }
            const container = document.createElement("div");
            container.style.display = options.autoSkill.enabled ? "flex" : "none";
            container.id = "OTFDAASkillPanel";
            for (const [skill, on] of Object.entries(options.autoSkill.skills)) {
                const button = document.createElement("button");
                button.classList.add(`f-${skill}`);
                button.disabled = true;
                const checkbox = document.createElement("input");
                checkbox.type = "checkbox";
                checkbox.checked = on;
                checkbox.addEventListener("change", event => {
                    autoSkillManager.setAutoSkill(skill, event.target.checked);
                });
                button.appendChild(checkbox);
                container.appendChild(button);
            }
            skillHolder.after(container);
        }
    }

    if (!window.OTFDAALoaded) {
        window.OTFDAALoaded = true;
        if (document.readyState === "complete") {
            initialize();
        }
        else {
            window.addEventListener("load", () => {
                initialize();
            });
        }
    }
})();