Greasy Fork

来自缓存

Greasy Fork is available in English.

Better BiteFight

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

当前为 2024-11-20 提交的版本,查看 最新版本

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Greasemonkey 油猴子Violentmonkey 暴力猴,才能安装此脚本。

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

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

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

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

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

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

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

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

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

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

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

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

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

// ==UserScript==
// @name         Better BiteFight
// @namespace    https://lobby.bitefight.gameforge.com/
// @version      0.6.3
// @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';
    // first, to avoid the page jump as much as possible
    insertCSS();
    addAdditionnalLink();

    // 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
    });

    // 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);
    }



    redirectAfterGrotteFight();
    updatePotionTimer();
    updateChurchCooldownTimer(); // Run the church cooldown update function if on the church page
    updateRegenHealth();
    updateCharacter();
    insertProgressBars(); // Insert progress bars after updating the character
    startHealthRegeneration(); // Start health regeneration on page load
    moveGameEventDiv(); // move down the game event div
    defaultNonPremiumShop();
    //console.log(CHARACTER);


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

    // 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

        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

        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.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);
    }



    // Start real-time health regeneration
    function startHealthRegeneration() {
        const regenInterval = 200; // Update every 200 ms
        const regenPerSecond = CHARACTER.regenHealth / 3600; // Convert regenHealth from per hour to per second

        setInterval(() => {
            // 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
            );

            // Update the progress bar with the calculated health
            updateProgressBars(updatedHealth);
        }, 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 Cooldown: ${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() {
        if (!window.location.pathname.contains('/city/church')) return; // Ensure this only runs on the church page

        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 Cooldown: ${hours.toString().padStart(2, '0')}:${minutes.toString().padStart(2, '0')}:${seconds.toString().padStart(2, '0')}`;
        } else {
            timerElement.textContent = 'Church Ready!';
        }
    }

    function addAdditionnalLink() {
        // 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">Shop</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() {
        if (!window.location.pathname.contains('/profile')) return;
        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() {
        if (!window.location.pathname.contains('/city/shop')) return;

        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 && window.location.href.includes('report/fightreport/') && window.location.href.includes('/grotte')) {
            // Redirect to '/city/grotte'
            window.location.href = '/city/grotte';
        }


        // all below is to add the button
        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);
    }
})();