NOTICE: By continued use of this site you understand and agree to the binding Terms of Service and Privacy Policy.
// ==UserScript== // @name Force Prime Heroes Combat Simulator // @version 2.1 // @description Fight projections for Force Prime Heroes // @license MIT // @namespace https://github.com/djizus // @author djizus // @icon https://forceprime.io/favicon.ico // @match https://forceprime.io/* // @grant none // ==/UserScript== (function() { 'use strict'; // Initial roster data let roster = [ {"Unit": "Centaur", "Number": 0, "ID": 268}, {"Unit": "Dwarf", "Number": 0, "ID": 273}, {"Unit": "Crusader", "Number": 0, "ID": 269}, {"Unit": "Monk", "Number": 0, "ID": 280}, {"Unit": "Angel", "Number": 0, "ID": 266} ]; let heroPower = [ {"Stat": "Attack", "Number": 0}, {"Stat": "Defense", "Number": 0} ]; let unitData = [ {"Unit": "Centaur", "Type": "Melee", "Atk": 3, "HP": 12, "Power": 6, "Cost": 150, "Weekly spawn": 16}, {"Unit": "Dwarf", "Type": "Ranged", "Atk": 5, "HP": 20, "Power": 10, "Cost": 250, "Weekly spawn": 12}, {"Unit": "Crusader", "Type": "Melee", "Atk": 4, "HP": 36, "Power": 12, "Cost": 350, "Weekly spawn": 12}, {"Unit": "Monk", "Type": "Ranged", "Atk": 12, "HP": 27, "Power": 18, "Cost": 500, "Weekly spawn": 8}, {"Unit": "Angel", "Type": "Melee", "Atk": 15, "HP": 60, "Power": 30, "Cost": 800, "Weekly spawn": 5}, {"Unit": "Young Ent", "Type": "Melee", "Atk": 2, "HP": 8, "Power": 4}, {"Unit": "Skeleton Mage", "Type": "Ranged", "Atk": 2, "HP": 8, "Power": 4}, {"Unit": "Fire Elemental", "Type": "Melee", "Atk": 3, "HP": 12, "Power": 6}, {"Unit": "Zombie", "Type": "Melee", "Atk": 5, "HP": 20, "Power": 10}, {"Unit": "Ent", "Type": "Melee", "Atk": 4, "HP": 25, "Power": 10}, {"Unit": "Genie", "Type": "Ranged", "Atk": 5, "HP": 20, "Power": 10}, {"Unit": "Vampire", "Type": "Melee", "Atk": 6, "HP": 24, "Power": 12}, {"Unit": "Cyclops", "Type": "Ranged", "Atk": 10, "HP": 40, "Power": 20}, {"Unit": "Minotaur", "Type": "Melee", "Atk": 12, "HP": 48, "Power": 24}, {"Unit": "Necromancer", "Type": "Melee", "Atk": 16, "HP": 36, "Power": 24}, {"Unit": "Fire Witch", "Type": "Ranged", "Atk": 16, "HP": 36, "Power": 24}, {"Unit": "Fairy Dragon", "Type": "Melee", "Atk": 20, "HP": 80, "Power": 40}, {"Unit": "Titan", "Type": "Ranged", "Atk": 20, "HP": 80, "Power": 40}, {"Unit": "Death", "Type": "Melee", "Atk": 25, "HP": 100, "Power": 50}, {"Unit": "Devil", "Type": "Melee", "Atk": 50, "HP": 200, "Power": 100}, {"Unit": "Bone Dragon", "Type": "Melee", "Atk": 250, "HP": 1000, "Power": 500} ]; // Function to check if the current URL matches the target function isPlayPage() { return window.location.href.startsWith("https://forceprime.io/play"); } // Function to parse URL parameters function parseUrlParams(url) { let params = {}; let queryString = url.split('?')[1]; if (queryString) { queryString.split('&').forEach(item => { let pair = item.split('='); params[pair[0]] = decodeURIComponent(pair[1] || ''); }); } return params; } // Function to calculate roster atk function calculateRosterAtk(roster, heroPower, unitData) { let rosterAtk = 0; const heroAtk = heroPower.find(stat => stat.Stat === "Attack").Number; roster.forEach(unit => { const unitInfo = unitData.find(info => info.Unit === unit.Unit); if (unitInfo.Type === "Ranged") { rosterAtk += Math.floor(unitInfo.Atk * unit.Number * (1 + heroAtk * 8 / 100)); console.log(rosterAtk); } }); return rosterAtk; } // Function to calculate roster power function calculateRosterPower(roster, heroPower, unitData) { let rosterPower = 0; const heroAtk = heroPower.find(stat => stat.Stat === "Attack").Number; const heroDef = heroPower.find(stat => stat.Stat === "Defense").Number; roster.forEach(unit => { const unitInfo = unitData.find(info => info.Unit === unit.Unit); if (unitInfo) { rosterPower += Math.floor(Math.sqrt( unitInfo.Atk * unit.Number * (1 + heroAtk * 8 / 100) * unitInfo.HP * unit.Number * (1 + heroDef / 10) )); } }); return rosterPower; } // Function to calculate fight function calculateFight(enemyUnit, enemyPower, roster, heroPower, unitData) { let damageDealt = 0; let splitStacksNumber = 0; const heroAtk = heroPower.find(stat => stat.Stat === "Attack").Number; const heroDef = heroPower.find(stat => stat.Stat === "Defense").Number; const enemy = unitData.find(unit => unit.Unit === unitData[enemyUnit].Unit); const enemyNumber = enemyPower / enemy.Power; roster.forEach(unit => { const unitInfo = unitData.find(info => info.Unit === unit.Unit); if (unitInfo && unit.Number > 0) { splitStacksNumber += 1; if (unitInfo.Type === "Ranged") { damageDealt += unitInfo.Atk * unit.Number * (1 + heroAtk / 10); } } }); let enemyLeft = Math.ceil(100 * (enemyNumber * enemy.HP - damageDealt) / enemy.HP); enemyLeft = enemyLeft / 100; let enemyPowerLeft = enemyLeft * enemy.Power; let damageTaken = 0; if (enemy.Type === "Ranged") { damageTaken += enemyNumber * enemy.Atk; let damageTakenByStack = damageTaken / splitStacksNumber; roster.forEach(unit => { const unitInfo = unitData.find(info => info.Unit === unit.Unit); if (unitInfo && unit.Number > 0) { let unitHp = unitInfo.HP * (1 + heroDef / 10); unit.Number = Math.ceil((unit.Number * unitHp - damageTakenByStack) / unitHp); if (unit.Number < 0) { unit.Number = 0; } } }); } let powerLeft = calculateRosterPower(roster, heroPower, unitData); if (enemyPowerLeft < 0) enemyPowerLeft = 0; let casualtiesFirstStep = Math.ceil(100 * enemyPowerLeft / powerLeft)** 2; let casualtiesPercentage = casualtiesFirstStep/(100**2); roster.forEach(unit => { if (unit.Number > 0) { let casualtiesToApply = Math.floor(unit.Number * casualtiesPercentage); unit.Number -= casualtiesToApply; if (unit.Number < 0) unit.Number = 0; } }); powerLeft = calculateRosterPower(roster, heroPower, unitData); let resultString = `<strong>Power Left:</strong> ${powerLeft}<br>`; resultString += '<strong>Units Left:</strong><br>'; resultString += roster.map(unit => `${unit.Unit}: ${unit.Number}`).join('<br>'); return resultString; } // Function to refresh roster power and attack stat function refreshCurrentPowerAtk(roster, heroPower, unitData) { let power = calculateRosterPower(roster, heroPower, unitData); let atk = calculateRosterAtk(roster, heroPower, unitData); let resultString = `<strong>Current Power:</strong> ${power}<br><strong>Current Attack:</strong> ${atk}<br>`; return resultString; } async function getCurrentData(container, roster, heroPower, unitData) { // Find the iframe element by its id or class let iframe = document.getElementById("game_iframe_bg"); if (iframe) { // Get the src attribute value let src = iframe.getAttribute('src'); // Parse the URL parameters let params = parseUrlParams(src); // Extract individual variables let private_key = params['private_key']; let public_key = params['public_key']; let account = params['account']; let access_token = params['access_token']; let sid = params['sid']; let uid = params['uid']; let world_id = params['world_id']; let server_url = params['server']; let torii = params['torii']; let contract = params['contract']; console.log(src); // Get game ID const gameResponse = await fetch(`https://forceprime.io/v2/dojo/player_game?player_id=${account}&world_id=${world_id}`, { method: "GET", headers: { "priority": "u=1, i", } }); const gameData = await gameResponse.json(); const gameId = gameData["game_id"]; // Get hero stats const heroStatsResponse = await fetch(`https://forceprime.io/v2/dojo/player_stats?game_id=${gameId}&world_id=${world_id}`, { method: "GET", headers: { "priority": "u=1, i", } }); const heroStats = await heroStatsResponse.json(); // Update our data heroPower.find(stat => stat.Stat === "Attack").Number = heroStats.stats.attack; heroPower.find(stat => stat.Stat === "Defense").Number = heroStats.stats.defence; // Get roster data const rosterDataResponse = await fetch(`https://forceprime.io/v2/dojo/player_units?game_id=${gameId}&world_id=${world_id}`, { method: "GET", headers: { "priority": "u=1, i", } }); const rosterData = await rosterDataResponse.json(); // Update our data roster.forEach(unit => {unit.Number = 0}); if (rosterData.units.unit_1_id != 0) { roster.find(unit => unit.ID === rosterData.units.unit_1_id).Number = rosterData.units.unit_1_count; } if (rosterData.units.unit_2_id != 0) { roster.find(unit => unit.ID === rosterData.units.unit_2_id).Number = rosterData.units.unit_2_count; } if (rosterData.units.unit_3_id != 0) { roster.find(unit => unit.ID === rosterData.units.unit_3_id).Number = rosterData.units.unit_3_count; } if (rosterData.units.unit_4_id != 0) { roster.find(unit => unit.ID === rosterData.units.unit_4_id).Number = rosterData.units.unit_4_count; } if (rosterData.units.unit_5_id != 0) { roster.find(unit => unit.ID === rosterData.units.unit_5_id).Number = rosterData.units.unit_5_count; } document.getElementById("powerAtkDiv").innerHTML = refreshCurrentPowerAtk(roster, heroPower, unitData); document.getElementById("input_Attack").value = heroPower.find(stat => stat.Stat === "Attack").Number; document.getElementById("input_Defense").value = heroPower.find(stat => stat.Stat === "Defense").Number; document.getElementById("input_Centaur").value = roster.find(unit => unit.Unit === "Centaur").Number; document.getElementById("input_Dwarf").value = roster.find(unit => unit.Unit === "Dwarf").Number; document.getElementById("input_Crusader").value = roster.find(unit => unit.Unit === "Crusader").Number; document.getElementById("input_Monk").value = roster.find(unit => unit.Unit === "Monk").Number; document.getElementById("input_Angel").value = roster.find(unit => unit.Unit === "Angel").Number; return "OK"; } return "KO"; } // Create UI container const container = document.createElement('div'); container.id = "forceprimehelper"; container.style.padding = '1%'; container.style.backgroundColor = 'black'; container.style.border = '1px solid white'; container.style.position = 'fixed'; container.style.top = '15%'; container.style.right = '2%'; container.style.width = '18%'; container.style.height = '75%'; container.style.overflowY = 'auto'; container.style.zIndex = '1000'; container.style.visibility = 'hidden'; // Create hero data div const refreshDataText = document.createElement('div'); refreshDataText.id = 'powerAtkDiv' refreshDataText.style.color = 'white'; refreshDataText.style.marginTop = '1%'; refreshDataText.innerHTML = '<strong>Current Power:</strong> 0<br><strong>Current Attack:</strong> 0<br>'; // Create refresh data button const refreshDataButton = document.createElement('button'); refreshDataButton.textContent = 'Get Current Data'; refreshDataButton.addEventListener('click', () => { getCurrentData(container, roster, heroPower, unitData); }); // Create player stats display const heroPowerDisplay = document.createElement('div'); heroPowerDisplay.innerHTML = '<strong>Hero Power</strong><br>'; heroPower.forEach(stat => { const statContainer = document.createElement('div'); statContainer.style.display = 'flex'; statContainer.style.alignItems = 'center'; statContainer.style.justifyContent = 'space-between'; statContainer.style.marginBottom = '1%'; const statName = document.createElement('span'); statName.textContent = `${stat.Stat} : `; statName.style.color = 'white'; statName.style.flex = '1'; statName.style.textAlign = 'left'; const statNumberInput = document.createElement('input'); statNumberInput.id = `input_${stat.Stat}`; statNumberInput.type = 'number'; statNumberInput.value = stat.Number; statNumberInput.min = 0; statNumberInput.style.width = '50%'; statNumberInput.style.backgroundColor = 'black'; statNumberInput.style.color = 'white'; statNumberInput.style.border = '1px solid white'; statNumberInput.style.textAlign = 'right'; statNumberInput.addEventListener('input', () => { stat.Number = parseInt(statNumberInput.value) || 0; refreshDataText.innerHTML = refreshCurrentPowerAtk(roster, heroPower, unitData); }); statContainer.appendChild(statName); statContainer.appendChild(statNumberInput); heroPowerDisplay.appendChild(statContainer); }); // Create player roster display const rosterDisplay = document.createElement('div'); rosterDisplay.innerHTML = '<strong>Roster</strong><br>'; roster.forEach(unit => { const unitContainer = document.createElement('div'); unitContainer.style.display = 'flex'; unitContainer.style.alignItems = 'center'; unitContainer.style.justifyContent = 'space-between'; unitContainer.style.marginBottom = '1%'; const unitName = document.createElement('span'); unitName.textContent = `${unit.Unit} : `; unitName.style.color = 'white'; unitName.style.flex = '1'; unitName.style.textAlign = 'left'; const unitNumberInput = document.createElement('input'); unitNumberInput.id = `input_${unit.Unit}`; unitNumberInput.type = 'number'; unitNumberInput.value = unit.Number; unitNumberInput.min = 0; unitNumberInput.style.width = '50%'; unitNumberInput.style.backgroundColor = 'black'; unitNumberInput.style.color = 'white'; unitNumberInput.style.border = '1px solid white'; unitNumberInput.style.textAlign = 'right'; unitNumberInput.addEventListener('input', () => { unit.Number = parseInt(unitNumberInput.value) || 0; refreshDataText.innerHTML = refreshCurrentPowerAtk(roster, heroPower, unitData); }); unitContainer.appendChild(unitName); unitContainer.appendChild(unitNumberInput); rosterDisplay.appendChild(unitContainer); }); // Create enemy display const enemyContainer = document.createElement('div'); enemyContainer.style.marginBottom = '10px'; enemyContainer.innerHTML = '<strong>Enemy Pick</strong><br>'; const enemySelectContainer = document.createElement('div'); enemySelectContainer.style.display = 'flex'; enemySelectContainer.style.alignItems = 'center'; enemySelectContainer.style.justifyContent = 'space-between'; enemySelectContainer.style.marginBottom = '1%'; const enemySelect = document.createElement('select'); enemySelect.style.flex = '1'; enemySelect.style.marginRight = '4%'; enemySelect.style.textAlign = 'left'; unitData.forEach((enemy, index) => { const option = document.createElement('option'); option.value = index; option.textContent = enemy.Unit; enemySelect.appendChild(option); }); const enemyUnitNumberInput = document.createElement('input'); enemyUnitNumberInput.id = 'enemyPower'; enemyUnitNumberInput.type = 'number'; enemyUnitNumberInput.value = 0; enemyUnitNumberInput.min = 0; enemyUnitNumberInput.style.width = '50%'; enemyUnitNumberInput.style.backgroundColor = 'black'; enemyUnitNumberInput.style.color = 'white'; enemyUnitNumberInput.style.border = '1px solid white'; enemyUnitNumberInput.style.textAlign = 'right'; enemySelectContainer.appendChild(enemySelect); enemySelectContainer.appendChild(enemyUnitNumberInput); enemyContainer.appendChild(enemySelectContainer); const fightButton = document.createElement('button'); fightButton.textContent = 'Calculate Fight'; const combatResultText = document.createElement('div'); combatResultText.style.color = 'white'; combatResultText.style.marginTop = '1%'; // Create fight button fightButton.addEventListener('click', () => { let enemyIndex = enemySelect.value; let enemyPower = enemyUnitNumberInput.value; let tmpRoster = JSON.parse(JSON.stringify(roster)); combatResultText.innerHTML = calculateFight(enemyIndex, enemyPower, tmpRoster, heroPower, unitData); }); // Load to the container container.appendChild(refreshDataText); container.appendChild(refreshDataButton); container.appendChild(heroPowerDisplay); container.appendChild(rosterDisplay); container.appendChild(enemyContainer); container.appendChild(fightButton); container.appendChild(combatResultText); // Add display to the page document.body.appendChild(container); // Periodically check if we are on the play page const checkInterval = setInterval(function() { if (isPlayPage()) { container.style.visibility = 'visible'; // Show the container } else { container.style.visibility = 'hidden'; // Hide the container } }, 1000); // Check every 1 second })();