// ==UserScript== // @name Better BiteFight // @namespace https://lobby.bitefight.gameforge.com/ // @version 0.6.1 // @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 // @downloadURL none // ==/UserScript== (function () { 'use strict'; // 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 }); // 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); } addAdditionnalLink(); updatePotionTimer(); updateChurchCooldownTimer(); // Run the church cooldown update function if on the church page updateRegenHealth(); updateCharacter(); insertCSS(); 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
  • containing the Chasse link const chasseListItem = document.querySelector('li.free-space > a[href$="/robbery/index"]'); if (chasseListItem) { // Navigate to the parent
  • 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
  • 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 = 'Grotte'; 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 = 'Graveyard'; 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 = 'Shop'; 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 = 'Quests'; // 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' }*/ } })();