Automate Arcanum

Automates the game arcanum by allowing autoclicking and autocasting

You will need to install an extension such as Tampermonkey, Greasemonkey or Violentmonkey to install this script.

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

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

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

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

You will need to install a user script manager extension to install this script.

(I already have a user script manager, let me install it!)

You will need to install an extension such as Stylus to install this style.

You will need to install an extension such as Stylus to install this style.

You will need to install an extension such as Stylus to install this style.

You will need to install a user style manager extension to install this style.

You will need to install a user style manager extension to install this style.

You will need to install a user style manager extension to install this style.

(I already have a user style manager, let me install it!)

// ==UserScript==
// @name         Automate Arcanum
// @namespace    http://tampermonkey.net/
// @version      1.0
// @description  Automates the game arcanum by allowing autoclicking and autocasting
// @author       You
// @match        https://mathiashjelm.gitlab.io/arcanum/
// @icon         https://www.google.com/s2/favicons?sz=64&domain=galaxy.click
// @grant        GM_addStyle
// @license      MIT
// ==/UserScript==

(function() {
    'use strict';
    const customStyles = `
  .timeInput {
    width: 6rem;
    background-color: #181818;
    border-color: #646464;
    color: #D7DADC;
    text-align: center;
    padding: var(--sm-gap);
    margin: var(--sm-gap);
  }
  .checkbox {
    position: relative;
    display: inline-block;
    width: 1.25em;
    height: 1.25em;
    background-color: transparent;
    border: 1px solid #000;
  }
  .autoCheck {
    margin-left: -1.75em;
    margin-top: 1.35em;
  }
  .quickslotTimer {
    width: 3rem;
    background-color: #181818;
    border-color: #646464;
    color: #D7DADC;
    text-align: center;
    padding: var(--sm-gap);
    margin: var(--sm-gap);
    height: 2rem;
  }
  .quickbar {
  flex-direction: column;
  flex-wrap: wrap;
   justify-content: center;
   align-items: center;
  }
`;
    const loadClass = "load-message"
    let hasLoaded = false;
    let actionInterval = 0;
    let autocastInterval = 0;
    let offMain = false;
    let intervalId;
    let autoKey;
    let quickslotTimers = [];

    function beginAutoAction() {
        clearInterval(intervalId);
        intervalId = setInterval(function() {
            const button = document.querySelector(`[data-key="${autoKey}"]`);
            if (button) {
                button.click();
                console.log(`Timer is currently ${actionInterval} and it is clicking ${autoKey}`);
            }
        }, actionInterval);
    }

    function stopAutoAction() {
        clearInterval(intervalId);
    }

    function beginAutoQuickslot(timer, index) {
        clearInterval(quickslotTimers[index]);
        quickslotTimers[index] = setInterval(function() {
            const quickslot = document.querySelector(`#slot${index}`);
            if (quickslot && quickslot.checked) {
                const quickslot = document.getElementsByClassName("quickslot")[index];
                quickslot.children[0].click();
                console.log(`Clicking the quickslot number ${parseInt(index+1)}`)
            }
        }, timer * 1000);
    }

    function stopAutoQuickSlot(index) {
        clearInterval(quickslotTimers[index]);
    }

    function checkOnlyOne(id, n){
        const checkboxes = document.getElementsByName(n);
        Array.prototype.forEach.call(checkboxes,function(e){
            if (e != id) {
                e.checked = false;
            }
        });
    }

    function pageUpdate() {

        const taskList = document.querySelector(".task-list")
        if (hasLoaded) {
            console.log("Page updated, checking if changes need to be made..");
        }

        let buttons = taskList.querySelectorAll(".task-btn");
        let filteredBtns = Array.from(buttons).filter(button => !button.classList.contains("locked"));
        let lockedBtns = Array.from(buttons).filter(button => button.classList.contains("locked"));
        let i = 0;
        let quickslots = document.querySelector(".quickbar").querySelectorAll(".quickslot").forEach(quickslot => {
            if (document.getElementById("slot"+i)) {
                return
            }

            const checkbox = document.createElement("input");
            checkbox.setAttribute("type", "checkbox");
            checkbox.setAttribute("name", "quickslot");
            checkbox.classList.add("quickCheck");
            checkbox.classList.add("checkbox");
            checkbox.id = "slot"+i;
            checkbox.addEventListener("change", function () {
                const checkboxIndex = parseInt(this.id.replace("slot", ""));
                if (this.checked) {
                    beginAutoQuickslot(document.getElementById("timer"+checkboxIndex).value, checkboxIndex);
                } else {
                    stopAutoQuickSlot(checkboxIndex, checkboxIndex);
                }
            });

            const timer = document.createElement("input");
            timer.setAttribute("placeholder", "(s)");
            timer.addEventListener("input", function(e) {
                const inputValue = e.target.value;
                const cleanedValue = inputValue
                .replace(/[^0-9]+/g, '') // Remove non-numeric and non-dot characters
                .replace(/(\..*?)\..*/g, '$1') // Remove multiple dots
                .replace(/^0[^.]/, '0'); // Remove leading zero before a non-dot character

                if (inputValue !== cleanedValue) {
                    // If the value was changed, update the input field
                    e.target.value = cleanedValue;
                }

                if (e.target.id == "actionTimer") {
                    actionInterval = e.target.value;
                    beginAutoAction();
                }

                if (document.querySelector(`#slot${e.target.id.replace("timer", "")}`).checked) {
                    console.log(`The current timer value is ${e.target.value}`);
                    beginAutoQuickslot(e.target.value, e.target.id.replace("timer", ""));
                }
            });
            timer.classList.add("quickslotTimer")
            timer.setAttribute("type", "text");
            timer.id = `timer${i}`;

            quickslot.insertAdjacentElement("afterend", timer);
            quickslot.insertAdjacentElement('beforeend', checkbox);
            i++;
        });

        filteredBtns.forEach(button => {
            const dataKey = button.getAttribute("data-key")
            if (document.getElementById(dataKey)) {
                return
            }
            const checkbox = document.createElement("input");
            checkbox.setAttribute("type", "checkbox");
            checkbox.setAttribute("name", "task");
            checkbox.classList.add("autoCheck");
            checkbox.classList.add("checkbox");
            checkbox.addEventListener("change", function() {
                if (this.checked && actionInterval != 0) {
                    autoKey = dataKey;
                    beginAutoAction();
                } else {
                    stopAutoAction();
                }
            });
            checkbox.id = dataKey
            button.insertAdjacentElement('afterend', checkbox);
        });

        lockedBtns.forEach(button => {
            const dataKey = button.getAttribute("data-key");
            const checkbox = document.getElementById(dataKey);
            if (checkbox) {
                checkbox.parentNode.removeChild(checkbox);
            }
        });
    }

    function handleDivAppeared(mutationsList) {
        for (const mutation of mutationsList) {
            if(mutation.type === "childList") {
                const addedNodes = Array.from(mutation.addedNodes);
                for (const addedNode of addedNodes) {
                    if (addedNode.classList && addedNode.classList.contains("main-tasks")) {
                        if (document.querySelector(".main-tasks")) {
                            offMain = false;
                            pageUpdate();
                        }
                    }
                }

            }
        }

    }

    function handleDivDisappeared(mutationsList) {
        for (const mutation of mutationsList) {
            if (mutation.type === 'childList') {
                const removedNodes = Array.from(mutation.removedNodes);
                for (const removedNode of removedNodes) {
                    if (removedNode.classList && removedNode.classList.contains("main-task")){
                        offMain = true;
                    }
                    if (removedNode.classList && removedNode.classList.contains(loadClass)) {
                        // Save loaded, run script
                        console.log(`Page loaded, beginning automation.`);
                        // Inject the custom CSS styles into the page
                        GM_addStyle(customStyles);
                        hasLoaded = true;
                        const timer = document.createElement("input");
                        timer.classList.add("timeInput");
                        timer.setAttribute("type", "text");
                        timer.id = "actionTimer";
                        const label = document.createElement("label");
                        label.setAttribute("for", "actionTimer")
                        label.innerHTML="Action interval (ms):"
                        document.querySelector(".load-opts").appendChild(timer);
                        document.querySelector(".load-opts").insertBefore(label, document.getElementById("actionTimer"));
                        timer.addEventListener("input", function(e) {
                            const inputValue = e.target.value;
                            const cleanedValue = inputValue
                            .replace(/[^0-9]+/g, '') // Remove non-numeric and non-dot characters
                            .replace(/(\..*?)\..*/g, '$1') // Remove multiple dots
                            .replace(/^0[^.]/, '0'); // Remove leading zero before a non-dot character

                            if (inputValue !== cleanedValue) {
                                // If the value was changed, update the input field
                                e.target.value = cleanedValue;
                            }

                            if (e.target.id == "actionTimer") {
                                actionInterval = e.target.value;
                                beginAutoAction();
                            }
                        });

                        pageUpdate();

                        startClassObserver();
                    }
                }
            }
        }
    }

    function startClassObserver() {
        const classObserver = new MutationObserver((mutationsList, classObserver) => {
            // Handle class attribute changes here
            pageUpdate();
        });

        const classConfig = { attributes: true, attributeFilter: ['class'] };

        // Start observing class attribute changes
        classObserver.observe(document, classConfig);
    }

    // Create a MutationObserver that watches for changes in the DOM
    const observer = new MutationObserver(handleDivDisappeared);
    const observer2 = new MutationObserver(handleDivAppeared);
    const config = { childList: true, subtree: true };

    // Start observing the DOM
    observer.observe(document, config);
    observer2.observe(document, config);

    document.addEventListener("click", function(e) {
        if(e.target.classList.contains("autoCheck")){
            checkOnlyOne(e.target, e.target.getAttribute("name"));
        }
    });

})();