Greasy Fork 还支持 简体中文。

Better BiteFight

Adds an healthbar, energybar, links and other QOL to BiteFight

ของเมื่อวันที่ 23-11-2024 ดู เวอร์ชันล่าสุด

คุณจะต้องติดตั้งส่วนขยาย เช่น Tampermonkey, Greasemonkey หรือ Violentmonkey เพื่อติดตั้งสคริปต์นี้

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

คุณจะต้องติดตั้งส่วนขยาย เช่น Tampermonkey หรือ Violentmonkey เพื่อติดตั้งสคริปต์นี้

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         Better BiteFight
// @namespace    https://lobby.bitefight.gameforge.com/
// @version      0.7.2
// @description  Adds an healthbar, energybar, links and other QOL to BiteFight
// @author       Spychopat
// @match        https://*.bitefight.gameforge.com/*
// @icon         https://lobby.bitefight.gameforge.com/favicon.ico
// @grant        GM_getValue
// @grant        GM_setValue
// @grant        GM_addStyle
// ==/UserScript==

(function () {
    'use strict';

    // uncomment to reset character data if needed
    //GM_setValue('character', {})

    // Script storage keys
    const KEY_CHARACTER = 'character';
    const pageLoadTime = Date.now();

    // Define character object
    const CHARACTER = GM_getValue(KEY_CHARACTER, {
        energy: 0,
        maxEnergy: 0,
        health: 0,
        maxHealth: 0,
        regenHealth: 0,
        potionCooldownEnd: 0,
        churchCooldownEnd: 0,
        autoRedirectGrotte: true,
        autoGrotto: [false, false, false],
        goldEarned: { 0: [], 1: [], 2: [] },
        dmgTaken: { 0: [], 1: [], 2: [] },
        xpEarned: { 0: [], 1: [], 2: [] }
    });

    //console.log(parseInt(formatNumber(document.getElementsByClassName("gold")[1].firstChild.textContent.split("\n")[1])));


    redirectAfterGrotteFight(); // run it first, because it has chances to redirect, so we don't work uselessly

    // early, to avoid the page jump as much as possible
    insertCSS();
    addAdditionnalLink();

    extractCharacterStats();
    updatePotionTimer(); // Run the church cooldown update function if on the church page
    updateRegenHealth();
    insertProgressBars(); // Insert progress bars after updating the character
    startHealthRegeneration(); // Start health regeneration on page load


    if (window.location.pathname.contains('/profile')){
        moveGameEventDiv(); // move down the game event div
    } else if (window.location.pathname.contains('/city/church')){
        updateChurchCooldownTimer();
    } else if (window.location.pathname.contains('/city/grotte')){
        addAutoGrottoButton();
        autoFightGrotto();
    } else if (window.location.pathname.contains('/city/shop')){
        defaultNonPremiumShop();
    }
    updateCharacter();
    //console.log(CHARACTER);



    function insertCSS() {
        GM_addStyle(`
        #upgrademsg {
            display: none;
        }
        #premium > img {
            display: none;
        }
        #mmonetbar {
            display: none !important;
            visibility: hidden;
        }
    `);
    }

    function extractCharacterStats(){
        // Get Stats
        var allStatsElement = document.getElementsByClassName("gold")[0];
        var statsValues = allStatsElement.textContent.split("\n");
        statsValues = statsValues.map(value => value.trim());
        statsValues.shift();

        // Extract energy, fragments, gold, health, and hellStones
        var energy = statsValues[3].trim();
        var currentEnergy = energy.split("/")[0];
        var maxEnergy = energy.split("/")[1];

        if (currentEnergy && maxEnergy) {
            CHARACTER.energy = parseInt(currentEnergy); // Use parseFloat to preserve decimals
            CHARACTER.maxEnergy = parseInt(maxEnergy); // Use parseFloat to preserve decimals
        }

        var health = statsValues[4].trim();
        var currentHealth = formatNumber(health.split("/")[0]);
        var maxHealth = formatNumber(health.split("/")[1]);

        if (currentHealth && maxHealth) {
            CHARACTER.health = parseInt(currentHealth);
            CHARACTER.maxHealth = parseInt(maxHealth);
        }
    }

    // Format texts to return as numbers (no thousand separators)
    function formatNumber(value) {
        while (value.indexOf(".") > 0) value = value.replace(".", "");
        return value;
    }

    function updateRegenHealth() {
        if (!window.location.pathname.endsWith('/profile/index')) return;
        var elements = document.getElementsByClassName("triggerTooltip");

        // Loop through the elements
        for (let i = 0; i < elements.length; i++) {
            // Check if the inner text or inner HTML contains "/ h"
            if (elements[i].innerText.includes("/ h") || elements[i].innerHTML.includes("/ h")) {
                CHARACTER.regenHealth = parseInt(elements[i].textContent);
                //console.log("Regen per hour found : ", parseInt(elements[i].textContent));
                break; // Exit the loop once the element is found
            }
        }
    }

    // Update character in local storage
    function updateCharacter() {
        GM_setValue(KEY_CHARACTER, CHARACTER);
    }

    function updatePotionTimer() {
        if (!window.location.pathname.endsWith('/profile/index')) return; // Ensure this only runs on the right page
        // Get all elements with the class "inactive"
        const inactiveElements = document.getElementsByClassName('inactive');
        let targetElement = null;
        // Loop through each "inactive" element to find the target
        for (const inactiveElement of inactiveElements) {
            // Find elements with the class "countdown_row countdown_amount" within the current "inactive" element
            const matchingElements = inactiveElement.getElementsByClassName('countdown_row countdown_amount');
            if (matchingElements.length > 0) {
                targetElement = matchingElements[0]; // Take the first matching element
                break; // Stop once we find the target
            }
        }

        if (targetElement) {
            // Get the current time and add the potion cooldown to get the end time
            const currentTime = new Date().getTime() / 1000; // Current time in seconds
            const cooldownTime = timeToSeconds(targetElement.textContent); // Convert cooldown time to seconds
            const endTime = currentTime + cooldownTime; // Calculate the end time

            // Save the end time to the character object
            CHARACTER.potionCooldownEnd = endTime;
            updateCharacter(); // Save updated character data
        }
    }

    function timeToSeconds(timeStr) {
        const [hours, minutes, seconds] = timeStr.split(':').map(Number);
        return (hours * 3600) + (minutes * 60) + seconds;
    }

    function insertProgressBars() {
        // Check if the layout container is already present
        if (document.getElementById('progressBarsTimersContainer')) {
            return;
        }

        // Create the main container for the layout
        let mainContainer = document.createElement('div');
        mainContainer.id = 'progressBarsTimersContainer';
        mainContainer.style.display = 'flex';
        mainContainer.style.justifyContent = 'space-between';
        mainContainer.style.alignItems = 'center';
        mainContainer.style.marginTop = '3px';

        // Left section for progress bars
        let progressBarsContainer = document.createElement('div');
        progressBarsContainer.style.display = 'flex';
        progressBarsContainer.style.flexDirection = 'column';
        progressBarsContainer.style.alignItems = 'flex-start';
        progressBarsContainer.style.paddingLeft = '50px';

        // Create Health Progress Bar
        let healthBarContainer = document.createElement('div');
        healthBarContainer.style.width = '250px';
        healthBarContainer.style.position = 'relative';
        healthBarContainer.id = 'healthProgressBar';
        healthBarContainer.style.backgroundColor = 'black'; // Black background
        healthBarContainer.style.border = '3px solid rgb(117, 117, 117)'; // White outline
        healthBarContainer.style.borderRadius = '3px 3px 0px 0px';


        let healthBar = document.createElement('div');
        healthBar.style.height = '20px';
        healthBar.style.width = `${(CHARACTER.health / CHARACTER.maxHealth) * 100}%`;
        healthBar.style.backgroundColor = '#b80000';

        let healthText = document.createElement('div');
        healthText.textContent = `${CHARACTER.health > 999 ? CHARACTER.health.toString().replace(/\B(?=(\d{3})+(?!\d))/g, '.') : CHARACTER.health}`;
        healthText.style.position = 'absolute';
        healthText.style.top = '50%';
        healthText.style.left = '50%';
        healthText.style.transform = 'translate(-50%, -50%)';
        healthText.style.color = 'white';
        healthText.style.fontSize = '12px';
        healthText.style.fontFamily = 'monospace';

        healthBarContainer.appendChild(healthBar);
        healthBarContainer.appendChild(healthText);

        // Create Energy Progress Bar
        let energyBarContainer = document.createElement('div');
        energyBarContainer.style.width = '250px';
        energyBarContainer.style.marginBottom = '5px';
        energyBarContainer.style.position = 'relative';
        energyBarContainer.id = 'energyProgressBar';
        energyBarContainer.style.backgroundColor = 'black'; // Black background
        energyBarContainer.style.borderLeft = '3px solid rgb(117, 117, 117)'; // White outline
        energyBarContainer.style.borderRight = '3px solid rgb(117, 117, 117)'; // White outline
        energyBarContainer.style.borderBottom = '3px solid rgb(117, 117, 117)'; // White outline
        energyBarContainer.style.borderRadius = '0px 0px 3px 3px';

        let energyBar = document.createElement('div');
        energyBar.style.height = '20px';
        energyBar.style.width = `${(CHARACTER.energy / CHARACTER.maxEnergy) * 100}%`;
        energyBar.style.backgroundColor = '#0000a4';

        let energyText = document.createElement('div');
        energyText.textContent = `${CHARACTER.energy}`;
        energyText.style.position = 'absolute';
        energyText.style.top = '50%';
        energyText.style.left = '50%';
        energyText.style.transform = 'translate(-50%, -50%)';
        energyText.style.color = 'white';
        energyText.style.fontSize = '12px';
        energyText.style.fontFamily = 'monospace';

        energyBarContainer.appendChild(energyBar);
        energyBarContainer.appendChild(energyText);

        progressBarsContainer.appendChild(healthBarContainer);
        progressBarsContainer.appendChild(energyBarContainer);

        // Right section for timers
        let timersContainer = document.createElement('div');
        timersContainer.style.textAlign = 'center';
        timersContainer.style.paddingRight = '50px';
        timersContainer.style.width = '256px';

        // Ensure timers are stacked vertically
        timersContainer.style.display = 'flex';
        timersContainer.style.flexDirection = 'column'; // Stack vertically
        timersContainer.style.alignItems = 'center'; // Center align each timer

        // Potion Timer Link
        let potionTimer = document.createElement('a');
        potionTimer.id = 'potionCooldownTimer';
        potionTimer.style.color = 'white';
        potionTimer.style.fontSize = '14px';
        potionTimer.style.fontFamily = 'monospace';
        potionTimer.style.margin = '0px';
        //potionTimer.href = '/profile/index#potions';
        potionTimer.href = '/profile/useItem/2/20';
        potionTimer.textContent = 'Potion Cooldown: Calculating...';

        // Church Timer Link
        let churchTimer = document.createElement('a');
        churchTimer.id = 'churchCooldownTimer';
        churchTimer.style.color = 'white';
        churchTimer.style.fontSize = '14px';
        churchTimer.style.fontFamily = 'monospace';
        churchTimer.style.margin = '0px';
        churchTimer.href = '/city/church';
        churchTimer.textContent = 'Church Cooldown: Calculating...';

        timersContainer.appendChild(potionTimer);
        timersContainer.appendChild(churchTimer);

        // Add both sections to the main container
        mainContainer.appendChild(progressBarsContainer);
        mainContainer.appendChild(timersContainer);

        // Append the main container to the desired parent element
        document.getElementsByClassName("gold")[0].appendChild(mainContainer);

        // Start updating the timers
        updatePotionCooldownDisplay();
        setInterval(updatePotionCooldownDisplay, 1000);
        updateChurchCooldownDisplay();
        setInterval(updateChurchCooldownDisplay, 1000);
    }

    function calculateCurrentHealth(){
        const regenPerSecond = CHARACTER.regenHealth / 3600; // Convert regenHealth from per hour to per second
        // Calculate the total health regenerated since the page loaded
        const elapsedSeconds = (Date.now() - pageLoadTime) / 1000; // Time elapsed in seconds
        const regeneratedHealth = regenPerSecond * elapsedSeconds;
        // Calculate the updated health, without modifying the original CHARACTER.health
        const updatedHealth = Math.min(
            CHARACTER.health + regeneratedHealth,
            CHARACTER.maxHealth
        );
        return updatedHealth;
    }

    // Start real-time health regeneration
    function startHealthRegeneration() {
        const regenInterval = 200; // Update every 200 ms
        setInterval(() => {
            // Update the progress bar with the calculated health
            updateProgressBars(calculateCurrentHealth());
        }, regenInterval);
    }

    // Update the existing progress bars
    function updateProgressBars(calculatedHealth) {
        // Update Energy Progress Bar
        //const energyBar = document.getElementById('energyProgressBar').children[0];
        //energyBar.style.width = `${(CHARACTER.energy / CHARACTER.maxEnergy) * 100}%`;
        //const energyText = document.getElementById('energyProgressBar').children[1];
        //energyText.textContent = `${CHARACTER.energy}`;

        // Update Health Progress Bar
        const healthBar = document.getElementById('healthProgressBar').children[0];
        healthBar.style.width = `${(calculatedHealth / CHARACTER.maxHealth) * 100}%`;

        const healthText = document.getElementById('healthProgressBar').children[1];
        // Format the health value with thousands separators
        let healthWithoutDecimals = Math.floor(calculatedHealth);
        healthText.textContent = `${healthWithoutDecimals > 999 ? healthWithoutDecimals.toString().replace(/\B(?=(\d{3})+(?!\d))/g, '.'): healthWithoutDecimals}`;
    }

    // Function to update the timer display
    function updatePotionCooldownDisplay() {
        const timerElement = document.getElementById('potionCooldownTimer');
        const currentTime = new Date().getTime() / 1000; // Current time in seconds

        if (CHARACTER.potionCooldownEnd > currentTime) {
            const remainingTime = CHARACTER.potionCooldownEnd - currentTime; // Remaining time in seconds
            const hours = Math.floor(remainingTime / 3600);
            const minutes = Math.floor((remainingTime % 3600) / 60);
            const seconds = Math.round(remainingTime % 60);
            timerElement.textContent = `Potion : ${hours.toString().padStart(2, '0')}:${minutes.toString().padStart(2, '0')}:${seconds.toString().padStart(2, '0')}`;
        } else {
            timerElement.textContent = 'Potion Ready!';
        }
    }

    // Add the church cooldown timer display
    function updateChurchCooldownTimer() {
        const churchCountdownElement = document.getElementsByClassName('hasCountdown')[0];
        if (churchCountdownElement) {
            const cooldownTime = churchCountdownElement.textContent.trim();
            const currentTime = new Date().getTime() / 1000; // Current time in seconds
            const cooldownSeconds = timeToSeconds(cooldownTime); // Convert cooldown time to seconds
            const endTime = currentTime + cooldownSeconds; // Calculate the end time

            // Save the end time to the character object
            CHARACTER.churchCooldownEnd = endTime;
            updateCharacter(); // Save updated character data
        }
    }

    // Function to update the church cooldown display
    function updateChurchCooldownDisplay() {
        const timerElement = document.getElementById('churchCooldownTimer');
        const currentTime = new Date().getTime() / 1000; // Current time in seconds

        if (CHARACTER.churchCooldownEnd > currentTime) {
            const remainingTime = CHARACTER.churchCooldownEnd - currentTime; // Remaining time in seconds
            const hours = Math.floor(remainingTime / 3600);
            const minutes = Math.floor((remainingTime % 3600) / 60);
            const seconds = Math.round(remainingTime % 60);
            timerElement.textContent = `Church : ${hours.toString().padStart(2, '0')}:${minutes.toString().padStart(2, '0')}:${seconds.toString().padStart(2, '0')}`;
        } else {
            timerElement.textContent = 'Church Ready!';
        }
    }

    function addAdditionnalLink() {
        // make the overview link open the attributes by default
        document.querySelectorAll('#menuHead li a')[1].href="/profile/index#tabs-2";
        // make the voodoo shop less flashy
        document.getElementById("premium").removeAttribute("id");
        //translation of all the menu in english, so everything is the same
        document.querySelectorAll('#menuHead li a')[0].textContent="News";
        document.querySelectorAll('#menuHead li a')[1].textContent="Overview";
        document.querySelectorAll('#menuHead li a')[2].textContent="Messages";
        document.querySelectorAll('#menuHead li a')[3].textContent="Hideout";
        document.querySelectorAll('#menuHead li a')[4].textContent="City";
        document.querySelectorAll('#menuHead li a')[5].textContent="Hunt";
        document.querySelectorAll('#menuHead li a')[6].textContent="Voodoo Shop";
        document.querySelectorAll('#menuHead li a')[7].textContent="Clan";
        document.querySelectorAll('#menuHead li a')[8].textContent="Buddy list";
        document.querySelectorAll('#menuHead li a')[9].textContent="Notepad";
        document.querySelectorAll('#menuHead li a')[10].textContent="Settings";
        document.querySelectorAll('#menuHead li a')[11].textContent="Forum";
        document.querySelectorAll('#menuHead li a')[12].textContent="Highscore";
        document.querySelectorAll('#menuHead li a')[13].textContent="Search";
        document.querySelectorAll('#menuHead li a')[14].textContent="Support";
        document.querySelectorAll('#menuHead li a')[15].textContent="Leave game";

        // Find the <li> containing the Chasse link
        const chasseListItem = document.querySelector('li.free-space > a[href$="/robbery/index"]');

        if (chasseListItem) {
            // Navigate to the parent <li> element
            const chasseLi = chasseListItem.closest('li');

            // Remove the class "free-space"
            chasseLi.removeAttribute('class');
            if (window.location.pathname.contains('robbery')){
                chasseLi.className = "active";
            }


            // Create a new <li> for the Grotte link
            const grotteLi = document.createElement('li');
            if (window.location.pathname.contains('/grotte')){
                if (document.getElementsByClassName("active")[0])document.getElementsByClassName("active")[0].removeAttribute('class');
                grotteLi.className = "active";
            }
            grotteLi.innerHTML = '<a href="/city/grotte" target="_top">Grotto</a>';


            const graveLi = document.createElement('li');
            if (window.location.pathname.contains('/city/graveyard') || window.location.pathname.contains('/user/working')){
                if (document.getElementsByClassName("active")[0])document.getElementsByClassName("active")[0].removeAttribute('class');
                graveLi.className = "active";
            }
            graveLi.innerHTML = '<a href="/city/graveyard" target="_top">Graveyard</a>';

            const shopLi = document.createElement('li');
            if (window.location.pathname.contains('/city/shop/')){
                if (document.getElementsByClassName("active")[0])document.getElementsByClassName("active")[0].removeAttribute('class');
                shopLi.className = "active";
            }
            shopLi.innerHTML = '<a href="/city/shop/potions/&page=1&premiumfilter=nonpremium" target="_top">Merchant</a>';

            const questsLi = document.createElement('li');
            questsLi.className = "free-space";
            if (window.location.pathname.contains('/city/missions')){
                if (document.getElementsByClassName("active")[0])document.getElementsByClassName("active")[0].removeAttribute('class');
                questsLi.className = "active free-space";
            }
            questsLi.innerHTML = '<a href="/city/missions" target="_top">Quests</a>';

            // Insert the new links (in reverse)
            chasseLi.insertAdjacentElement('afterend', questsLi);
            chasseLi.insertAdjacentElement('afterend', shopLi);
            chasseLi.insertAdjacentElement('afterend', graveLi);
            chasseLi.insertAdjacentElement('afterend', grotteLi);
        }
    }

    function moveGameEventDiv() {
        const gameEventDiv = document.getElementById('gameEvent');
        const itemsDiv = document.getElementById('items');

        if (gameEventDiv && itemsDiv) {
            itemsDiv.insertAdjacentElement('afterend', gameEventDiv);
        }
        //scroll up after upgrading a skill
        if (window.location.hash=='#tabs-2') window.scrollTo(0, 0);
        //if (window.location.hash=='#potions') document.getElementsByClassName("ui-accordion-header")[2].scrollIntoView({ behavior: 'smooth' });
    }

    function defaultNonPremiumShop() {

        const links = document.querySelectorAll('a'); // Select all anchor elements
        links.forEach(link => {
            // Check if the link href contains '/city/shop/'
            if (link.href.includes('/city/shop')) {
                // If the URL doesn't already have a query string, add it
                if (!link.href.includes('&premiumfilter=nonpremium')) {
                    link.href += '&premiumfilter=nonpremium';
                }
            }
        });

        /*
        var premiumfilter = document.querySelector('select[name="premiumfilter"]'); // Replace with the correct selector if necessary
        if (premiumfilter) {
            premiumfilter.value = 'nonpremium'; // Set default value to 'nonpremium'
        }*/
    }

    function redirectAfterGrotteFight() {
        if ((CHARACTER.autoRedirectGrotte || CHARACTER.autoGrotto[0] || CHARACTER.autoGrotto[1] || CHARACTER.autoGrotto[2]) && window.location.href.includes('report/fightreport/') && window.location.href.includes('/grotte')) {
            // Redirect to '/city/grotte'
            for (let index = 0; index < 3; index++) {
                if(CHARACTER.autoGrotto[index])saveGrottoStats(index);
            }
            window.location.href = '/city/grotte';
        }


        // all below is to add the checkbox
        if (!window.location.pathname.contains('/city/grotte')) return;
        const buildingDescElement = document.getElementsByClassName('buildingDesc')[0];
        if (!buildingDescElement) return;
        // Create the switch container
        const switchContainer = document.createElement('div');
        switchContainer.classList.add('auto-redirect-switch');
        switchContainer.style.marginTop = '10px';

        // Create the label for the switch
        const label = document.createElement('label');
        label.textContent = 'Auto redirect after fight';
        label.style.marginRight = '10px';

        // Create the switch itself
        const switchInput = document.createElement('input');
        switchInput.type = 'checkbox';
        switchInput.style.transform = 'scale(1.2)'; // Optional: make it slightly bigger
        switchInput.style.display = 'block';
        switchInput.checked = CHARACTER.autoRedirectGrotte; // Get the saved state

        // Add change event to store the state of the switch
        switchInput.addEventListener('change', () => {
            CHARACTER.autoRedirectGrotte = switchInput.checked;
            updateCharacter();
            // Implement logic for auto redirection here if needed
        });

        // Append the label and input (switch) to the container
        switchContainer.appendChild(label);
        switchContainer.appendChild(switchInput);

        // Append the switch to the buildingDesc element
        buildingDescElement.appendChild(switchContainer);
    }

    function addAutoGrottoButton() {

        // make sure the variable is defined
        if (!Array.isArray(CHARACTER.autoGrotto)) {
            CHARACTER.autoGrotto = [false, false, false];
        }
        if (!CHARACTER.goldEarned) {
            CHARACTER.goldEarned = {};
        }
        if (!CHARACTER.dmgTaken) {
            CHARACTER.dmgTaken = {};
        }
        if (!CHARACTER.xpEarned) {
            CHARACTER.xpEarned = {};
        }

        for (let index = 0; index < 3; index++) {
            //const target = $(`table.noBackground form.clearfix div input`)[index];
            const target = document.querySelectorAll('table.noBackground form.clearfix div input')[index];
            if (!target) return;

            // Create the button
            const button = document.createElement("button");
            if(CHARACTER.autoGrotto[index]){
                button.textContent = "Auto : ON";
                button.style.backgroundPosition = '0 -183px';
            } else {
                button.textContent = "Auto : OFF";
                //button.style.backgroundPosition = '0 -44px';
            }
            button.className = "btn-small left btn-autoGrotto";
            button.style.margin = "10px";
            button.style.padding = "0 0 5px";

            // Add a click event listener
            button.addEventListener("click", function (event) {
                event.preventDefault(); // Prevent default button behavior
                if(!CHARACTER.autoGrotto[index] && isReadyForGrotto()){
                    CHARACTER.autoGrotto[index] = true;
                    updateCharacter();
                    button.textContent = "Auto : ON";
                    button.style.backgroundPosition = '0 -183px';
                    document.querySelectorAll('table.noBackground form.clearfix div input')[index].click();
                } else {
                    CHARACTER.autoGrotto[index] = false;
                    updateCharacter();
                    button.textContent = "Auto : OFF";
                    button.style.backgroundPosition = '0 -44px';
                }
                //console.log(`CHARACTER.autoGrotto[${index}] is now `, CHARACTER.autoGrotto);
            });
            // Insert the button after the target element
            target.parentNode.insertBefore(button, target.nextSibling);
            displayStatsAverage(index);
        }
    }

    function autoFightGrotto() {
        if(!CHARACTER.autoGrotto[0] && !CHARACTER.autoGrotto[1] && !CHARACTER.autoGrotto[2]) return;
        const randomDelay = Math.random() * 1000 + 200;

        console.log(`Action will be executed after ${randomDelay.toFixed(0)}ms`);

        // Set the timeout
        setTimeout(() => {
            if(!isReadyForGrotto()){
                CHARACTER.autoGrotto[0] = false;
                CHARACTER.autoGrotto[1] = false;
                CHARACTER.autoGrotto[2] = false;
                updateCharacter();
                const buttons = document.querySelectorAll('.btn-autoGrotto');
                buttons.forEach(button => {
                    button.textContent = 'Auto : OFF';
                    button.style.backgroundPosition = '0 -44px';
                });
            } else {
                if(CHARACTER.autoGrotto[0])document.querySelectorAll('table.noBackground form.clearfix div input')[0].click();
                if(CHARACTER.autoGrotto[1])document.querySelectorAll('table.noBackground form.clearfix div input')[1].click();
                if(CHARACTER.autoGrotto[2])document.querySelectorAll('table.noBackground form.clearfix div input')[2].click();
            }
        }, randomDelay);
    }

    function isReadyForGrotto() {
        return (calculateCurrentHealth() > 9000 && CHARACTER.energy > 0);
    }

    function saveGrottoStats(grottoDifficulty){
        const goldEarned = parseInt(formatNumber(document.getElementsByClassName("gold")[1].firstChild.textContent.split("\n")[1]));
        const xpEarned = parseInt(document.getElementsByClassName("gold")[1].lastChild.textContent.replace(/\D/g, ''));

        // Ensure the goldEarned array for the selected difficulty exists
        if (!CHARACTER.goldEarned[grottoDifficulty]) {
            CHARACTER.goldEarned[grottoDifficulty] = [];
        }

        // Push the new gold earned value into the array
        CHARACTER.goldEarned[grottoDifficulty].push(goldEarned);

        // Keep only the last 20 entries for each difficulty
        if (CHARACTER.goldEarned[grottoDifficulty].length > 20) {
            CHARACTER.goldEarned[grottoDifficulty].shift(); // Remove the oldest entry
        }

        // Ensure the dmgTaken array for the selected difficulty exists
        if (!CHARACTER.dmgTaken[grottoDifficulty]) {
            CHARACTER.dmgTaken[grottoDifficulty] = [];
        }

        // Push the new dmg taken value into the array
        CHARACTER.dmgTaken[grottoDifficulty].push(getGrottoHealthDamage(goldEarned));

        // Keep only the last 20 entries for each difficulty
        if (CHARACTER.dmgTaken[grottoDifficulty].length > 20) {
            CHARACTER.dmgTaken[grottoDifficulty].shift(); // Remove the oldest entry
        }

        // Ensure the xpEarned array for the selected difficulty exists
        if (!CHARACTER.xpEarned[grottoDifficulty]) {
            CHARACTER.xpEarned[grottoDifficulty] = [];
        }

        // Push the new dmg taken value into the array
        CHARACTER.xpEarned[grottoDifficulty].push(xpEarned);

        // Keep only the last 20 entries for each difficulty
        if (CHARACTER.xpEarned[grottoDifficulty].length > 20) {
            CHARACTER.xpEarned[grottoDifficulty].shift(); // Remove the oldest entry
        }

        // Update the character data with the new gold earnings
        updateCharacter();
    }

    // This function calculates the average gold for a given difficulty
    function calculateGoldAverage(grottoDifficulty) {
        if (!CHARACTER.goldEarned[grottoDifficulty] || CHARACTER.goldEarned[grottoDifficulty].length < 1) return 0;

        let totalGold = 0;
        // Loop through the gold history for the given difficulty
        for (let i = 0; i < CHARACTER.goldEarned[grottoDifficulty].length; i++) {
            totalGold += CHARACTER.goldEarned[grottoDifficulty][i];
        }
        return totalGold / CHARACTER.goldEarned[grottoDifficulty].length;
    }

    function calculateDamageAverage(grottoDifficulty) {
        if (!CHARACTER.dmgTaken[grottoDifficulty] || CHARACTER.dmgTaken[grottoDifficulty].length < 1) return 0;

        let totalDmg = 0;
        // Loop through the gold history for the given difficulty
        for (let i = 0; i < CHARACTER.dmgTaken[grottoDifficulty].length; i++) {
            totalDmg += CHARACTER.dmgTaken[grottoDifficulty][i];
        }
        return totalDmg / CHARACTER.dmgTaken[grottoDifficulty].length;
    }

    function calculateXPAverage(grottoDifficulty) {
        if (!CHARACTER.xpEarned[grottoDifficulty] || CHARACTER.xpEarned[grottoDifficulty].length < 1) return 0;

        let totalGold = 0;
        // Loop through the gold history for the given difficulty
        for (let i = 0; i < CHARACTER.xpEarned[grottoDifficulty].length; i++) {
            totalGold += CHARACTER.xpEarned[grottoDifficulty][i];
        }
        return totalGold / CHARACTER.xpEarned[grottoDifficulty].length;
    }

    // Function to display the average gold under each button
    function displayStatsAverage(grottoDifficulty) {
        //console.log("Average gold for difficult "+grottoDifficulty+" : "+calculateGoldAverage(grottoDifficulty));
        const button = document.querySelectorAll('.btn-autoGrotto')[grottoDifficulty];
        if (!button) return;
        let avgGoldText = `${calculateGoldAverage(grottoDifficulty).toFixed(0).replace(/\B(?=(\d{3})+(?!\d))/g, '.')} `;
        let avgDmg = `${calculateDamageAverage(grottoDifficulty).toFixed(0).replace(/\B(?=(\d{3})+(?!\d))/g, '.')} `;
        let avgXP = `${calculateXPAverage(grottoDifficulty).toFixed(0).replace(/\B(?=(\d{3})+(?!\d))/g, '.')} `;

        // Check if the display already exists, and update or create a new one
        let avgElement = button.querySelector('.gold-average');
        avgElement = document.createElement('div');
        avgElement.classList.add('gold-average');
        avgElement.style.fontSize = '15px';
        avgElement.style.marginLeft = '10px';
        avgElement.style.textAlign = 'center'; // Center the text
        avgElement.style.display = 'block'; // Make sure it takes up the full width and goes below the button
        avgElement.style.width = '160px';
        avgElement.style.textShadow = "0 0 4px #FF0000";
        avgElement.style.fontFamily = 'monospace';
        avgElement.style.fontWeight = 'bold';
        avgElement.style.color = 'white';
        avgElement.style.lineHeight = '25px';
        button.parentNode.insertBefore(avgElement, button.nextSibling);
        avgElement.textContent = avgGoldText;
        // Create an image element and append it after the text
        let imgGold = document.createElement('img');
        imgGold.src = '/img/symbols/res2.gif';
        avgElement.appendChild(imgGold);
        imgGold.align = 'absmiddle';
        // health lost
        var lineBreak = document.createElement('br');
        avgElement.appendChild(lineBreak);
        var avgDmgText = document.createTextNode(avgDmg);
        avgElement.appendChild(avgDmgText);
        let imgHealth = document.createElement('img');
        imgHealth.src = '/img/symbols/herz.png';
        avgElement.appendChild(imgHealth);
        imgHealth.align = 'absmiddle';
        // xp earned
        var lineBreak2 = document.createElement('br');
        avgElement.appendChild(lineBreak2);
        var avgXPText = document.createTextNode(avgXP);
        avgElement.appendChild(avgXPText);
        let imgXP = document.createElement('img');
        imgXP.src = '/img/symbols/level.gif';
        avgElement.appendChild(imgXP);
        imgXP.align = 'absmiddle';
    }

    // if gold earned = 0, then it's lost,and the dmg taken is on left and not on right
    function getGrottoHealthDamage(goldEarned){
        var win = 1;
        if(goldEarned == 0){
            win = 0;
        }
        const wholeText = document.querySelectorAll('#reportResult div.wrap-left div.wrap-content p')[0].textContent;
        const extractedText = wholeText.match(/\(([^)]+)\)/)[1]; // Regular expression to match content between parentheses
        const numbers = extractedText.split(":");
        return(parseInt(numbers[win]));
    }

})();