157 lines
4.5 KiB
TypeScript
157 lines
4.5 KiB
TypeScript
import { Game } from './lib/game';
|
|
import { getTypeInfo, TypeName } from './lib/types';
|
|
import './style.css';
|
|
|
|
const app = document.getElementById('app')!;
|
|
|
|
const game = new Game(render);
|
|
game.nextQuestion();
|
|
|
|
function renderTypeBadge(name: TypeName, size: 'small' | 'large' = 'small'): string {
|
|
const info = getTypeInfo(name);
|
|
const sizeClass = size === 'large' ? 'type-badge-large' : '';
|
|
return `<span class="type-badge ${sizeClass}" style="background: ${info.backgroundColor}; color: ${info.color}">${info.name}</span>`;
|
|
}
|
|
|
|
function render(): void {
|
|
const state = game.getState();
|
|
const { options, startKey } = game.getOptions();
|
|
const stats = game.getStats();
|
|
|
|
if (!state.currentQuestion) {
|
|
app.innerHTML = '<div class="loading">Loading...</div>';
|
|
return;
|
|
}
|
|
|
|
const q = state.currentQuestion;
|
|
const defenderHtml = q.defenderTypes.length === 1
|
|
? renderTypeBadge(q.defenderTypes[0], 'large')
|
|
: `${renderTypeBadge(q.defenderTypes[0], 'large')} / ${renderTypeBadge(q.defenderTypes[1], 'large')}`;
|
|
|
|
const optionsHtml = options.map((opt, i) => {
|
|
let cls = 'option-btn';
|
|
if (state.showResult) {
|
|
if (opt.value === q.correctAnswer) {
|
|
cls += ' correct';
|
|
} else if (i === state.selectedIndex && !state.isCorrect) {
|
|
cls += ' wrong';
|
|
}
|
|
} else if (i === state.selectedIndex) {
|
|
cls += ' selected';
|
|
}
|
|
|
|
const shortcut = `${startKey + i}`;
|
|
const disabled = state.showResult ? 'disabled' : '';
|
|
|
|
return `
|
|
<button class="${cls}" data-index="${i}" ${disabled}>
|
|
<span class="option-key">${shortcut}</span>
|
|
<span class="option-label">${opt.label}</span>
|
|
</button>
|
|
`;
|
|
}).join('');
|
|
|
|
const resultHtml = state.showResult ? `
|
|
<div class="result ${state.isCorrect ? 'correct' : 'wrong'}">
|
|
${state.isCorrect
|
|
? '<div class="result-title">✓ Correct!</div>'
|
|
: `<div class="result-title">✗ Wrong</div>
|
|
<div class="explanation">${game.getExplanation()}</div>`
|
|
}
|
|
<button class="next-btn">Next Question (Enter)</button>
|
|
</div>
|
|
` : '';
|
|
|
|
app.innerHTML = `
|
|
<div class="game-container">
|
|
<header class="header">
|
|
<h1>Pokemon Type Quiz</h1>
|
|
<div class="stats">
|
|
<span class="stat">Streak: ${state.streak}</span>
|
|
<span class="stat">Accuracy: ${stats.accuracy.toFixed(1)}%</span>
|
|
<span class="stat">Total: ${stats.total}</span>
|
|
</div>
|
|
</header>
|
|
|
|
<div class="settings">
|
|
<label class="toggle">
|
|
<input type="checkbox" ${state.settings.allowDualTypes ? 'checked' : ''} id="dualToggle">
|
|
<span class="toggle-label">Allow Dual Types</span>
|
|
</label>
|
|
</div>
|
|
|
|
<div class="question">
|
|
<div class="question-label">Attacking Type</div>
|
|
<div class="attack-type">${renderTypeBadge(q.attackType, 'large')}</div>
|
|
|
|
<div class="vs">against</div>
|
|
|
|
<div class="question-label">Defending Type${q.defenderTypes.length > 1 ? 's' : ''}</div>
|
|
<div class="defender-types">${defenderHtml}</div>
|
|
</div>
|
|
|
|
<div class="options">
|
|
${optionsHtml}
|
|
</div>
|
|
|
|
${resultHtml}
|
|
|
|
<footer class="footer">
|
|
<span class="hint">Press ${startKey}-${startKey + options.length - 1} to select • Enter to continue</span>
|
|
</footer>
|
|
</div>
|
|
`;
|
|
|
|
attachEventListeners();
|
|
}
|
|
|
|
function attachEventListeners(): void {
|
|
document.querySelectorAll('.option-btn').forEach(btn => {
|
|
btn.addEventListener('click', () => {
|
|
const index = parseInt(btn.getAttribute('data-index')!);
|
|
game.selectAnswer(index);
|
|
});
|
|
});
|
|
|
|
const dualToggle = document.getElementById('dualToggle') as HTMLInputElement;
|
|
if (dualToggle) {
|
|
dualToggle.addEventListener('change', () => {
|
|
game.setAllowDualTypes(dualToggle.checked);
|
|
});
|
|
}
|
|
|
|
const nextBtn = document.querySelector('.next-btn');
|
|
if (nextBtn) {
|
|
nextBtn.addEventListener('click', () => {
|
|
game.nextQuestion();
|
|
});
|
|
}
|
|
}
|
|
|
|
document.addEventListener('keydown', (e) => {
|
|
const state = game.getState();
|
|
const { options, startKey } = game.getOptions();
|
|
|
|
if (state.showResult) {
|
|
if (e.key === 'Enter' || e.key === ' ') {
|
|
e.preventDefault();
|
|
game.nextQuestion();
|
|
}
|
|
return;
|
|
}
|
|
|
|
const num = parseInt(e.key);
|
|
if (num >= startKey && num < startKey + options.length) {
|
|
e.preventDefault();
|
|
game.selectAnswer(num - startKey);
|
|
}
|
|
|
|
if (e.key === 'Enter') {
|
|
if (state.selectedAnswer !== null) {
|
|
e.preventDefault();
|
|
}
|
|
}
|
|
});
|
|
|
|
render();
|