Files
pogo/index.html
T

232 lines
8.6 KiB
HTML

<!-- Description: Pokémon GO Level Calculator -->
<!-- Author: Justin Oros -->
<!-- Source: https://github.com/JustinOros -->
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Pokémon GO Level Calculator</title>
<link rel="icon" type="image/x-icon" href="favicon.ico">
<link rel="icon" type="image/png" sizes="32x32" href="pogo-32.png">
<link rel="icon" type="image/png" sizes="192x192" href="pogo-192.png">
<link rel="apple-touch-icon" sizes="180x180" href="pogo-180.png">
<link rel="icon" type="image/png" sizes="512x512" href="pogo-512.png">
<script src="https://cdn.tailwindcss.com"></script>
<style>
body {
font-family: 'Inter', sans-serif;
position: relative;
background: url('pogo-wallpaper.jpg') no-repeat center center fixed;
background-size: cover;
transition: background-color 0.4s, color 0.4s;
}
body::before {
content: '';
position: fixed;
top: 0; left: 0;
width: 100%; height: 100%;
background: rgba(255, 255, 255, 0.8);
background-image: radial-gradient(circle at 1px 1px, rgba(0,0,0,0.05) 1px, transparent 0);
background-size: 20px 20px;
opacity: 0.8;
z-index: -1;
transition: background 0.4s, opacity 0.4s;
}
.dark body::before {
background: rgba(0,0,0,0.7);
background-image: radial-gradient(circle at 1px 1px, rgba(255,255,255,0.08) 1px, transparent 0);
opacity: 0.9;
}
.dark-mode {
background-color: #0f172a;
color: #f1f5f9;
}
.dark-mode .bg-white { background-color: #1e293b !important; }
.dark-mode .text-slate-900 { color: #f1f5f9 !important; }
.dark-mode .text-slate-500,
.dark-mode .text-slate-600 { color: #cbd5e1 !important; }
.dark-mode .border-slate-200 { border-color: #334155 !important; }
.dark-mode input { background-color: #334155; color: #f1f5f9; }
.dark-mode #progress-bar { background-color: #38bdf8 !important; }
.dark-mode #theme-toggle { color: #facc15; }
#theme-toggle {
position: absolute;
top: 0.75rem;
right: 0.75rem;
font-size: 1.5rem;
cursor: pointer;
transition: transform 0.3s;
}
#theme-toggle:hover {
transform: scale(1.2);
}
#edit-xp { display: inline-block; transform: scaleX(-1); }
</style>
</head>
<body class="flex items-center justify-center min-h-screen p-4 transition-colors duration-500">
<div id="main-card" class="relative w-full max-w-md bg-white rounded-2xl shadow-xl p-6 md:p-8 border border-slate-200 text-center">
<div id="theme-toggle">☀️</div>
<img src="pogo.png" alt="Pokémon GO" class="mx-auto mb-4 w-32 h-auto">
<h1 class="text-2xl md:text-3xl font-bold text-slate-900 mb-4">Pokémon GO Level Calculator</h1>
<p class="text-slate-500 mb-6">Enter your total XP to see your level</p>
<div class="flex gap-2 mb-4 justify-center">
<input type="text" id="xp-input" placeholder="e.g., 123,456,789"
class="flex-1 px-4 py-3 border border-slate-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-400 text-center text-lg">
<button id="calculate-btn"
class="w-12 h-12 bg-green-500 hover:bg-green-600 text-white font-bold rounded-lg transition-transform transform hover:scale-105">
GO!
</button>
</div>
<div id="result" class="mt-8 hidden">
<div class="relative w-full h-16 bg-slate-200 rounded-lg overflow-hidden">
<div id="progress-bar" class="absolute top-0 left-0 h-full bg-green-500"></div>
<div id="level-text" class="absolute inset-0 flex items-center justify-center text-3xl font-bold text-black"></div>
</div>
<div class="flex justify-between text-xs text-slate-500 mt-1">
<span id="level-left"></span>
<span id="level-right"></span>
</div>
<div id="extra-info" class="mt-3 text-sm text-slate-600">
<p>
If you average
<span id="xp-per-day">15,000</span> XP per day,
you will reach the next level in
<span id="days-next">0</span> and level 80 in
<span id="days-80">0</span>. <span id="edit-xp" class="ml-1 text-blue-500 cursor-pointer">✏️</span>
</p>
</div>
</div>
</div>
<script>
let xpThresholds = [];
let avgXpPerDay = 15000;
let currentXp = 0;
let currentLevel = 1;
async function loadXPThresholds() {
try {
const response = await fetch('levels.csv');
const text = await response.text();
const lines = text.trim().split('\n');
let cumulative = 0;
xpThresholds = lines.map((line, i) => {
const parts = line.split(',');
let xp = parseInt(parts[1].replace(/,/g, ''), 10);
if (i === 0) return 0; // Level 1 starts at 0 XP
cumulative += xp;
return cumulative;
});
console.log("Loaded cumulative XP thresholds:", xpThresholds);
} catch (error) {
alert("Failed to load levels.csv. Please ensure it's in the same folder as index.html.");
console.error(error);
}
}
function calculateLevel(xp) {
let level = 1;
for (let i = 0; i < xpThresholds.length; i++) {
if (xp >= xpThresholds[i]) level = i + 1;
else break;
}
return level;
}
function formatDays(days) {
if (days > 365) return (days / 365).toFixed(1) + ' years';
return days.toLocaleString() + ' days';
}
function updateExtraInfo() {
const maxLevel = xpThresholds.length;
const maxXp = xpThresholds[maxLevel - 1];
if (currentXp >= maxXp) {
document.getElementById('extra-info').innerHTML = `
<p class="text-green-600 font-semibold text-lg">
🎉 Congratulations! You're max level!
</p>`;
return;
}
let nextLevelXp = (currentLevel < xpThresholds.length) ? xpThresholds[currentLevel] : currentXp;
let xpToNext = Math.max(0, nextLevelXp - currentXp);
let xpTo40 = Math.max(0, xpThresholds[39] - currentXp);
let xpTo80 = Math.max(0, xpThresholds[79] - currentXp);
let daysNext = Math.ceil(xpToNext / avgXpPerDay);
let days40 = Math.ceil(xpTo40 / avgXpPerDay);
let days80 = Math.ceil(xpTo80 / avgXpPerDay);
const nextText = currentLevel < 40
? `If you average ${avgXpPerDay.toLocaleString()} XP per day, you will reach the next level in ${formatDays(daysNext)}, level 40 in ${formatDays(days40)}, and level 80 in ${formatDays(days80)}.`
: `If you average ${avgXpPerDay.toLocaleString()} XP per day, you will reach the next level in ${formatDays(daysNext)} and level 80 in ${formatDays(days80)}.`;
document.getElementById('extra-info').innerHTML = `
<p>${nextText} <span id="edit-xp" class="ml-1 text-blue-500 cursor-pointer">✏️</span></p>`;
document.getElementById('edit-xp').addEventListener('click', () => {
let newVal = prompt("Enter your average XP per day:", avgXpPerDay);
if (newVal && !isNaN(newVal) && parseInt(newVal) > 0) {
avgXpPerDay = parseInt(newVal, 10);
updateExtraInfo();
}
});
}
document.getElementById('calculate-btn').addEventListener('click', () => {
if (xpThresholds.length === 0) {
alert('Level data not loaded yet. Please wait a moment and try again.');
return;
}
let xp = document.getElementById('xp-input').value.replace(/,/g, '').trim();
if (!xp || isNaN(xp)) {
alert('Please enter a valid number for XP.');
return;
}
currentXp = parseInt(xp, 10);
currentLevel = calculateLevel(currentXp);
let nextLevelXp = (currentLevel < xpThresholds.length) ? xpThresholds[currentLevel] : currentXp;
let currentLevelXp = xpThresholds[currentLevel - 1];
let progress = (currentXp - currentLevelXp) / (nextLevelXp - currentLevelXp) * 100;
document.getElementById('progress-bar').style.width = progress + '%';
document.getElementById('level-text').textContent = `Level ${currentLevel}`;
document.getElementById('level-left').textContent = currentLevel;
document.getElementById('level-right').textContent = (currentLevel < xpThresholds.length) ? currentLevel + 1 : currentLevel;
document.getElementById('result').classList.remove('hidden');
updateExtraInfo();
});
document.getElementById('xp-input').addEventListener('input', (e) => {
e.target.value = e.target.value.replace(/[^0-9,]/g, '');
});
const themeToggle = document.getElementById('theme-toggle');
themeToggle.addEventListener('click', () => {
document.body.classList.toggle('dark-mode');
themeToggle.textContent = document.body.classList.contains('dark-mode') ? '🌙' : '☀️';
});
window.addEventListener('DOMContentLoaded', loadXPThresholds);
</script>
</body>
</html>