|
| 1 | +const toggleBtn = document.getElementById("toggleOptionalBtn"); |
| 2 | +const optionalFields = document.getElementById("optionalFields"); |
| 3 | + |
| 4 | +document.getElementById("calculateBtn").addEventListener("click", () => { |
| 5 | + // --- Read helper to parse numeric inputs safely --- |
| 6 | + function num(id, def = 0) { |
| 7 | + const v = parseFloat(document.getElementById(id)?.value); |
| 8 | + return (isNaN(v) ? def : v); |
| 9 | + } |
| 10 | + |
| 11 | + // --- Gather player inputs --- |
| 12 | + const player = { |
| 13 | + weaponType: document.getElementById("playerWeaponType").value, |
| 14 | + power: num("playerPower", NaN), |
| 15 | + physBreak: num("playerPhysBreak", 0), |
| 16 | + physDef: num("playerPhysDef", 0), |
| 17 | + energyBreak: num("playerEnergyBreak", 0), |
| 18 | + energyDef: num("playerEnergyDef", 0), |
| 19 | + extraAerial: num("playerExtraAerial", 0), |
| 20 | + reduceAerial: num("playerReduceAerial", 0), |
| 21 | + extraPlayer: num("playerExtraPlayer", 0), |
| 22 | + reducePlayer: num("playerReducePlayer", 0), |
| 23 | + |
| 24 | + // Optional weapon-specific attacks |
| 25 | + MGAttack: num("playerMGAttack", 0), |
| 26 | + WGAttack: num("playerWGAttack", 0), |
| 27 | + MSLAttack: num("playerMSLAttack", 0), |
| 28 | + }; |
| 29 | + |
| 30 | + // --- Gather enemy inputs --- |
| 31 | + const enemy = { |
| 32 | + weaponType: document.getElementById("enemyWeaponType").value, |
| 33 | + power: num("enemyPower", NaN), |
| 34 | + physBreak: num("enemyPhysBreak", 0), |
| 35 | + physDef: num("enemyPhysDef", 0), |
| 36 | + energyBreak: num("enemyEnergyBreak", 0), |
| 37 | + energyDef: num("enemyEnergyDef", 0), |
| 38 | + extraAerial: num("enemyExtraAerial", 0), |
| 39 | + reduceAerial: num("enemyReduceAerial", 0), |
| 40 | + extraPlayer: num("enemyExtraPlayer", 0), |
| 41 | + reducePlayer: num("enemyReducePlayer", 0), |
| 42 | + |
| 43 | + // Optional weapon-specific attacks |
| 44 | + MGAttack: num("enemyMGAttack", 0), |
| 45 | + WGAttack: num("enemyWGAttack", 0), |
| 46 | + MSLAttack: num("enemyMSLAttack", 0), |
| 47 | + }; |
| 48 | + |
| 49 | + // Basic validation |
| 50 | + if (!isFinite(player.power) || player.power <= 0 || !isFinite(enemy.power) || enemy.power <= 0) { |
| 51 | + alert("Please enter valid positive Power values for both Player and Enemy."); |
| 52 | + return; |
| 53 | + } |
| 54 | + |
| 55 | + // Calculation function |
| 56 | + function effectivePower(attacker, defender) { |
| 57 | + const breakStat = (attacker.weaponType === "physical") ? attacker.physBreak : attacker.energyBreak; |
| 58 | + const defenseStat = (attacker.weaponType === "physical") ? defender.physDef : defender.energyDef; |
| 59 | + |
| 60 | + const rawFactor = 1 + ((breakStat - defenseStat) * 0.001); |
| 61 | + const clampedFactor = Math.max(0.5, Math.min(1.5, rawFactor)); |
| 62 | + |
| 63 | + const aerialNet = attacker.extraAerial - defender.reduceAerial; |
| 64 | + const playerNet = attacker.extraPlayer - defender.reducePlayer; |
| 65 | + |
| 66 | + const aerialFactor = 1 + (aerialNet / 100); |
| 67 | + const playerFactor = 1 + (playerNet / 100); |
| 68 | + |
| 69 | + const safeAerial = Math.max(0, aerialFactor); |
| 70 | + const safePlayer = Math.max(0, playerFactor); |
| 71 | + |
| 72 | + const totalMultiplier = safeAerial * safePlayer; |
| 73 | + return { clampedFactor, totalMultiplier }; |
| 74 | + } |
| 75 | + |
| 76 | + // --- Calculate effective multipliers --- |
| 77 | + const playerFactors = effectivePower(player, enemy); |
| 78 | + const enemyFactors = effectivePower(enemy, player); |
| 79 | + |
| 80 | + // --- Apply same scaling to Power and the optional attack stats --- |
| 81 | + function scaleStats(entity, factors) { |
| 82 | + return { |
| 83 | + effectivePower: entity.power * factors.clampedFactor * factors.totalMultiplier, |
| 84 | + effectiveMG: entity.MGAttack * factors.clampedFactor * factors.totalMultiplier, |
| 85 | + effectiveWG: entity.WGAttack * factors.clampedFactor * factors.totalMultiplier, |
| 86 | + effectiveMSL: entity.MSLAttack * factors.clampedFactor * factors.totalMultiplier, |
| 87 | + }; |
| 88 | + } |
| 89 | + |
| 90 | + const playerScaled = scaleStats(player, playerFactors); |
| 91 | + const enemyScaled = scaleStats(enemy, enemyFactors); |
| 92 | + |
| 93 | + // --- Win chance (same as before) --- |
| 94 | + let winChance; |
| 95 | + if (enemyScaled.effectivePower <= 0 && playerScaled.effectivePower <= 0) { |
| 96 | + winChance = 50; |
| 97 | + } else if (enemyScaled.effectivePower <= 0) { |
| 98 | + winChance = 100; |
| 99 | + } else { |
| 100 | + const ratio = playerScaled.effectivePower / enemyScaled.effectivePower; |
| 101 | + const steepness = 10; |
| 102 | + winChance = Math.round(100 / (1 + Math.exp(-steepness * (ratio - 1)))); |
| 103 | + winChance = Math.max(0, Math.min(100, winChance)); |
| 104 | + } |
| 105 | + |
| 106 | + // --- Output results --- |
| 107 | + document.getElementById("playerEffectivePower").textContent = playerScaled.effectivePower.toFixed(2); |
| 108 | + document.getElementById("enemyEffectivePower").textContent = enemyScaled.effectivePower.toFixed(2); |
| 109 | + |
| 110 | + // Create or update comparison lines for MG/WG/MSL if applicable |
| 111 | + function setStatLine(id, label, playerVal, enemyVal) { |
| 112 | + if (playerVal > 0 || enemyVal > 0) { |
| 113 | + let elem = document.getElementById(id); |
| 114 | + if (!elem) { |
| 115 | + const ul = document.querySelector("#results ul"); |
| 116 | + elem = document.createElement("li"); |
| 117 | + elem.id = id; |
| 118 | + ul.appendChild(elem); |
| 119 | + } |
| 120 | + elem.innerHTML = `<strong>${label}:</strong> Player ${playerVal.toFixed(2)} vs Enemy ${enemyVal.toFixed(2)}`; |
| 121 | + } else { |
| 122 | + // Remove the line if it exists but both values are 0 |
| 123 | + const elem = document.getElementById(id); |
| 124 | + if (elem) elem.remove(); |
| 125 | + } |
| 126 | + } |
| 127 | + |
| 128 | + setStatLine("mgCompare", "Main Gun Effective Attack", playerScaled.effectiveMG, enemyScaled.effectiveMG); |
| 129 | + setStatLine("wgCompare", "Wing Gun Effective Attack", playerScaled.effectiveWG, enemyScaled.effectiveWG); |
| 130 | + setStatLine("mslCompare", "Missile Effective Attack", playerScaled.effectiveMSL, enemyScaled.effectiveMSL); |
| 131 | + |
| 132 | + document.getElementById("winChance").textContent = `${winChance}%`; |
| 133 | + document.getElementById("results").style.display = "block"; |
| 134 | +}); |
| 135 | + |
| 136 | +document.getElementById("goBackBtn").addEventListener("click", () => { |
| 137 | + window.location.href = "../index.html"; |
| 138 | +}); |
| 139 | + |
| 140 | +// Toggle Optional Stats section |
| 141 | +toggleBtn.addEventListener("click", () => { |
| 142 | + const isVisible = optionalFields.style.display === "block"; |
| 143 | + if (isVisible) { |
| 144 | + optionalFields.style.display = "none"; |
| 145 | + toggleBtn.textContent = "⚙️ Show Optional Stats"; |
| 146 | + } else { |
| 147 | + optionalFields.style.display = "block"; |
| 148 | + toggleBtn.textContent = "⚙️ Hide Optional Stats"; |
| 149 | + } |
| 150 | +}); |
0 commit comments