<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>PokeAPI ポケモンバトル - はじめの仲間たち</title>
<script src="https://cdn.tailwindcss.com"></script>
<link href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0/css/all.min.css" rel="stylesheet">
<style>
@import url('https://fonts.googleapis.com/css2?family=DotGothic16&display=swap');
body {
font-family: 'Helvetica Neue', Arial, "Hiragino Kaku Gothic ProN", "Hiragino Sans", Meiryo, sans-serif;
background-color: #1a202c;
color: #333;
overflow-x: hidden;
}
.pixel-font {
font-family: 'DotGothic16', sans-serif;
}
/* Title Screen Animation */
.title-float {
animation: float 3s ease-in-out infinite;
}
@keyframes float {
0%, 100% { transform: translateY(0); }
50% { transform: translateY(-10px); }
}
.battle-screen {
position: relative;
height: 350px;
border-radius: 8px;
overflow: hidden;
box-shadow: inset 0 0 50px rgba(0,0,0,0.2);
transition: all 0.8s ease;
}
.bg-grassland { background: linear-gradient(to bottom, #87CEEB 0%, #87CEEB 60%, #90EE90 60%, #90EE90 100%); }
.gmax-bg {
background: linear-gradient(to bottom, #2d004d 0%, #4b0082 60%, #1a0033 100%) !important;
}
.pokemon-img {
image-rendering: pixelated;
transition: transform 0.5s ease, opacity 0.5s ease, filter 0.5s;
}
.shiny-sparkle {
animation: sparkle 0.8s ease-in-out infinite;
filter: drop-shadow(0 0 10px gold);
}
@keyframes sparkle {
0%, 100% { filter: brightness(1) drop-shadow(0 0 5px gold); }
50% { filter: brightness(1.5) drop-shadow(0 0 15px white); }
}
.mega-img {
transform: scale(1.2) translateY(-10px);
filter: drop-shadow(0 0 15px rgba(0, 255, 255, 0.6));
}
.gmax-img {
transform: scale(1.6) translateY(-15px);
filter: drop-shadow(0 0 20px rgba(255, 50, 50, 0.8));
}
.evolving {
animation: evolveEffect 2s infinite;
}
@keyframes evolveEffect {
0% { filter: brightness(1) contrast(1) invert(0); transform: scale(1); }
50% { filter: brightness(5) contrast(0) invert(1); transform: scale(1.3); }
100% { filter: brightness(1) contrast(1) invert(0); transform: scale(1); }
}
.hp-bar {
height: 10px;
transition: width 0.5s ease-in-out;
}
.shake { animation: shake 0.5s cubic-bezier(.36,.07,.19,.97) both; }
@keyframes shake {
10%, 90% { transform: translate3d(-1px, 0, 0); }
20%, 80% { transform: translate3d(2px, 0, 0); }
30%, 50%, 70% { transform: translate3d(-4px, 0, 0); }
40%, 60% { transform: translate3d(4px, 0, 0); }
}
.attack-player { animation: attackPlayer 0.5s ease-in-out; }
@keyframes attackPlayer { 0% { transform: translateX(0); } 50% { transform: translateX(30px) translateY(-20px); } 100% { transform: translateX(0); } }
.attack-enemy { animation: attackEnemy 0.5s ease-in-out; }
@keyframes attackEnemy { 0% { transform: translateX(0); } 50% { transform: translateX(-30px) translateY(20px); } 100% { transform: translateX(0); } }
.message-box {
background-color: rgba(255, 255, 255, 0.9);
border: 4px solid #333;
min-height: 85px;
}
.loader {
border: 4px solid #f3f3f3;
border-top: 4px solid #3498db;
border-radius: 50%;
width: 30px;
height: 30px;
animation: spin 2s linear infinite;
}
@keyframes spin { 0% { transform: rotate(0deg); } 100% { transform: rotate(360deg); } }
.starter-card {
transition: transform 0.2s;
}
.starter-card:hover {
transform: translateY(-5px);
border-color: #3b82f6;
}
</style>
</head>
<body class="flex flex-col items-center justify-center min-h-screen p-4 bg-gray-900 text-gray-800">
<div id="game-container" class="max-w-lg w-full bg-white rounded-xl shadow-2xl overflow-hidden border-4 border-gray-800">
<!-- Title Screen -->
<div id="title-screen" class="p-10 flex flex-col items-center justify-center text-center">
<div class="mb-8 title-float">
<h1 class="text-4xl font-black text-blue-600 pixel-font tracking-tighter italic">POCKET<br><span class="text-red-500">BATTLE</span></h1>
<p class="text-xs font-bold text-gray-400 mt-2">API Edition</p>
</div>
<div class="w-24 h-24 mb-10 opacity-20">
<img src="https://raw.githubusercontent.com/PokeAPI/sprites/master/sprites/items/poke-ball.png" class="w-full h-full object-contain grayscale">
</div>
<button onclick="startGame()" class="group relative px-8 py-4 bg-red-600 text-white rounded-full font-bold text-xl shadow-[0_4px_0_rgb(153,27,27)] hover:shadow-none hover:translate-y-1 transition-all pixel-font">
ぼうけんを はじめる
</button>
<p class="mt-8 text-[10px] text-gray-400">PRESS START TO PLAY</p>
</div>
<!-- Starter Selection (Initially Hidden) -->
<div id="starter-selection" class="p-6 hidden">
<h2 class="text-xl font-bold text-center pixel-font mb-4">冒険に 出かける ポケモンを えらぼう!</h2>
<div class="grid grid-cols-4 gap-3 max-h-[400px] overflow-y-auto p-2 border-2 border-gray-100 rounded-lg bg-gray-50 shadow-inner" id="starter-list"></div>
<div id="selection-loader" class="mt-8 flex flex-col items-center">
<div class="loader mb-2"></div>
<p class="text-xs text-gray-500">図鑑を 読み込み中...</p>
</div>
</div>
<!-- Battle Screen -->
<div id="battle-area" class="hidden">
<div id="battle-bg" class="battle-screen p-4 flex flex-col justify-between bg-grassland">
<!-- Enemy Area -->
<div class="flex justify-start items-start">
<div id="enemy-stats" class="bg-white/90 p-2 rounded-lg border-2 border-gray-700 w-48 shadow-md">
<div class="flex justify-between items-center mb-1">
<span id="enemy-name" class="font-bold text-xs truncate mr-1">...</span>
<span class="text-[10px] font-bold shrink-0">Lv.50</span>
</div>
<div class="w-full bg-gray-300 rounded-full border border-gray-400">
<div id="enemy-hp-fill" class="hp-bar bg-green-500 rounded-full" style="width: 100%"></div>
</div>
<div class="text-right text-[10px] mt-1 font-bold" id="enemy-hp-text">0/0</div>
</div>
<div class="ml-auto relative">
<img id="enemy-img" src="" alt="Enemy" class="w-28 h-28 pokemon-img object-contain">
</div>
</div>
<!-- Player Area -->
<div class="flex justify-end items-end">
<div class="mr-auto">
<img id="player-img" src="" alt="Player" class="w-36 h-36 pokemon-img object-contain">
</div>
<div id="player-stats" class="bg-white/90 p-2 rounded-lg border-2 border-gray-700 w-48 shadow-md">
<div class="flex justify-between items-center mb-1">
<span id="player-name" class="font-bold text-xs truncate mr-1">...</span>
<span class="text-[10px] font-bold shrink-0">Lv.50</span>
</div>
<div class="w-full bg-gray-300 rounded-full border border-gray-400">
<div id="player-hp-fill" class="hp-bar bg-green-500 rounded-full" style="width: 100%"></div>
</div>
<div class="text-right text-[10px] mt-1 font-bold" id="player-hp-text">0/0</div>
</div>
</div>
</div>
<div id="log-container" class="message-box p-3 m-2 rounded-lg border-double border-4 flex flex-col justify-center">
<p id="message-text" class="text-sm font-bold leading-relaxed pixel-font">...</p>
<div id="evolution-btn-container" class="hidden mt-2">
<button onclick="evolveActivePokemon()" class="w-full bg-yellow-500 text-white font-bold py-1 px-4 rounded shadow-md text-xs pixel-font animate-pulse">進化させる!</button>
</div>
</div>
<div id="controls" class="p-3 bg-gray-200 border-t-4 border-gray-800">
<div id="action-controls">
<div class="grid grid-cols-2 gap-2" id="move-buttons"></div>
<div class="mt-2 flex flex-wrap gap-2">
<button onclick="healPlayer()" class="flex-1 bg-green-600 text-white font-bold py-2 rounded shadow-md text-xs">きずぐすり</button>
<button onclick="toggleBallMenu()" class="flex-1 bg-red-600 text-white font-bold py-2 rounded shadow-md text-xs">捕まえる</button>
<button onclick="changeEnemyPokemon()" class="flex-1 bg-indigo-600 text-white font-bold py-2 rounded shadow-md text-xs">別の場所へ</button>
<button id="special-transform-btn" onclick="executeSpecialTransformation()" class="hidden flex-1 bg-gradient-to-r from-blue-400 via-purple-500 to-pink-500 text-white font-bold py-2 rounded shadow-md text-xs border-2 border-white/50 animate-bounce">メガシンカ!</button>
</div>
</div>
<div id="ball-selection-menu" class="hidden">
<p class="text-[10px] font-bold mb-2 text-center">どのボールを つかいますか?</p>
<div class="grid grid-cols-3 gap-2">
<button onclick="catchWithBall('common')" class="bg-white border-2 border-red-500 p-2 rounded text-lg flex flex-col items-center">⚾<span class="text-[8px] text-gray-500">モンスター</span></button>
<button onclick="catchWithBall('great')" class="bg-white border-2 border-blue-500 p-2 rounded text-lg flex flex-col items-center">🔵<span class="text-[8px] text-gray-500">スーパー</span></button>
<button onclick="catchWithBall('ultra')" class="bg-white border-2 border-yellow-500 p-2 rounded text-lg flex flex-col items-center">🟡<span class="text-[8px] text-gray-500">ハイパー</span></button>
</div>
<button onclick="toggleBallMenu()" class="w-full mt-2 text-[10px] text-gray-500 py-1 underline">キャンセル</button>
</div>
</div>
</div>
<div id="main-loader" class="p-8 hidden flex flex-col items-center justify-center">
<div class="loader mb-4"></div>
<p class="text-gray-600 font-bold">読み込み中...</p>
</div>
</div>
<div id="collection-area" class="max-w-lg w-full mt-4 p-4 bg-white/10 rounded-xl border-2 border-white/20 hidden">
<div class="flex justify-between items-center mb-2">
<h3 class="text-white font-bold text-sm"><i class="fas fa-box-open mr-2"></i> てもちのポケモン</h3>
<span class="text-yellow-400 text-[10px] font-bold">色違い確率: 20%</span>
</div>
<div id="collection-list" class="flex flex-wrap gap-2"></div>
</div>
<script>
const API_URL = "https://pokeapi.co/api/v2/pokemon/";
const SHINY_CHANCE = 0.2;
let activePartnerId = null;
let player = null;
let enemy = null;
let isProcessing = false;
let capturedPokemons = [];
let isTransformed = false;
const MEGA_MAP = {
3: 10033, 6: 10034, 9: 10036, 65: 10037, 94: 10038, 257: 10050, 384: 10084, 445: 10058, 448: 10059
};
const GMAX_LIST = {
6: "charizard-gmax", 25: "pikachu-gmax", 133: "eevee-gmax", 143: "snorlax-gmax", 94: "gengar-gmax", 131: "lapras-gmax", 68: "machamp-gmax"
};
const ballData = { common: { name: "モンスターボール", rate: 1.0 }, great: { name: "スーパーボール", rate: 1.5 }, ultra: { name: "ハイパーボール", rate: 2.5 } };
function startGame() {
document.getElementById('title-screen').classList.add('hidden');
document.getElementById('starter-selection').classList.remove('hidden');
initStarterSelection();
}
function getJapaneseName(names) {
const ja = names.find(n => n.language.name === 'ja-Hrkt' || n.language.name === 'ja');
return ja ? ja.name : names[0].name;
}
async function fetchPokemon(idOrName, forcedShiny = null) {
const isShiny = forcedShiny !== null ? forcedShiny : Math.random() < SHINY_CHANCE;
const pokeRes = await fetch(`${API_URL}${idOrName}`);
const data = await pokeRes.json();
const speciesRes = await fetch(data.species.url);
const speciesData = await speciesRes.json();
let japaneseName = getJapaneseName(speciesData.names);
const isGmax = data.name.includes('-gmax');
const isMega = data.id >= 10000;
if (isGmax) {
japaneseName = `キョダイ${japaneseName}`;
} else if (isMega) {
if (data.id === 10035) japaneseName = "メガリザードンY";
else if (data.id === 10034) japaneseName = "メガリザードンX";
else japaneseName = `メガ${japaneseName}`;
}
let moves = [];
if (isGmax || isMega) {
const prefix = isGmax ? "キョダイ" : "メガ";
moves = [
{ name: `${prefix}${japaneseName.replace(prefix, '')}わざ`, power: 150 },
{ name: isMega ? "はかいこうせん" : "ダイマックスほう", power: 120 },
{ name: "サイコキネシス", power: 90 },
{ name: "しんそく", power: 80 }
];
} else {
moves = await Promise.all(data.moves.slice(0, 4).map(async (m) => {
const moveRes = await fetch(m.move.url);
const moveData = await moveRes.json();
return { name: getJapaneseName(moveData.names), power: moveData.power || 50 };
}));
}
const sprites = data.sprites.versions['generation-v']?.['black-white']?.animated || data.sprites;
return {
baseId: data.id >= 10000 ? speciesData.id : data.id,
id: data.id,
name: japaneseName,
hp: data.stats[0].base_stat * (isGmax || isMega ? 3 : 2),
maxHp: data.stats[0].base_stat * (isGmax || isMega ? 3 : 2),
attack: data.stats[1].base_stat * (isGmax || isMega ? 1.6 : 1),
defense: data.stats[2].base_stat * (isGmax || isMega ? 1.4 : 1),
sprite: isShiny ? (sprites.front_shiny || data.sprites.front_shiny) : (sprites.front_default || data.sprites.front_default),
backSprite: isShiny ? (sprites.back_shiny || data.sprites.back_shiny) : (sprites.back_default || data.sprites.back_default),
staticSprite: isShiny ? data.sprites.front_shiny : data.sprites.front_default,
moves: moves,
speciesUrl: data.species.url,
wins: 0,
isShiny: isShiny,
isGmax: isGmax,
isMega: isMega
};
}
async function initStarterSelection() {
const starters = [1, 4, 7, 152, 155, 158, 252, 255, 258, 387, 390, 393, 25, 133, 447, 92];
const starterList = document.getElementById('starter-list');
starterList.innerHTML = '';
for (const id of starters) {
try {
const pokemon = await fetchPokemon(id);
const card = document.createElement('div');
card.className = `starter-card p-2 rounded-lg text-center border-2 cursor-pointer flex flex-col items-center bg-white border-gray-200`;
card.onclick = () => {
addCollection(pokemon);
switchActivePokemon(pokemon.id);
document.getElementById('starter-selection').classList.add('hidden');
document.getElementById('collection-area').classList.remove('hidden');
startNewBattle();
};
card.innerHTML = `
<img src="${pokemon.staticSprite}" class="w-12 h-12">
<p class="text-[8px] font-bold mt-1">${pokemon.name}</p>
`;
starterList.appendChild(card);
} catch(e) { console.error(e); }
}
document.getElementById('selection-loader').classList.add('hidden');
}
async function changeEnemyPokemon() {
if (isProcessing) return;
updateMessage("別の場所へ ポケモンを さがしにいく...");
await new Promise(r => setTimeout(r, 800));
startNewBattle();
}
function switchActivePokemon(id) {
const target = capturedPokemons.find(p => p.id === id);
if (!target) return;
activePartnerId = id;
player = JSON.parse(JSON.stringify(target));
isTransformed = false;
updateUI();
renderMoves();
renderCollection();
}
async function startNewBattle() {
isProcessing = true;
document.getElementById('main-loader').classList.remove('hidden');
document.getElementById('battle-area').classList.add('hidden');
document.getElementById('battle-bg').classList.remove('gmax-bg');
try {
const randomId = Math.floor(Math.random() * 800) + 1;
enemy = await fetchPokemon(randomId);
updateUI();
renderMoves();
document.getElementById('main-loader').classList.add('hidden');
document.getElementById('battle-area').classList.remove('hidden');
updateMessage(enemy.isShiny ? `★野生の 色違い ${enemy.name} が とびだしてきた!★` : `あ! 野生の ${enemy.name} が とびだしてきた!`);
isProcessing = false;
} catch (e) { startNewBattle(); }
}
function updateUI() {
if (!player || !enemy) return;
const pImgEl = document.getElementById('player-img');
document.getElementById('player-name').innerText = (player.isShiny ? '★' : '') + player.name;
pImgEl.src = player.backSprite || player.sprite;
pImgEl.classList.remove('gmax-img', 'mega-img');
const bg = document.getElementById('battle-bg');
bg.classList.remove('gmax-bg');
if (player.isGmax) { pImgEl.classList.add('gmax-img'); bg.classList.add('gmax-bg'); }
else if (player.isMega) { pImgEl.classList.add('mega-img'); bg.classList.add('gmax-bg'); }
updateHPBar('player', player.hp, player.maxHp);
document.getElementById('enemy-name').innerText = (enemy.isShiny ? '★' : '') + enemy.name;
document.getElementById('enemy-img').src = enemy.sprite;
updateHPBar('enemy', enemy.hp, enemy.maxHp);
const specialBtn = document.getElementById('special-transform-btn');
const baseId = player.baseId || player.id;
const canMega = MEGA_MAP[baseId] && !isTransformed;
const canGmax = GMAX_LIST[baseId] && !isTransformed;
if (canMega || canGmax) {
specialBtn.classList.remove('hidden');
specialBtn.innerText = canMega ? "メガシンカ!" : "キョダイマックス!";
} else {
specialBtn.classList.add('hidden');
}
}
function updateHPBar(who, current, max) {
const percent = Math.max(0, (current / max) * 100);
const fill = document.getElementById(`${who}-hp-fill`);
fill.style.width = `${percent}%`;
fill.className = `hp-bar ${percent < 20 ? 'bg-red-500' : percent < 50 ? 'bg-yellow-500' : 'bg-green-500'} rounded-full`;
document.getElementById(`${who}-hp-text`).innerText = `${Math.ceil(current)} / ${max}`;
}
function renderMoves() {
const container = document.getElementById('move-buttons');
container.innerHTML = '';
player.moves.forEach(move => {
const btn = document.createElement('button');
btn.className = `border-2 font-bold py-2 rounded text-[11px] shadow-sm transition-all active:scale-95 ${move.power >= 100 ? 'bg-red-50 border-red-500 text-red-700' : 'bg-white border-gray-400'}`;
btn.innerText = move.name;
btn.onclick = () => handleAttack(move);
container.appendChild(btn);
});
}
function updateMessage(text) { document.getElementById('message-text').innerText = text; }
async function handleAttack(move) {
if (isProcessing) return;
isProcessing = true;
await performMove(player, enemy, move, 'player');
if (enemy.hp <= 0) {
updateMessage(`${enemy.name} を たおした!`);
recordWin();
showBattleEndOptions();
return;
}
await enemyTurn();
}
async function enemyTurn() {
await new Promise(r => setTimeout(r, 1000));
const move = enemy.moves[Math.floor(Math.random() * enemy.moves.length)];
await performMove(enemy, player, move, 'enemy');
if (player.hp <= 0) {
updateMessage(`${player.name} は たおれてしまった...`);
showBattleEndOptions();
return;
}
isProcessing = false;
}
async function performMove(attacker, defender, move, role) {
updateMessage(`${attacker.name} の ${move.name}!`);
document.getElementById(`${role}-img`).classList.add(role === 'player' ? 'attack-player' : 'attack-enemy');
await new Promise(r => setTimeout(r, 500));
document.getElementById(`${role}-img`).classList.remove(role === 'player' ? 'attack-player' : 'attack-enemy');
if (move.power > 0) {
let damage = (((2 * 50 / 5 + 2) * move.power * (attacker.attack / defender.defense)) / 50) + 2;
defender.hp = Math.max(0, defender.hp - damage);
document.getElementById(role === 'player' ? 'enemy-img' : 'player-img').classList.add('shake');
updateHPBar(role === 'player' ? 'enemy' : 'player', defender.hp, defender.maxHp);
await new Promise(r => setTimeout(r, 500));
document.getElementById(role === 'player' ? 'enemy-img' : 'player-img').classList.remove('shake');
} else {
updateMessage(`${attacker.name} は まもりを かためた!`);
await new Promise(r => setTimeout(r, 800));
}
}
function showBattleEndOptions() {
const container = document.getElementById('move-buttons');
container.innerHTML = `
<button onclick="startNewBattle()" class="col-span-2 bg-blue-600 text-white font-bold py-3 rounded text-sm pixel-font shadow-lg hover:bg-blue-700">次のポケモンを さがす</button>
`;
isProcessing = false;
}
function toggleBallMenu() {
document.getElementById('action-controls').classList.toggle('hidden');
document.getElementById('ball-selection-menu').classList.toggle('hidden');
}
async function catchWithBall(ballType) {
toggleBallMenu();
isProcessing = true;
updateMessage(`${ballData[ballType].name} を なげた!`);
await new Promise(r => setTimeout(r, 1000));
const catchRate = 0.4 * ballData[ballType].rate * (1 + (1 - (enemy.hp / enemy.maxHp)));
if (Math.random() < catchRate) {
updateMessage(`${enemy.name} を つかまえた!`);
addCollection(enemy);
showBattleEndOptions();
} else {
updateMessage("だめだ! ボールから もれだしてしまった!");
await enemyTurn();
}
}
async function executeSpecialTransformation() {
if (isProcessing) return;
isProcessing = true;
isTransformed = true;
const baseId = player.baseId || player.id;
const targetId = MEGA_MAP[baseId] || GMAX_LIST[baseId];
updateMessage(`${player.name} は キーストーンに 反応している!`);
document.getElementById('player-img').classList.add('evolving');
await new Promise(r => setTimeout(r, 2000));
const newForm = await fetchPokemon(targetId, player.isShiny);
const hpRatio = player.hp / player.maxHp;
const wins = player.wins;
player = newForm;
player.hp = player.maxHp * hpRatio;
player.wins = wins;
document.getElementById('player-img').classList.remove('evolving');
updateUI();
renderMoves();
updateMessage(`${player.name} へ 変身した!`);
isProcessing = false;
}
function healPlayer() {
if (isProcessing || player.hp >= player.maxHp) return;
isProcessing = true;
player.hp = Math.min(player.maxHp, player.hp + 60);
updateHPBar('player', player.hp, player.maxHp);
updateMessage(`${player.name} の きずが いえた!`);
setTimeout(enemyTurn, 800);
}
function recordWin() {
const p = capturedPokemons.find(p => (p.baseId || p.id) === (player.baseId || player.id));
if (p) { p.wins++; checkEvolution(p); }
}
function checkEvolution(p) { if (p.wins >= 2) document.getElementById('evolution-btn-container').classList.remove('hidden'); }
async function evolveActivePokemon() {
if (isProcessing) return;
isProcessing = true;
document.getElementById('evolution-btn-container').classList.add('hidden');
updateMessage(`${player.name} の ようすが おかしい...?`);
document.getElementById('player-img').classList.add('evolving');
await new Promise(r => setTimeout(r, 2000));
try {
const res = await fetch(player.speciesUrl);
const speciesData = await res.json();
const evoRes = await fetch(speciesData.evolution_chain.url);
const evoData = await evoRes.json();
let current = evoData.chain;
while (current && current.species.name !== speciesData.name) current = current.evolves_to[0];
if (current && current.evolves_to.length > 0) {
const evolved = await fetchPokemon(current.evolves_to[0].species.name, player.isShiny);
const idx = capturedPokemons.findIndex(x => (x.baseId || x.id) === (player.baseId || player.id));
capturedPokemons[idx] = evolved;
switchActivePokemon(evolved.id);
updateMessage(`${evolved.name} に しんかした!`);
}
} catch (e) { console.error(e); }
document.getElementById('player-img').classList.remove('evolving');
isProcessing = false;
}
function addCollection(p) {
const exists = capturedPokemons.find(x => (x.baseId || x.id) === (p.baseId || p.id));
if (!exists) capturedPokemons.push(p);
renderCollection();
}
function renderCollection() {
const list = document.getElementById('collection-list');
list.innerHTML = '';
capturedPokemons.forEach(p => {
const item = document.createElement('div');
// playerがまだ存在しない場合を考慮して?.を使用(初期選択時など)
const isActive = player && (p.baseId || p.id) === (player.baseId || player.id);
item.className = `w-10 h-10 bg-white rounded border-2 cursor-pointer flex items-center justify-center relative ${isActive ? 'border-blue-500 ring-2 ring-blue-200' : 'border-gray-300'}`;
item.innerHTML = `<img src="${p.staticSprite}" class="w-full h-full object-contain ${p.isShiny ? 'shiny-sparkle' : ''}">`;
item.onclick = () => !isProcessing && switchActivePokemon(p.id);
list.appendChild(item);
});
}
</script>
</body>
</html>
ポケモンバトルゲーム