Spaces:
Running
Running
| <html lang="en"> | |
| <head> | |
| <meta charset="UTF-8"> | |
| <meta name="viewport" content="width=device-width, initial-scale=1.0"> | |
| <title>8-Bit Pokémon Adventure</title> | |
| <script src="https://cdn.tailwindcss.com"></script> | |
| <style> | |
| /* Pixel art styling */ | |
| .pixel { | |
| image-rendering: pixelated; | |
| image-rendering: -moz-crisp-edges; | |
| image-rendering: crisp-edges; | |
| } | |
| .pixel-font { | |
| font-family: 'Press Start 2P', cursive; | |
| } | |
| .dialog-box { | |
| border: 4px solid #000; | |
| background-color: #e0f8d0; | |
| box-shadow: 4px 4px 0 rgba(0,0,0,0.2); | |
| } | |
| .health-bar { | |
| height: 10px; | |
| background: linear-gradient(to right, #ff0000, #ffcc00); | |
| border: 2px solid #000; | |
| box-sizing: border-box; | |
| } | |
| .pixel-button { | |
| background-color: #e0f8d0; | |
| border: 3px solid #000; | |
| box-shadow: 3px 3px 0 rgba(0,0,0,0.2); | |
| transition: all 0.1s; | |
| } | |
| .pixel-button:hover { | |
| transform: translate(1px, 1px); | |
| box-shadow: 2px 2px 0 rgba(0,0,0,0.2); | |
| } | |
| .pixel-button:active { | |
| transform: translate(3px, 3px); | |
| box-shadow: none; | |
| } | |
| .battle-screen { | |
| background-color: #88c070; | |
| } | |
| .grass { | |
| background-color: #48d0b0; | |
| } | |
| .water { | |
| background-color: #6890f0; | |
| } | |
| .fire { | |
| background-color: #f08030; | |
| } | |
| .electric { | |
| background-color: #f8d030; | |
| } | |
| @keyframes shake { | |
| 0%, 100% { transform: translateX(0); } | |
| 20%, 60% { transform: translateX(-5px); } | |
| 40%, 80% { transform: translateX(5px); } | |
| } | |
| .shake { | |
| animation: shake 0.4s; | |
| } | |
| @keyframes flash { | |
| 0%, 100% { opacity: 1; } | |
| 50% { opacity: 0.3; } | |
| } | |
| .flash { | |
| animation: flash 0.3s 2; | |
| } | |
| @keyframes float { | |
| 0%, 100% { transform: translateY(0); } | |
| 50% { transform: translateY(-10px); } | |
| } | |
| .float { | |
| animation: float 2s infinite; | |
| } | |
| /* Pixel sprites */ | |
| .sprite { | |
| width: 64px; | |
| height: 64px; | |
| background-size: contain; | |
| background-repeat: no-repeat; | |
| background-position: center; | |
| } | |
| .trainer-sprite { | |
| width: 48px; | |
| height: 48px; | |
| background-size: contain; | |
| background-repeat: no-repeat; | |
| background-position: center; | |
| } | |
| /* Game map */ | |
| .map-tile { | |
| width: 32px; | |
| height: 32px; | |
| background-size: contain; | |
| } | |
| .grass-tile { | |
| background-image: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32"><rect width="32" height="32" fill="%2348d0b0"/><rect x="0" y="24" width="32" height="8" fill="%2378c850"/><circle cx="8" cy="20" r="2" fill="%2378c850"/><circle cx="24" cy="18" r="1" fill="%2378c850"/><circle cx="16" cy="22" r="1" fill="%2378c850"/></svg>'); | |
| } | |
| .tree-tile { | |
| background-image: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32"><rect width="32" height="32" fill="%2348d0b0"/><rect x="14" y="16" width="4" height="16" fill="%23a07050"/><rect x="8" y="8" width="16" height="8" fill="%2378c850"/></svg>'); | |
| } | |
| .water-tile { | |
| background-image: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32"><rect width="32" height="32" fill="%236890f0"/><rect x="0" y="28" width="8" height="4" fill="%235888e0" opacity="0.7"/><rect x="16" y="26" width="8" height="6" fill="%235888e0" opacity="0.7"/><rect x="8" y="30" width="8" height="2" fill="%235888e0" opacity="0.7"/></svg>'); | |
| } | |
| .path-tile { | |
| background-image: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32"><rect width="32" height="32" fill="%23e0c068"/><rect x="0" y="0" width="32" height="32" fill="%23e0c068" patternUnits="userSpaceOnUse" patternTransform="rotate(45 16 16)" patternContentUnits="userSpaceOnUse"><pattern id="diagonalHatch" width="4" height="4" patternTransform="rotate(45 16 16)" patternUnits="userSpaceOnUse"><rect width="2" height="4" fill="%23d0b050"/></pattern><rect width="32" height="32" fill="url(%23diagonalHatch)"/></rect></svg>'); | |
| } | |
| /* Battle sprites */ | |
| .pikachu-sprite { | |
| background-image: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" width="64" height="64" viewBox="0 0 64 64"><rect width="64" height="64" fill="transparent"/><rect x="20" y="32" width="24" height="20" fill="%23f8d030"/><rect x="16" y="36" width="8" height="12" fill="%23f8d030"/><rect x="40" y="36" width="8" height="12" fill="%23f8d030"/><rect x="24" y="16" width="16" height="16" fill="%23f8d030"/><rect x="24" y="12" width="4" height="4" fill="%23f8d030"/><rect x="36" y="12" width="4" height="4" fill="%23f8d030"/><circle cx="28" cy="24" r="2" fill="%23000"/><circle cx="36" cy="24" r="2" fill="%23000"/><rect x="32" y="28" width="4" height="2" fill="%23000"/><path d="M28,40 L36,40 L34,44 L30,44 Z" fill="%23000"/><path d="M44,20 L52,12 L56,16 L48,24 Z" fill="%23f8d030" stroke="%23000" stroke-width="1"/><path d="M12,24 L4,16 L8,12 L16,20 Z" fill="%23f8d030" stroke="%23000" stroke-width="1"/></svg>'); | |
| } | |
| .charmander-sprite { | |
| background-image: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" width="64" height="64" viewBox="0 0 64 64"><rect width="64" height="64" fill="transparent"/><rect x="20" y="28" width="24" height="24" fill="%23f08030"/><rect x="16" y="32" width="8" height="16" fill="%23f08030"/><rect x="40" y="32" width="8" height="16" fill="%23f08030"/><rect x="24" y="16" width="16" height="12" fill="%23f08030"/><circle cx="28" cy="24" r="2" fill="%23000"/><circle cx="36" cy="24" r="2" fill="%23000"/><rect x="32" y="28" width="4" height="2" fill="%23000"/><path d="M28,44 L36,44 L34,48 L30,48 Z" fill="%23000"/><path d="M44,24 L48,20 L52,24 L48,28 Z" fill="%23ff0000"/><path d="M44,24 L48,20 L52,24 L48,28 Z" fill="%23ff0000" stroke="%23000" stroke-width="1"/></svg>'); | |
| } | |
| .squirtle-sprite { | |
| background-image: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" width="64" height="64" viewBox="0 0 64 64"><rect width="64" height="64" fill="transparent"/><circle cx="32" cy="36" r="16" fill="%236890f0"/><circle cx="32" cy="24" r="12" fill="%236890f0"/><circle cx="28" cy="20" r="2" fill="%23000"/><circle cx="36" cy="20" r="2" fill="%23000"/><rect x="30" cy="28" width="4" height="2" fill="%23000"/><rect x="20" y="36" width="8" height="12" fill="%236890f0"/><rect x="36" y="36" width="8" height="12" fill="%236890f0"/><path d="M24,48 L40,48 L36,52 L28,52 Z" fill="%23000"/></svg>'); | |
| } | |
| .bulbasaur-sprite { | |
| background-image: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" width="64" height="64" viewBox="0 0 64 64"><rect width="64" height="64" fill="transparent"/><circle cx="32" cy="40" r="16" fill="%2378c850"/><circle cx="32" cy="24" r="12" fill="%2378c850"/><circle cx="28" cy="20" r="2" fill="%23000"/><circle cx="36" cy="20" r="2" fill="%23000"/><rect x="30" cy="28" width="4" height="2" fill="%23000"/><rect x="20" y="40" width="8" height="12" fill="%2378c850"/><rect x="36" y="40" width="8" height="12" fill="%2378c850"/><path d="M24,52 L40,52 L36,56 L28,56 Z" fill="%23000"/><circle cx="32" cy="8" r="6" fill="%2348d0b0"/><rect x="30" y="8" width="4" height="8" fill="%2348d0b0"/></svg>'); | |
| } | |
| .rattata-sprite { | |
| background-image: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" width="64" height="64" viewBox="0 0 64 64"><rect width="64" height="64" fill="transparent"/><rect x="20" y="28" width="24" height="16" fill="%23a890f0"/><rect x="16" y="32" width="8" height="8" fill="%23a890f0"/><rect x="40" y="32" width="8" height="8" fill="%23a890f0"/><rect x="24" y="20" width="16" height="8" fill="%23a890f0"/><circle cx="28" cy="24" r="2" fill="%23000"/><circle cx="36" cy="24" r="2" fill="%23000"/><rect x="32" y="28" width="4" height="2" fill="%23000"/><path d="M24,40 L40,40 L36,44 L28,44 Z" fill="%23000"/><rect x="44" y="28" width="4" height="4" fill="%23a890f0"/><rect x="48" y="24" width="4" height="12" fill="%23a890f0"/></svg>'); | |
| } | |
| .pidgey-sprite { | |
| background-image: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" width="64" height="64" viewBox="0 0 64 64"><rect width="64" height="64" fill="transparent"/><rect x="24" y="32" width="16" height="12" fill="%23f8d058"/><rect x="20" y="36" width="8" height="8" fill="%23f8d058"/><rect x="36" y="36" width="8" height="8" fill="%23f8d058"/><rect x="28" y="24" width="8" height="8" fill="%23f8d058"/><circle cx="30" cy="28" r="2" fill="%23000"/><circle cx="34" cy="28" r="2" fill="%23000"/><rect x="32" y="32" width="4" height="2" fill="%23000"/><path d="M28,40 L36,40 L34,44 L30,44 Z" fill="%23000"/><rect x="40" y="28" width="12" height="4" fill="%23f8d058"/><rect x="48" y="24" width="4" height="8" fill="%23f8d058"/></svg>'); | |
| } | |
| /* Trainer sprites */ | |
| .player-sprite { | |
| background-image: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" width="48" height="48" viewBox="0 0 48 48"><rect width="48" height="48" fill="transparent"/><rect x="16" y="24" width="16" height="16" fill="%233088f8"/><rect x="16" y="16" width="16" height="8" fill="%23f8c870"/><rect x="20" y="12" width="8" height="4" fill="%23000"/><circle cx="20" cy="14" r="2" fill="%23f8c870"/><circle cx="28" cy="14" r="2" fill="%23f8c870"/><rect x="24" y="18" width="4" height="2" fill="%23000"/><rect x="12" y="24" width="4" height="12" fill="%233088f8"/><rect x="32" y="24" width="4" height="12" fill="%233088f8"/><rect x="16" y="40" width="4" height="4" fill="%23000"/><rect x="28" y="40" width="4" height="4" fill="%23000"/></svg>'); | |
| } | |
| .rival-sprite { | |
| background-image: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" width="48" height="48" viewBox="0 0 48 48"><rect width="48" height="48" fill="transparent"/><rect x="16" y="24" width="16" height="16" fill="%23e05050"/><rect x="16" y="16" width="16" height="8" fill="%23f8c870"/><rect x="20" y="12" width="8" height="4" fill="%23000"/><circle cx="20" cy="14" r="2" fill="%23f8c870"/><circle cx="28" cy="14" r="2" fill="%23f8c870"/><rect x="24" y="18" width="4" height="2" fill="%23000"/><rect x="12" y="24" width="4" height="12" fill="%23e05050"/><rect x="32" y="24" width="4" height="12" fill="%23e05050"/><rect x="16" y="40" width="4" height="4" fill="%23000"/><rect x="28" y="40" width="4" height="4" fill="%23000"/></svg>'); | |
| } | |
| /* Battle animations */ | |
| .tackle-animation { | |
| animation: tackle 0.3s; | |
| } | |
| @keyframes tackle { | |
| 0% { transform: translateX(0); } | |
| 50% { transform: translateX(20px); } | |
| 100% { transform: translateX(0); } | |
| } | |
| .ember-animation { | |
| position: relative; | |
| } | |
| .ember-animation::after { | |
| content: ''; | |
| position: absolute; | |
| top: 50%; | |
| left: 100%; | |
| width: 32px; | |
| height: 32px; | |
| background-image: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32"><rect width="32" height="32" fill="transparent"/><circle cx="16" cy="16" r="8" fill="%23ff0000"/><circle cx="12" cy="12" r="4" fill="%23ffcc00"/><circle cx="20" cy="12" r="3" fill="%23ffcc00"/><circle cx="16" cy="20" r="3" fill="%23ffcc00"/></svg>'); | |
| animation: ember 0.5s forwards; | |
| } | |
| @keyframes ember { | |
| 0% { transform: translate(0, -50%) scale(0.5); opacity: 0; } | |
| 20% { transform: translate(0, -50%) scale(1); opacity: 1; } | |
| 100% { transform: translate(100px, -50%) scale(0.5); opacity: 0; } | |
| } | |
| .thunderbolt-animation { | |
| position: relative; | |
| } | |
| .thunderbolt-animation::after { | |
| content: ''; | |
| position: absolute; | |
| top: 50%; | |
| left: 100%; | |
| width: 32px; | |
| height: 32px; | |
| background-image: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32"><rect width="32" height="32" fill="transparent"/><path d="M16,4 L24,16 L16,12 L24,28 L12,20 L16,24 Z" fill="%23f8d030"/></svg>'); | |
| animation: thunderbolt 0.4s forwards; | |
| } | |
| @keyframes thunderbolt { | |
| 0% { transform: translate(0, -50%) scale(0.5); opacity: 0; } | |
| 20% { transform: translate(0, -50%) scale(1); opacity: 1; } | |
| 100% { transform: translate(100px, -50%) scale(0.5); opacity: 0; } | |
| } | |
| .bubble-animation { | |
| position: relative; | |
| } | |
| .bubble-animation::after { | |
| content: ''; | |
| position: absolute; | |
| top: 50%; | |
| left: 100%; | |
| width: 24px; | |
| height: 24px; | |
| background-image: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><rect width="24" height="24" fill="transparent"/><circle cx="12" cy="12" r="8" fill="%236890f0" opacity="0.7"/><circle cx="8" cy="8" r="3" fill="%23ffffff" opacity="0.9"/></svg>'); | |
| animation: bubble 0.6s forwards; | |
| } | |
| @keyframes bubble { | |
| 0% { transform: translate(0, -50%) scale(0.5); opacity: 0; } | |
| 20% { transform: translate(0, -50%) scale(1); opacity: 1; } | |
| 100% { transform: translate(100px, -50%) scale(0.5); opacity: 0; } | |
| } | |
| .vine-whip-animation { | |
| position: relative; | |
| } | |
| .vine-whip-animation::after { | |
| content: ''; | |
| position: absolute; | |
| top: 50%; | |
| left: 100%; | |
| width: 64px; | |
| height: 8px; | |
| background-image: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" width="64" height="8" viewBox="0 0 64 8"><rect width="64" height="8" fill="transparent"/><rect x="0" y="2" width="64" height="4" fill="%2378c850"/><rect x="8" y="0" width="4" height="8" fill="%2378c850"/><rect x="24" y="0" width="4" height="8" fill="%2378c850"/><rect x="40" y="0" width="4" height="8" fill="%2378c850"/><rect x="56" y="0" width="4" height="8" fill="%2378c850"/></svg>'); | |
| animation: vineWhip 0.5s forwards; | |
| } | |
| @keyframes vineWhip { | |
| 0% { transform: translate(0, -50%) scaleX(0.1); opacity: 0; } | |
| 20% { transform: translate(0, -50%) scaleX(1); opacity: 1; } | |
| 100% { transform: translate(100px, -50%) scaleX(0.1); opacity: 0; } | |
| } | |
| </style> | |
| <link href="https://fonts.googleapis.com/css2?family=Press+Start+2P&display=swap" rel="stylesheet"> | |
| </head> | |
| <body class="bg-gray-900 text-white pixel-font"> | |
| <div id="game-container" class="max-w-3xl mx-auto p-4"> | |
| <!-- Title Screen --> | |
| <div id="title-screen" class="text-center"> | |
| <h1 class="text-4xl mb-8 text-yellow-300">POKÉMON ADVENTURE</h1> | |
| <div class="sprite mx-auto mb-8 pikachu-sprite float"></div> | |
| <div class="flex flex-col space-y-4 max-w-xs mx-auto"> | |
| <button onclick="startGame()" class="pixel-button px-4 py-2 text-lg">NEW GAME</button> | |
| <button onclick="loadGame()" class="pixel-button px-4 py-2 text-lg">LOAD GAME</button> | |
| <button onclick="showOptions()" class="pixel-button px-4 py-2 text-lg">OPTIONS</button> | |
| </div> | |
| </div> | |
| <!-- Starter Selection --> | |
| <div id="starter-screen" class="hidden text-center"> | |
| <h2 class="text-2xl mb-4">CHOOSE YOUR STARTER POKÉMON</h2> | |
| <div class="flex justify-around mb-8"> | |
| <div class="cursor-pointer" onclick="selectStarter('charmander')"> | |
| <div class="sprite charmander-sprite mx-auto"></div> | |
| <p class="mt-2">CHARMANDER</p> | |
| </div> | |
| <div class="cursor-pointer" onclick="selectStarter('squirtle')"> | |
| <div class="sprite squirtle-sprite mx-auto"></div> | |
| <p class="mt-2">SQUIRTLE</p> | |
| </div> | |
| <div class="cursor-pointer" onclick="selectStarter('bulbasaur')"> | |
| <div class="sprite bulbasaur-sprite mx-auto"></div> | |
| <p class="mt-2">BULBASAUR</p> | |
| </div> | |
| </div> | |
| <div class="dialog-box p-4 mb-4"> | |
| <p>These are the three starter Pokémon available in this region. Choose wisely!</p> | |
| </div> | |
| </div> | |
| <!-- World Map --> | |
| <div id="world-screen" class="hidden"> | |
| <div class="flex justify-between mb-4"> | |
| <div> | |
| <p>PLAYER: <span id="player-name">ASH</span></p> | |
| <p>POKÉDEX: <span id="pokedex-count">1</span> SEEN</p> | |
| </div> | |
| <div> | |
| <p>TIME: <span id="game-time">10:00</span></p> | |
| <p>MONEY: $<span id="player-money">1000</span></p> | |
| </div> | |
| </div> | |
| <div id="map-container" class="relative border-4 border-gray-800 bg-gray-800 mx-auto" style="width: 512px; height: 320px;"> | |
| <!-- Map tiles will be generated here --> | |
| <div id="player" class="absolute trainer-sprite player-sprite" style="top: 144px; left: 240px;"></div> | |
| <div id="rival" class="absolute trainer-sprite rival-sprite hidden" style="top: 96px; left: 192px;"></div> | |
| <div id="wild-pokemon" class="absolute sprite hidden" style="top: 80px; left: 320px;"></div> | |
| </div> | |
| <div class="dialog-box p-4 mt-4"> | |
| <p id="map-message">You are in the tall grass. Wild Pokémon may appear!</p> | |
| </div> | |
| <div class="grid grid-cols-2 gap-4 mt-4"> | |
| <button onclick="openMenu('pokemon')" class="pixel-button px-4 py-2">POKÉMON</button> | |
| <button onclick="openMenu('bag')" class="pixel-button px-4 py-2">BAG</button> | |
| <button onclick="openMenu('player')" class="pixel-button px-4 py-2">PLAYER</button> | |
| <button onclick="openMenu('save')" class="pixel-button px-4 py-2">SAVE</button> | |
| </div> | |
| </div> | |
| <!-- Battle Screen --> | |
| <div id="battle-screen" class="hidden battle-screen"> | |
| <div class="relative" style="height: 240px;"> | |
| <!-- Enemy Pokémon --> | |
| <div id="enemy-pokemon" class="absolute sprite" style="top: 40px; right: 40px;"></div> | |
| <div class="absolute bg-white bg-opacity-30 border-2 border-black" style="top: 20px; right: 20px; width: 120px; height: 40px;"> | |
| <p class="text-right px-2"><span id="enemy-name">PIDGEY</span> Lv.<span id="enemy-level">5</span></p> | |
| <div class="health-bar mx-2" id="enemy-health" style="width: 100px;"></div> | |
| </div> | |
| <!-- Player Pokémon --> | |
| <div id="player-pokemon" class="absolute sprite" style="bottom: 60px; left: 40px;"></div> | |
| <div class="absolute bg-white bg-opacity-30 border-2 border-black" style="bottom: 20px; left: 20px; width: 160px; height: 60px;"> | |
| <p class="text-left px-2"><span id="player-pokemon-name">PIKACHU</span> Lv.<span id="player-pokemon-level">5</span></p> | |
| <p class="text-left px-2">HP: <span id="player-pokemon-hp">20</span>/<span id="player-pokemon-max-hp">20</span></p> | |
| <div class="health-bar mx-2" id="player-health" style="width: 140px;"></div> | |
| </div> | |
| </div> | |
| <div id="battle-dialog" class="dialog-box p-4"> | |
| <p>A wild <span id="battle-pokemon-name">PIDGEY</span> appeared!</p> | |
| </div> | |
| <div id="battle-options" class="grid grid-cols-2 gap-4 mt-4"> | |
| <button onclick="battleAction('fight')" class="pixel-button px-4 py-2">FIGHT</button> | |
| <button onclick="battleAction('pokemon')" class="pixel-button px-4 py-2">POKÉMON</button> | |
| <button onclick="battleAction('bag')" class="pixel-button px-4 py-2">BAG</button> | |
| <button onclick="battleAction('run')" class="pixel-button px-4 py-2">RUN</button> | |
| </div> | |
| <div id="move-options" class="hidden grid grid-cols-2 gap-4 mt-4"> | |
| <!-- Move buttons will be added here dynamically --> | |
| </div> | |
| </div> | |
| <!-- Battle Result Screen --> | |
| <div id="battle-result" class="hidden text-center"> | |
| <div class="dialog-box p-4 mb-4 mx-auto max-w-md"> | |
| <p id="result-message">PIKACHU gained 100 EXP. Points!</p> | |
| </div> | |
| <button onclick="returnToWorld()" class="pixel-button px-4 py-2">CONTINUE</button> | |
| </div> | |
| <!-- Pokémon Menu --> | |
| <div id="pokemon-menu" class="hidden"> | |
| <h2 class="text-2xl mb-4">POKÉMON</h2> | |
| <div id="pokemon-list" class="space-y-2 mb-4"> | |
| <!-- Pokémon list will be added here dynamically --> | |
| </div> | |
| <button onclick="closeMenu()" class="pixel-button px-4 py-2">BACK</button> | |
| </div> | |
| <!-- Bag Menu --> | |
| <div id="bag-menu" class="hidden"> | |
| <h2 class="text-2xl mb-4">BAG</h2> | |
| <div class="flex space-x-4 mb-4"> | |
| <button onclick="showBagCategory('pokeballs')" class="pixel-button px-4 py-2">POKÉBALLS</button> | |
| <button onclick="showBagCategory('potions')" class="pixel-button px-4 py-2">POTIONS</button> | |
| <button onclick="showBagCategory('other')" class="pixel-button px-4 py-2">OTHER</button> | |
| </div> | |
| <div id="bag-items" class="space-y-2 mb-4"> | |
| <!-- Bag items will be added here dynamically --> | |
| </div> | |
| <button onclick="closeMenu()" class="pixel-button px-4 py-2">BACK</button> | |
| </div> | |
| <!-- Player Menu --> | |
| <div id="player-menu" class="hidden text-center"> | |
| <h2 class="text-2xl mb-4">PLAYER INFO</h2> | |
| <div class="trainer-sprite player-sprite mx-auto mb-4"></div> | |
| <div class="dialog-box p-4 mb-4 mx-auto max-w-md"> | |
| <p>NAME: <span id="menu-player-name">ASH</span></p> | |
| <p>MONEY: $<span id="menu-player-money">1000</span></p> | |
| <p>POKÉDEX: <span id="menu-pokedex-count">1</span> SEEN</p> | |
| <p>PLAY TIME: <span id="menu-game-time">10:00</span></p> | |
| </div> | |
| <button onclick="closeMenu()" class="pixel-button px-4 py-2">BACK</button> | |
| </div> | |
| <!-- Save Menu --> | |
| <div id="save-menu" class="hidden text-center"> | |
| <h2 class="text-2xl mb-4">SAVE GAME</h2> | |
| <div class="dialog-box p-4 mb-4 mx-auto max-w-md"> | |
| <p>Would you like to save your game?</p> | |
| </div> | |
| <div class="flex justify-center space-x-4"> | |
| <button onclick="saveGame()" class="pixel-button px-4 py-2">YES</button> | |
| <button onclick="closeMenu()" class="pixel-button px-4 py-2">NO</button> | |
| </div> | |
| </div> | |
| <!-- Save Confirmation --> | |
| <div id="save-confirm" class="hidden text-center"> | |
| <div class="dialog-box p-4 mb-4 mx-auto max-w-md"> | |
| <p>Game saved successfully!</p> | |
| </div> | |
| <button onclick="closeMenu()" class="pixel-button px-4 py-2">CONTINUE</button> | |
| </div> | |
| </div> | |
| <script> | |
| // Game state | |
| const gameState = { | |
| player: { | |
| name: 'ASH', | |
| money: 1000, | |
| pokedex: [], | |
| pokemon: [], | |
| items: { | |
| pokeballs: [ | |
| { name: 'POKÉ BALL', quantity: 5 }, | |
| { name: 'GREAT BALL', quantity: 2 } | |
| ], | |
| potions: [ | |
| { name: 'POTION', quantity: 3 }, | |
| { name: 'SUPER POTION', quantity: 1 } | |
| ], | |
| other: [ | |
| { name: 'POKÉ DOLL', quantity: 1 }, | |
| { name: 'ESCAPE ROPE', quantity: 1 } | |
| ] | |
| } | |
| }, | |
| currentScreen: 'title', | |
| inBattle: false, | |
| battle: { | |
| enemy: null, | |
| playerPokemon: null, | |
| turn: 'player' | |
| }, | |
| starterSelected: false, | |
| gameTime: 10 * 60, // 10:00 in minutes | |
| map: { | |
| width: 16, | |
| height: 10, | |
| playerPosition: { x: 7, y: 4 }, | |
| rivalPosition: { x: 6, y: 3 }, | |
| wildPokemon: null | |
| } | |
| }; | |
| // Pokémon data | |
| const pokemonData = { | |
| pikachu: { | |
| name: 'PIKACHU', | |
| type: 'electric', | |
| sprite: 'pikachu-sprite', | |
| baseStats: { hp: 35, attack: 55, defense: 40, speed: 90 }, | |
| moves: [ | |
| { name: 'TACKLE', power: 40, accuracy: 100, type: 'normal', pp: 35 }, | |
| { name: 'THUNDERBOLT', power: 90, accuracy: 100, type: 'electric', pp: 15 } | |
| ] | |
| }, | |
| charmander: { | |
| name: 'CHARMANDER', | |
| type: 'fire', | |
| sprite: 'charmander-sprite', | |
| baseStats: { hp: 39, attack: 52, defense: 43, speed: 65 }, | |
| moves: [ | |
| { name: 'TACKLE', power: 40, accuracy: 100, type: 'normal', pp: 35 }, | |
| { name: 'EMBER', power: 40, accuracy: 100, type: 'fire', pp: 25 } | |
| ] | |
| }, | |
| squirtle: { | |
| name: 'SQUIRTLE', | |
| type: 'water', | |
| sprite: 'squirtle-sprite', | |
| baseStats: { hp: 44, attack: 48, defense: 65, speed: 43 }, | |
| moves: [ | |
| { name: 'TACKLE', power: 40, accuracy: 100, type: 'normal', pp: 35 }, | |
| { name: 'BUBBLE', power: 40, accuracy: 100, type: 'water', pp: 30 } | |
| ] | |
| }, | |
| bulbasaur: { | |
| name: 'BULBASAUR', | |
| type: 'grass', | |
| sprite: 'bulbasaur-sprite', | |
| baseStats: { hp: 45, attack: 49, defense: 49, speed: 45 }, | |
| moves: [ | |
| { name: 'TACKLE', power: 40, accuracy: 100, type: 'normal', pp: 35 }, | |
| { name: 'VINE WHIP', power: 45, accuracy: 100, type: 'grass', pp: 25 } | |
| ] | |
| }, | |
| rattata: { | |
| name: 'RATTATA', | |
| type: 'normal', | |
| sprite: 'rattata-sprite', | |
| baseStats: { hp: 30, attack: 56, defense: 35, speed: 72 }, | |
| moves: [ | |
| { name: 'TACKLE', power: 40, accuracy: 100, type: 'normal', pp: 35 } | |
| ] | |
| }, | |
| pidgey: { | |
| name: 'PIDGEY', | |
| type: 'normal', | |
| sprite: 'pidgey-sprite', | |
| baseStats: { hp: 40, attack: 45, defense: 40, speed: 56 }, | |
| moves: [ | |
| { name: 'TACKLE', power: 40, accuracy: 100, type: 'normal', pp: 35 } | |
| ] | |
| } | |
| }; | |
| // Wild Pokémon encounter table | |
| const wildPokemon = [ | |
| { name: 'rattata', level: 2, chance: 0.5 }, | |
| { name: 'pidgey', level: 3, chance: 0.3 }, | |
| { name: 'pikachu', level: 5, chance: 0.2 } | |
| ]; | |
| // Type effectiveness chart | |
| const typeEffectiveness = { | |
| normal: { normal: 1, fire: 1, water: 1, electric: 1, grass: 1 }, | |
| fire: { normal: 1, fire: 0.5, water: 0.5, electric: 1, grass: 2 }, | |
| water: { normal: 1, fire: 2, water: 0.5, electric: 1, grass: 0.5 }, | |
| electric: { normal: 1, fire: 1, water: 2, electric: 0.5, grass: 0.5 }, | |
| grass: { normal: 1, fire: 0.5, water: 2, electric: 1, grass: 0.5 } | |
| }; | |
| // Initialize game | |
| function initGame() { | |
| // Set up event listeners for movement | |
| document.addEventListener('keydown', handleKeyPress); | |
| // Start game clock | |
| setInterval(updateGameTime, 1000); | |
| // Generate initial map | |
| generateMap(); | |
| } | |
| // Start a new game | |
| function startGame() { | |
| document.getElementById('title-screen').classList.add('hidden'); | |
| document.getElementById('starter-screen').classList.remove('hidden'); | |
| gameState.currentScreen = 'starter'; | |
| } | |
| // Load game (placeholder) | |
| function loadGame() { | |
| alert('Load game functionality not implemented in this demo.'); | |
| } | |
| // Show options (placeholder) | |
| function showOptions() { | |
| alert('Options menu not implemented in this demo.'); | |
| } | |
| // Select starter Pokémon | |
| function selectStarter(starter) { | |
| const starterPokemon = { | |
| ...pokemonData[starter], | |
| level: 5, | |
| currentHp: calculateStat(pokemonData[starter].baseStats.hp, 5), | |
| experience: 0, | |
| moves: pokemonData[starter].moves.map(move => ({ ...move, currentPp: move.pp })) | |
| }; | |
| gameState.player.pokemon.push(starterPokemon); | |
| gameState.player.pokedex.push(starterPokemon.name); | |
| gameState.starterSelected = true; | |
| document.getElementById('starter-screen').classList.add('hidden'); | |
| document.getElementById('world-screen').classList.remove('hidden'); | |
| gameState.currentScreen = 'world'; | |
| // Show welcome message | |
| showDialog(`You chose ${starterPokemon.name}! Good luck on your journey!`); | |
| } | |
| // Generate a Pokémon with random level | |
| function generatePokemon(pokemonName, level) { | |
| const baseData = pokemonData[pokemonName]; | |
| return { | |
| ...baseData, | |
| level: level, | |
| currentHp: calculateStat(baseData.baseStats.hp, level), | |
| maxHp: calculateStat(baseData.baseStats.hp, level), | |
| moves: baseData.moves.map(move => ({ ...move, currentPp: move.pp })) | |
| }; | |
| } | |
| // Calculate a stat based on level | |
| function calculateStat(base, level) { | |
| return Math.floor((2 * base * level) / 100) + level + 10; | |
| } | |
| // Calculate damage | |
| function calculateDamage(attacker, defender, move) { | |
| const attackStat = attacker.baseStats.attack; | |
| const defenseStat = defender.baseStats.defense; | |
| const level = attacker.level; | |
| const power = move.power; | |
| const effectiveness = typeEffectiveness[move.type][defender.type] || 1; | |
| // Simplified damage formula | |
| const damage = Math.floor((((2 * level / 5 + 2) * attackStat * power / defenseStat) / 50 + 2) * effectiveness); | |
| return damage; | |
| } | |
| // Generate the game map | |
| function generateMap() { | |
| const mapContainer = document.getElementById('map-container'); | |
| mapContainer.innerHTML = ''; | |
| // Add player sprite back | |
| const player = document.createElement('div'); | |
| player.id = 'player'; | |
| player.className = 'absolute trainer-sprite player-sprite'; | |
| player.style.top = `${gameState.map.playerPosition.y * 32}px`; | |
| player.style.left = `${gameState.map.playerPosition.x * 32}px`; | |
| mapContainer.appendChild(player); | |
| // Generate tiles | |
| for (let y = 0; y < gameState.map.height; y++) { | |
| for (let x = 0; x < gameState.map.width; x++) { | |
| const tile = document.createElement('div'); | |
| tile.className = 'absolute map-tile'; | |
| // Simple map generation | |
| if (x === 0 || y === 0 || x === gameState.map.width - 1 || y === gameState.map.height - 1) { | |
| tile.classList.add('tree-tile'); | |
| } else if (x > 3 && x < 12 && y > 2 && y < 7) { | |
| tile.classList.add('grass-tile'); | |
| } else if (x === 8 && y === 5) { | |
| tile.classList.add('water-tile'); | |
| } else { | |
| tile.classList.add('path-tile'); | |
| } | |
| tile.style.top = `${y * 32}px`; | |
| tile.style.left = `${x * 32}px`; | |
| mapContainer.appendChild(tile); | |
| } | |
| } | |
| // Add rival if not in battle | |
| if (!gameState.inBattle) { | |
| const rival = document.createElement('div'); | |
| rival.id = 'rival'; | |
| rival.className = 'absolute trainer-sprite rival-sprite'; | |
| rival.style.top = `${gameState.map.rivalPosition.y * 32}px`; | |
| rival.style.left = `${gameState.map.rivalPosition.x * 32}px`; | |
| mapContainer.appendChild(rival); | |
| } | |
| } | |
| // Handle key presses for movement | |
| function handleKeyPress(e) { | |
| if (gameState.currentScreen !== 'world' || gameState.inBattle) return; | |
| let newX = gameState.map.playerPosition.x; | |
| let newY = gameState.map.playerPosition.y; | |
| switch(e.key) { | |
| case 'ArrowUp': | |
| newY = Math.max(0, newY - 1); | |
| break; | |
| case 'ArrowDown': | |
| newY = Math.min(gameState.map.height - 1, newY + 1); | |
| break; | |
| case 'ArrowLeft': | |
| newX = Math.max(0, newX - 1); | |
| break; | |
| case 'ArrowRight': | |
| newX = Math.min(gameState.map.width - 1, newX + 1); | |
| break; | |
| default: | |
| return; | |
| } | |
| // Check if the new position is valid (not a tree) | |
| const isTree = (newX === 0 || newY === 0 || newX === gameState.map.width - 1 || newY === gameState.map.height - 1); | |
| if (!isTree) { | |
| gameState.map.playerPosition.x = newX; | |
| gameState.map.playerPosition.y = newY; | |
| // Update player position on screen | |
| const player = document.getElementById('player'); | |
| player.style.top = `${newY * 32}px`; | |
| player.style.left = `${newX * 32}px`; | |
| // Check for encounters | |
| checkForEncounter(); | |
| // Check for rival battle | |
| if (newX === gameState.map.rivalPosition.x && newY === gameState.map.rivalPosition.y) { | |
| startRivalBattle(); | |
| } | |
| } | |
| } | |
| // Check for wild Pokémon encounter | |
| function checkForEncounter() { | |
| const inGrass = ( | |
| gameState.map.playerPosition.x > 3 && | |
| gameState.map.playerPosition.x < 12 && | |
| gameState.map.playerPosition.y > 2 && | |
| gameState.map.playerPosition.y < 7 | |
| ); | |
| if (inGrass && Math.random() < 0.2) { | |
| const encounter = getRandomEncounter(); | |
| startWildBattle(encounter.name, encounter.level); | |
| } | |
| } | |
| // Get a random wild Pokémon encounter | |
| function getRandomEncounter() { | |
| const rand = Math.random(); | |
| let cumulativeChance = 0; | |
| for (const pokemon of wildPokemon) { | |
| cumulativeChance += pokemon.chance; | |
| if (rand <= cumulativeChance) { | |
| return pokemon; | |
| } | |
| } | |
| return wildPokemon[0]; // Fallback | |
| } | |
| // Start a wild Pokémon battle | |
| function startWildBattle(pokemonName, level) { | |
| gameState.inBattle = true; | |
| gameState.currentScreen = 'battle'; | |
| // Generate wild Pokémon | |
| gameState.battle.enemy = generatePokemon(pokemonName, level); | |
| gameState.battle.playerPokemon = gameState.player.pokemon[0]; | |
| // Add to Pokédex if not already seen | |
| if (!gameState.player.pokedex.includes(gameState.battle.enemy.name)) { | |
| gameState.player.pokedex.push(gameState.battle.enemy.name); | |
| updatePokedexCount(); | |
| } | |
| // Update UI | |
| document.getElementById('world-screen').classList.add('hidden'); | |
| document.getElementById('battle-screen').classList.remove('hidden'); | |
| // Set up battle display | |
| document.getElementById('enemy-pokemon').className = `sprite ${gameState.battle.enemy.sprite}`; | |
| document.getElementById('enemy-name').textContent = gameState.battle.enemy.name; | |
| document.getElementById('enemy-level').textContent = gameState.battle.enemy.level; | |
| document.getElementById('enemy-health').style.width = '100px'; | |
| document.getElementById('player-pokemon').className = `sprite ${gameState.battle.playerPokemon.sprite}`; | |
| document.getElementById('player-pokemon-name').textContent = gameState.battle.playerPokemon.name; | |
| document.getElementById('player-pokemon-level').textContent = gameState.battle.playerPokemon.level; | |
| document.getElementById('player-pokemon-hp').textContent = gameState.battle.playerPokemon.currentHp; | |
| document.getElementById('player-pokemon-max-hp').textContent = calculateStat(gameState.battle.playerPokemon.baseStats.hp, gameState.battle.playerPokemon.level); | |
| updateHealthBar('player-health', gameState.battle.playerPokemon.currentHp, calculateStat(gameState.battle.playerPokemon.baseStats.hp, gameState.battle.playerPokemon.level)); | |
| document.getElementById('battle-pokemon-name').textContent = gameState.battle.enemy.name; | |
| document.getElementById('battle-dialog').innerHTML = `<p>A wild ${gameState.battle.enemy.name} appeared!</p>`; | |
| // Show battle options | |
| document.getElementById('battle-options').classList.remove('hidden'); | |
| document.getElementById('move-options').classList.add('hidden'); | |
| } | |
| // Start a rival battle | |
| function startRivalBattle() { | |
| gameState.inBattle = true; | |
| gameState.currentScreen = 'battle'; | |
| // Generate rival's Pokémon (always Squirtle for this demo) | |
| gameState.battle.enemy = generatePokemon('squirtle', 5); | |
| gameState.battle.playerPokemon = gameState.player.pokemon[0]; | |
| // Update UI | |
| document.getElementById('world-screen').classList.add('hidden'); | |
| document.getElementById('battle-screen').classList.remove('hidden'); | |
| // Set up battle display | |
| document.getElementById('enemy-pokemon').className = `sprite ${gameState.battle.enemy.sprite}`; | |
| document.getElementById('enemy-name').textContent = gameState.battle.enemy.name; | |
| document.getElementById('enemy-level').textContent = gameState.battle.enemy.level; | |
| document.getElementById('enemy-health').style.width = '100px'; | |
| document.getElementById('player-pokemon').className = `sprite ${gameState.battle.playerPokemon.sprite}`; | |
| document.getElementById('player-pokemon-name').textContent = gameState.battle.playerPokemon.name; | |
| document.getElementById('player-pokemon-level').textContent = gameState.battle.playerPokemon.level; | |
| document.getElementById('player-pokemon-hp').textContent = gameState.battle.playerPokemon.currentHp; | |
| document.getElementById('player-pokemon-max-hp').textContent = calculateStat(gameState.battle.playerPokemon.baseStats.hp, gameState.battle.playerPokemon.level); | |
| updateHealthBar('player-health', gameState.battle.playerPokemon.currentHp, calculateStat(gameState.battle.playerPokemon.baseStats.hp, gameState.battle.playerPokemon.level)); | |
| document.getElementById('battle-pokemon-name').textContent = 'RIVAL'; | |
| document.getElementById('battle-dialog').innerHTML = `<p>RIVAL wants to battle!</p><p>RIVAL sent out ${gameState.battle.enemy.name}!</p>`; | |
| // Show battle options | |
| document.getElementById('battle-options').classList.remove('hidden'); | |
| document.getElementById('move-options').classList.add('hidden'); | |
| } | |
| // Handle battle actions | |
| function battleAction(action) { | |
| if (action === 'fight') { | |
| // Show move selection | |
| document.getElementById('battle-options').classList.add('hidden'); | |
| document.getElementById('move-options').classList.remove('hidden'); | |
| // Clear any existing move buttons | |
| const moveOptions = document.getElementById('move-options'); | |
| moveOptions.innerHTML = ''; | |
| // Add move buttons | |
| gameState.battle.playerPokemon.moves.forEach((move, index) => { | |
| const button = document.createElement('button'); | |
| button.className = 'pixel-button px-4 py-2'; | |
| button.textContent = `${move.name} (${move.currentPp}/${move.pp})`; | |
| button.onclick = () => useMove(index); | |
| moveOptions.appendChild(button); | |
| }); | |
| // Add back button | |
| const backButton = document.createElement('button'); | |
| backButton.className = 'pixel-button px-4 py-2'; | |
| backButton.textContent = 'BACK'; | |
| backButton.onclick = () => { | |
| document.getElementById('battle-options').classList.remove('hidden'); | |
| document.getElementById('move-options').classList.add('hidden'); | |
| }; | |
| moveOptions.appendChild(backButton); | |
| } else if (action === 'pokemon') { | |
| // Show Pokémon selection (simplified for demo) | |
| showDialog("You can only use one Pokémon in this demo."); | |
| } else if (action === 'bag') { | |
| // Show bag (simplified for demo) | |
| showDialog("Bag items not implemented in this demo."); | |
| } else if (action === 'run') { | |
| // Attempt to run (always succeeds in this demo) | |
| showDialog("Got away safely!"); | |
| setTimeout(() => { | |
| endBattle(false); | |
| }, 1500); | |
| } | |
| } | |
| // Use a move in battle | |
| function useMove(moveIndex) { | |
| const move = gameState.battle.playerPokemon.moves[moveIndex]; | |
| // Check if move has PP left | |
| if (move.currentPp <= 0) { | |
| showDialog("No PP left for this move!"); | |
| return; | |
| } | |
| // Deduct PP | |
| move.currentPp--; | |
| // Player's turn | |
| playerAttack(move); | |
| // Enemy's turn (if not defeated) | |
| if (gameState.battle.enemy.currentHp > 0) { | |
| setTimeout(() => { | |
| enemyAttack(); | |
| }, 2000); | |
| } | |
| } | |
| // Player attacks | |
| function playerAttack(move) { | |
| // Calculate damage | |
| const damage = calculateDamage( | |
| gameState.battle.playerPokemon, | |
| gameState.battle.enemy, | |
| move | |
| ); | |
| // Apply damage | |
| gameState.battle.enemy.currentHp = Math.max(0, gameState.battle.enemy.currentHp - damage); | |
| // Update UI | |
| showDialog(`${gameState.battle.playerPokemon.name} used ${move.name}!`); | |
| // Add animation based on move type | |
| const enemySprite = document.getElementById('enemy-pokemon'); | |
| enemySprite.classList.add('shake'); | |
| if (move.name === 'TACKLE') { | |
| const playerSprite = document.getElementById('player-pokemon'); | |
| playerSprite.classList.add('tackle-animation'); | |
| } else if (move.name === 'EMBER') { | |
| const playerSprite = document.getElementById('player-pokemon'); | |
| playerSprite.classList.add('ember-animation'); | |
| } else if (move.name === 'THUNDERBOLT') { | |
| const playerSprite = document.getElementById('player-pokemon'); | |
| playerSprite.classList.add('thunderbolt-animation'); | |
| } else if (move.name === 'BUBBLE') { | |
| const playerSprite = document.getElementById('player-pokemon'); | |
| playerSprite.classList.add('bubble-animation'); | |
| } else if (move.name === 'VINE WHIP') { | |
| const playerSprite = document.getElementById('player-pokemon'); | |
| playerSprite.classList.add('vine-whip-animation'); | |
| } | |
| // Update enemy health | |
| setTimeout(() => { | |
| const maxHp = calculateStat(gameState.battle.enemy.baseStats.hp, gameState.battle.enemy.level); | |
| updateHealthBar('enemy-health', gameState.battle.enemy.currentHp, maxHp); | |
| // Check if enemy fainted | |
| if (gameState.battle.enemy.currentHp <= 0) { | |
| showDialog(`${gameState.battle.enemy.name} fainted!`); | |
| setTimeout(() => { | |
| endBattle(true); | |
| }, 1500); | |
| } | |
| // Remove animations | |
| enemySprite.classList.remove('shake'); | |
| const playerSprite = document.getElementById('player-pokemon'); | |
| playerSprite.classList.remove('tackle-animation', 'ember-animation', 'thunderbolt-animation', 'bubble-animation', 'vine-whip-animation'); | |
| }, 500); | |
| } | |
| // Enemy attacks | |
| function enemyAttack() { | |
| // Simple AI - choose random move | |
| const moveIndex = Math.floor(Math.random() * gameState.battle.enemy.moves.length); | |
| const move = gameState.battle.enemy.moves[moveIndex]; | |
| // Calculate damage | |
| const damage = calculateDamage( | |
| gameState.battle.enemy, | |
| gameState.battle.playerPokemon, | |
| move | |
| ); | |
| // Apply damage | |
| gameState.battle.playerPokemon.currentHp = Math.max(0, gameState.battle.playerPokemon.currentHp - damage); | |
| // Update UI | |
| showDialog(`Enemy ${gameState.battle.enemy.name} used ${move.name}!`); | |
| // Add animation | |
| const playerSprite = document.getElementById('player-pokemon'); | |
| playerSprite.classList.add('shake', 'flash'); | |
| // Update player health | |
| setTimeout(() => { | |
| const maxHp = calculateStat(gameState.battle.playerPokemon.baseStats.hp, gameState.battle.playerPokemon.level); | |
| updateHealthBar('player-health', gameState.battle.playerPokemon.currentHp, maxHp); | |
| document.getElementById('player-pokemon-hp').textContent = gameState.battle.playerPokemon.currentHp; | |
| // Check if player's Pokémon fainted | |
| if (gameState.battle.playerPokemon.currentHp <= 0) { | |
| showDialog(`${gameState.battle.playerPokemon.name} fainted!`); | |
| setTimeout(() => { | |
| endBattle(false); | |
| }, 1500); | |
| } | |
| // Remove animations | |
| playerSprite.classList.remove('shake', 'flash'); | |
| }, 500); | |
| } | |
| // End battle | |
| function endBattle(playerWon) { | |
| if (playerWon) { | |
| // Calculate experience gained | |
| const baseExp = gameState.battle.enemy.level * 10; | |
| const exp = Math.floor(baseExp * 1.5); // Bonus for defeating a trainer | |
| // Add experience | |
| gameState.battle.playerPokemon.experience += exp; | |
| // Check for level up (simplified) | |
| if (gameState.battle.playerPokemon.experience >= gameState.battle.playerPokemon.level * 100) { | |
| gameState.battle.playerPokemon.level++; | |
| gameState.battle.playerPokemon.currentHp = calculateStat(gameState.battle.playerPokemon.baseStats.hp, gameState.battle.playerPokemon.level); | |
| // Show result with level up | |
| document.getElementById('battle-screen').classList.add('hidden'); | |
| document.getElementById('battle-result').classList.remove('hidden'); | |
| document.getElementById('result-message').textContent = | |
| `${gameState.battle.playerPokemon.name} gained ${exp} EXP. Points! ${gameState.battle.playerPokemon.name} grew to LV. ${gameState.battle.playerPokemon.level}!`; | |
| } else { | |
| // Show result without level up | |
| document.getElementById('battle-screen').classList.add('hidden'); | |
| document.getElementById('battle-result').classList.remove('hidden'); | |
| document.getElementById('result-message').textContent = | |
| `${gameState.battle.playerPokemon.name} gained ${exp} EXP. Points!`; | |
| } | |
| // Add money (only from trainers in this demo) | |
| if (gameState.battle.enemy.name === 'SQUIRTLE') { // Rival's Pokémon | |
| gameState.player.money += 500; | |
| updatePlayerMoney(); | |
| } | |
| } else { | |
| // Player lost | |
| document.getElementById('battle-screen').classList.add('hidden'); | |
| document.getElementById('battle-result').classList.remove('hidden'); | |
| document.getElementById('result-message').textContent = | |
| "You lost the battle!"; | |
| } | |
| // Reset battle state | |
| gameState.inBattle = false; | |
| gameState.battle = { | |
| enemy: null, | |
| playerPokemon: null, | |
| turn: 'player' | |
| }; | |
| } | |
| // Return to world after battle | |
| function returnToWorld() { | |
| document.getElementById('battle-result').classList.add('hidden'); | |
| document.getElementById('world-screen').classList.remove('hidden'); | |
| gameState.currentScreen = 'world'; | |
| // Regenerate map to show changes | |
| generateMap(); | |
| } | |
| // Update health bar display | |
| function updateHealthBar(elementId, currentHp, maxHp) { | |
| const healthBar = document.getElementById(elementId); | |
| const percentage = Math.max(0, (currentHp / maxHp) * 100); | |
| healthBar.style.width = `${percentage}%`; | |
| // Change color based on health | |
| if (percentage < 20) { | |
| healthBar.style.background = '#ff0000'; | |
| } else if (percentage < 50) { | |
| healthBar.style.background = '#ffcc00'; | |
| } else { | |
| healthBar.style.background = '#00cc00'; | |
| } | |
| } | |
| // Show dialog message | |
| function showDialog(message) { | |
| const dialog = document.getElementById('battle-dialog'); | |
| dialog.innerHTML = `<p>${message}</p>`; | |
| } | |
| // Open menu | |
| function openMenu(menu) { | |
| document.getElementById('world-screen').classList.add('hidden'); | |
| if (menu === 'pokemon') { | |
| document.getElementById('pokemon-menu').classList.remove('hidden'); | |
| showPokemonList(); | |
| } else if (menu === 'bag') { | |
| document.getElementById('bag-menu').classList.remove('hidden'); | |
| showBagCategory('pokeballs'); | |
| } else if (menu === 'player') { | |
| document.getElementById('player-menu').classList.remove('hidden'); | |
| } else if (menu === 'save') { | |
| document.getElementById('save-menu').classList.remove('hidden'); | |
| } | |
| } | |
| // Close menu | |
| function closeMenu() { | |
| document.getElementById('pokemon-menu').classList.add('hidden'); | |
| document.getElementById('bag-menu').classList.add('hidden'); | |
| document.getElementById('player-menu').classList.add('hidden'); | |
| document.getElementById('save-menu').classList.add('hidden'); | |
| document.getElementById('save-confirm').classList.add('hidden'); | |
| document.getElementById('world-screen').classList.remove('hidden'); | |
| } | |
| // Show Pokémon list | |
| function showPokemonList() { | |
| const listContainer = document.getElementById('pokemon-list'); | |
| listContainer.innerHTML = ''; | |
| gameState.player.pokemon.forEach(pokemon => { | |
| const item = document.createElement('div'); | |
| item.className = 'flex items-center p-2 bg-white bg-opacity-10 rounded'; | |
| const sprite = document.createElement('div'); | |
| sprite.className = `sprite ${pokemon.sprite} mr-4`; | |
| const info = document.createElement('div'); | |
| info.className = 'flex-1'; | |
| info.innerHTML = ` | |
| <p>${pokemon.name} Lv.${pokemon.level}</p> | |
| <div class="flex items-center"> | |
| <span class="mr-2">HP:</span> | |
| <div class="health-bar" style="width: 100px;"></div> | |
| </div> | |
| `; | |
| // Update health bar for this Pokémon | |
| const maxHp = calculateStat(pokemon.baseStats.hp, pokemon.level); | |
| const healthBar = info.querySelector('.health-bar'); | |
| const percentage = Math.max(0, (pokemon.currentHp / maxHp) * 100); | |
| healthBar.style.width = `${percentage}%`; | |
| if (percentage < 20) { | |
| healthBar.style.background = '#ff0000'; | |
| } else if (percentage < 50) { | |
| healthBar.style.background = '#ffcc00'; | |
| } else { | |
| healthBar.style.background = '#00cc00'; | |
| } | |
| item.appendChild(sprite); | |
| item.appendChild(info); | |
| listContainer.appendChild(item); | |
| }); | |
| } | |
| // Show bag category | |
| function showBagCategory(category) { | |
| const itemsContainer = document.getElementById('bag-items'); | |
| itemsContainer.innerHTML = ''; | |
| gameState.player.items[category].forEach(item => { | |
| const itemElement = document.createElement('div'); | |
| itemElement.className = 'flex justify-between items-center p-2 bg-white bg-opacity-10 rounded'; | |
| itemElement.innerHTML = ` | |
| <span>${item.name}</span> | |
| <span>×${item.quantity}</span> | |
| `; | |
| itemsContainer.appendChild(itemElement); | |
| }); | |
| } | |
| // Save game (placeholder) | |
| function saveGame() { | |
| document.getElementById('save-menu').classList.add('hidden'); | |
| document.getElementById('save-confirm').classList.remove('hidden'); | |
| // In a real game, you would save to localStorage or a server here | |
| localStorage.setItem('pokemonGameSave', JSON.stringify(gameState)); | |
| } | |
| // Update game time | |
| function updateGameTime() { | |
| gameState.gameTime++; | |
| const hours = Math.floor(gameState.gameTime / 60) % 24; | |
| const minutes = gameState.gameTime % 60; | |
| const timeString = `${hours.toString().padStart(2, '0')}:${minutes.toString().padStart(2, '0')}`; | |
| document.getElementById('game-time').textContent = timeString; | |
| document.getElementById('menu-game-time').textContent = timeString; | |
| } | |
| // Update Pokédex count | |
| function updatePokedexCount() { | |
| const count = gameState.player.pokedex.length; | |
| document.getElementById('pokedex-count').textContent = count; | |
| document.getElementById('menu-pokedex-count').textContent = count; | |
| } | |
| // Update player money | |
| function updatePlayerMoney() { | |
| document.getElementById('player-money').textContent = gameState.player.money; | |
| document.getElementById('menu-player-money').textContent = gameState.player.money; | |
| } | |
| // Initialize the game when the page loads | |
| window.onload = initGame; | |
| </script> | |
| <p style="border-radius: 8px; text-align: center; font-size: 12px; color: #fff; margin-top: 16px;position: fixed; left: 8px; bottom: 8px; z-index: 10; background: rgba(0, 0, 0, 0.8); padding: 4px 8px;">Made with <img src="https://enzostvs-deepsite.hf.space/logo.svg" alt="DeepSite Logo" style="width: 16px; height: 16px; vertical-align: middle;display:inline-block;margin-right:3px;filter:brightness(0) invert(1);"><a href="https://enzostvs-deepsite.hf.space" style="color: #fff;text-decoration: underline;" target="_blank" >DeepSite</a> - 🧬 <a href="https://enzostvs-deepsite.hf.space?remix=vikassabbi/vspace" style="color: #fff;text-decoration: underline;" target="_blank" >Remix</a></p></body> | |
| </html> |