v15 — игры в чате: крестики-нолики, угадай число, реакция, спорт-викторина
This commit is contained in:
parent
0b1b216715
commit
6e31752125
190
index.html
190
index.html
@ -105,6 +105,19 @@ th{color:var(--gray-500);font-size:11px;text-transform:uppercase}
|
||||
.chat-tab{flex:1;padding:8px;border-radius:10px;border:none;font-size:13px;font-weight:600;cursor:pointer;background:#1a2332;color:var(--gray-500);transition:all .2s}
|
||||
.chat-tab.active{background:var(--cyan);color:var(--ink)}
|
||||
|
||||
|
||||
.game-grid{display:grid;grid-template-columns:repeat(3,1fr);gap:4px;width:180px;margin:12px auto}
|
||||
.game-cell{aspect-ratio:1;background:#1a2332;border-radius:8px;display:flex;align-items:center;justify-content:center;font-size:32px;font-weight:800;cursor:pointer;transition:background .2s;border:2px solid #2a3342}
|
||||
.game-cell:active{background:#2a3342}
|
||||
.game-cell.x{color:var(--cyan)}
|
||||
.game-cell.o{color:var(--red)}
|
||||
.game-cell.disabled{pointer-events:none}
|
||||
.game-status{text-align:center;font-size:14px;font-weight:600;margin:8px 0}
|
||||
.quiz-option{display:block;width:100%;padding:12px;margin:6px 0;background:#1a2332;border:2px solid #2a3342;border-radius:10px;color:var(--white);font-size:14px;cursor:pointer;text-align:left;transition:all .2s}
|
||||
.quiz-option:active{background:#2a3342}
|
||||
.quiz-option.right{background:var(--green);border-color:var(--green);color:#fff}
|
||||
.quiz-option.wrong{background:var(--red);border-color:var(--red);color:#fff}
|
||||
|
||||
.empty-state{text-align:center;padding:40px 20px;color:var(--gray-500)}
|
||||
.empty-state .big{font-size:48px;margin-bottom:8px}
|
||||
|
||||
@ -476,7 +489,10 @@ function renderChatList(){
|
||||
const other=users.filter(u=>u.id!==uid());
|
||||
const msgs=LS('messages')||{};
|
||||
let h='<div class="card"><div class="chat-tabs"><button class="chat-tab active" onclick="chatFilter=\'all\';renderPage()">💬 Все</button><button class="chat-tab" onclick="chatFilter=\'athlete\';renderPage()">🏊 Спортсмены</button><button class="chat-tab" onclick="chatFilter=\'coach\';renderPage()">🏋 Тренеры</button><button class="chat-tab" onclick="chatFilter=\'parent\';renderPage()">👨‍👦 Родители</button></div>';
|
||||
// Show create group chat button
|
||||
// Games button
|
||||
h+='<button class="btn outline" onclick="renderGames()" style="width:100%;margin-bottom:8px">🎮 Игры</button>';
|
||||
Show create group chat button
|
||||
|
||||
h+='<button class="btn small outline" onclick="createGroupChat()" style="width:100%;margin-bottom:8px">+ Создать групповой чат</button>';
|
||||
if(!other.length){h+='<div class="empty-state"><div class="big">💬</div>Нет других пользователей</div></div>';return h}
|
||||
h+='<div style="font-size:13px;color:var(--gray-500);margin-bottom:6px">Выбери с кем общаться:</div>';
|
||||
@ -529,6 +545,178 @@ function sendMsg(){
|
||||
// scroll to bottom
|
||||
setTimeout(()=>{const el=document.getElementById('chatMsgs');if(el)el.scrollTop=el.scrollHeight},100);
|
||||
}
|
||||
let gameState = null;
|
||||
|
||||
function renderGames(){
|
||||
const c = document.getElementById('mainContent');
|
||||
c.innerHTML = `
|
||||
<div class="card" style="text-align:center">
|
||||
<h3>🎮 Игры в чате</h3>
|
||||
<p class="muted">Выбери игру и играй с друзьями!</p>
|
||||
<button class="quiz-option" onclick="startTicTacToe()">❌⭕ Крестики-нолики</button>
|
||||
<button class="quiz-option" onclick="startGuessNumber()">🔢 Угадай число (1-100)</button>
|
||||
<button class="quiz-option" onclick="startReaction()">⚡ Реакция — кто быстрее</button>
|
||||
<button class="quiz-option" onclick="startQuiz()">🏆 Спорт-викторина</button>
|
||||
<button class="btn small outline" onclick="showPage('chat')" style="margin-top:8px">← Назад в чаты</button>
|
||||
</div>
|
||||
<div id="gameArea"></div>
|
||||
`;
|
||||
}
|
||||
|
||||
function startTicTacToe(){
|
||||
gameState = { type: 'tictactoe', board: Array(9).fill(''), turn: 'X', over: false };
|
||||
renderTicTacToe();
|
||||
}
|
||||
function renderTicTacToe(){
|
||||
const g = gameState;
|
||||
let h = '<div class="card"><h3 style="text-align:center">❌⭕ Крестики-нолики</h3><div class="game-status">';
|
||||
if(g.over){
|
||||
h += g.winner === 'draw' ? 'Ничья!' : g.winner + ' победил!';
|
||||
h += '<br><button class="btn small outline" onclick="startTicTacToe()" style="margin-top:8px">Играть ещё</button>';
|
||||
} else {
|
||||
h += 'Ход: ' + (g.turn === 'X' ? '❌ Крестики' : '⭕ Нолики');
|
||||
}
|
||||
h += '</div><div class="game-grid">';
|
||||
g.board.forEach((cell, i) => {
|
||||
h += `<div class="game-cell${cell?' '+ (cell==='X'?'x':'o')+(g.over||cell?' disabled':'') :''}" onclick="ticMove(${i})">${cell}</div>`;
|
||||
});
|
||||
h += '</div><button class="btn small outline" onclick="startTicTacToe()" style="width:100%;margin-top:8px">Новая игра</button></div>';
|
||||
document.getElementById('gameArea').innerHTML = h;
|
||||
}
|
||||
function ticMove(i){
|
||||
if(!gameState||gameState.over||gameState.board[i])return;
|
||||
gameState.board[i] = gameState.turn;
|
||||
const lines = [[0,1,2],[3,4,5],[6,7,8],[0,3,6],[1,4,7],[2,5,8],[0,4,8],[2,4,6]];
|
||||
for(const [a,b,c] of lines){
|
||||
if(gameState.board[a]&&gameState.board[a]===gameState.board[b]&&gameState.board[a]===gameState.board[c]){
|
||||
gameState.over = true; gameState.winner = gameState.board[a]; break;
|
||||
}
|
||||
}
|
||||
if(!gameState.over && gameState.board.every(c=>c)){ gameState.over = true; gameState.winner = 'draw'; }
|
||||
if(!gameState.over) gameState.turn = gameState.turn === 'X' ? 'O' : 'X';
|
||||
renderTicTacToe();
|
||||
}
|
||||
|
||||
function startGuessNumber(){
|
||||
gameState = { type: 'guess', number: Math.floor(Math.random()*100)+1, attempts: 0, max: 7 };
|
||||
renderGuessNumber();
|
||||
}
|
||||
function renderGuessNumber(){
|
||||
const g = gameState;
|
||||
let h = `<div class="card" style="text-align:center"><h3>🔢 Угадай число (1-100)</h3>
|
||||
<p class="muted">Осталось попыток: <strong>${g.max - g.attempts}</strong></p>`;
|
||||
if(g.won){
|
||||
h += `<p style="color:var(--green);font-size:20px;font-weight:800">🎉 Угадал! Это ${g.number}!</p><p class="muted">За ${g.attempts} попыток</p>`;
|
||||
h += '<button class="btn small outline" onclick="startGuessNumber()">Играть ещё</button>';
|
||||
} else if(g.attempts >= g.max){
|
||||
h += `<p style="color:var(--red);font-size:18px;font-weight:800">Не угадал! Это было ${g.number}</p>`;
|
||||
h += '<button class="btn small outline" onclick="startGuessNumber()">Играть ещё</button>';
|
||||
} else {
|
||||
h += '<input type="number" id="guessInput" placeholder="Твоё число" min="1" max="100" onkeydown="if(event.key==='Enter')doGuess()"><button class="btn" onclick="doGuess()" style="margin-top:8px">Проверить</button>';
|
||||
}
|
||||
h += '</div><button class="btn small outline" onclick="renderGames()" style="width:100%">← К играм</button>';
|
||||
document.getElementById('gameArea').innerHTML = h;
|
||||
}
|
||||
function doGuess(){
|
||||
if(!gameState||gameState.won||gameState.attempts>=gameState.max)return;
|
||||
const n = +document.getElementById('guessInput').value;
|
||||
if(!n||n<1||n>100)return;
|
||||
gameState.attempts++;
|
||||
if(n === gameState.number){ gameState.won = true; renderGuessNumber(); return }
|
||||
const hint = n < gameState.number ? '📈 Больше!' : '📉 Меньше!';
|
||||
const tempDiv = document.createElement('div'); tempDiv.innerHTML = `<div class="card" style="text-align:center;padding:12px;margin-bottom:8px"><p>${n} — ${hint}</p></div>`;
|
||||
document.getElementById('gameArea').insertBefore(tempDiv, document.getElementById('gameArea').firstChild);
|
||||
if(gameState.attempts >= gameState.max) renderGuessNumber();
|
||||
else { document.getElementById('guessInput').value=''; document.getElementById('guessInput').focus(); }
|
||||
}
|
||||
|
||||
function startReaction(){
|
||||
gameState = { type: 'reaction', state: 'waiting', startTime: 0, best: getMy('reactionBest') || 9999 };
|
||||
renderReaction();
|
||||
}
|
||||
function renderReaction(){
|
||||
const g = gameState;
|
||||
let bg = '#151c28', txt = '';
|
||||
if(g.state === 'waiting'){ bg = '#FF6B6B'; txt = '🔴 Жди зелёный!'; }
|
||||
else if(g.state === 'ready'){ bg = '#4CAF50'; txt = '🟢 ЖМИ СЕЙЧАС!'; }
|
||||
else if(g.state === 'early'){ bg = '#FFD700'; txt = '⚠ Слишком рано! Жми "Начать"'; }
|
||||
else if(g.state === 'done'){ txt = `⏱ Твоё время: <strong>${g.reaction} мс</strong><br>Рекорд: ${g.best !== 9999 ? g.best+' мс' : '—'}`; }
|
||||
let h = `<div class="card" style="text-align:center">
|
||||
<h3>⚡ Реакция</h3>
|
||||
<div style="width:200px;height:200px;border-radius:50%;background:${bg};display:flex;align-items:center;justify-content:center;margin:16px auto;cursor:${g.state==='ready'?'pointer':'default'};font-size:18px;font-weight:800;transition:background .1s" onclick="reactClick()">${txt||'🎯 Нажми "Начать"'}</div>`;
|
||||
if(g.state === 'done'){
|
||||
h += '<button class="btn small outline" onclick="startReaction()">Ещё раз</button>';
|
||||
} else if(g.state === 'waiting' || g.state === 'early'){
|
||||
h += '<button class="btn" onclick="reactStart()">Начать</button>';
|
||||
}
|
||||
h += '</div><button class="btn small outline" onclick="renderGames()" style="width:100%">← К играм</button>';
|
||||
document.getElementById('gameArea').innerHTML = h;
|
||||
}
|
||||
function reactStart(){
|
||||
if(!gameState||gameState.state==='ready')return;
|
||||
gameState.state = 'waiting'; renderReaction();
|
||||
const delay = 1500 + Math.random() * 3000;
|
||||
setTimeout(() => {
|
||||
if(gameState.state !== 'waiting') return;
|
||||
gameState.state = 'ready'; gameState.startTime = Date.now(); renderReaction();
|
||||
}, delay);
|
||||
}
|
||||
function reactClick(){
|
||||
if(!gameState)return;
|
||||
if(gameState.state === 'waiting'){ gameState.state = 'early'; renderReaction(); return; }
|
||||
if(gameState.state === 'ready'){ gameState.reaction = Date.now() - gameState.startTime; gameState.state = 'done';
|
||||
if(gameState.reaction < gameState.best){ gameState.best = gameState.reaction; setMy('reactionBest', gameState.reaction); }
|
||||
renderReaction(); }
|
||||
}
|
||||
|
||||
function startQuiz(){
|
||||
const questions = [
|
||||
{q:'Сколько метров в олимпийском бассейне?',a:['25 м','50 м','100 м','33 м'],r:1},
|
||||
{q:'Какой стиль плавания самый быстрый?',a:['Брасс','Баттерфляй','Кроль','На спине'],r:2},
|
||||
{q:'Сколько золотых медалей у Майкла Фелпса?',a:['8','15','23','28'],r:2},
|
||||
{q:'Как зовут лучшего спринтера мира (50 м в/с)?',a:['Майкл Фелпс','Калеб Дрессел','Райан Лохте','Адам Пити'],r:1},
|
||||
{q:'Где пройдёт Олимпиада-2032?',a:['Лос-Анджелес','Париж','Брисбен','Токио'],r:2},
|
||||
{q:'Сколько длится олимпийский цикл?',a:['2 года','3 года','4 года','5 лет'],r:2},
|
||||
{q:'Какая страна выиграла больше всех медалей в плавании?',a:['Китай','Австралия','Россия','США'],r:3},
|
||||
{q:'Как называется поворот в кроле?',a:['Сальто','Кувырок','Разворот','Маятник'],r:1}
|
||||
];
|
||||
gameState = { type: 'quiz', questions, current: 0, score: 0, answered: false };
|
||||
renderQuiz();
|
||||
}
|
||||
function renderQuiz(){
|
||||
const g = gameState;
|
||||
if(g.current >= g.questions.length){
|
||||
document.getElementById('gameArea').innerHTML = `<div class="card" style="text-align:center"><h3>🏆 Викторина завершена!</h3><p style="font-size:24px;font-weight:800;color:var(--cyan)">${g.score} / ${g.questions.length}</p><p class="muted">${g.score>=6?'👑 Ты знаток спорта!':g.score>=4?'👍 Неплохо!':'📚 Учи матчасть!'}</p><button class="btn" onclick="startQuiz()">Ещё раз</button></div>`;
|
||||
return;
|
||||
}
|
||||
const q = g.questions[g.current];
|
||||
let h = `<div class="card"><h3>❓ Вопрос ${g.current+1}/${g.questions.length}</h3><p style="font-size:17px;font-weight:600;margin:12px 0">${q.q}</p>`;
|
||||
q.a.forEach((ans,i)=>{
|
||||
let cls = 'quiz-option';
|
||||
if(g.answered){
|
||||
if(i === q.r) cls += ' right';
|
||||
else if(g.selected === i) cls += ' wrong';
|
||||
}
|
||||
h += `<button class="${cls}" onclick="answerQuiz(${i})" ${g.answered?'disabled':''}>${ans}</button>`;
|
||||
});
|
||||
h += `<div style="margin-top:8px;font-size:13px;color:var(--gray-500)">Счёт: ${g.score}</div>`;
|
||||
if(g.answered){
|
||||
h += '<button class="btn small" onclick="nextQuiz()" style="margin-top:8px">Дальше →</button>';
|
||||
}
|
||||
h += '</div>';
|
||||
document.getElementById('gameArea').innerHTML = h;
|
||||
}
|
||||
function answerQuiz(i){
|
||||
if(!gameState||gameState.answered)return;
|
||||
gameState.answered = true; gameState.selected = i;
|
||||
if(i === gameState.questions[gameState.current].r) gameState.score++;
|
||||
renderQuiz();
|
||||
}
|
||||
function nextQuiz(){
|
||||
gameState.current++; gameState.answered = false; gameState.selected = null; renderQuiz();
|
||||
}
|
||||
|
||||
|
||||
function createGroupChat(){
|
||||
const name=prompt('Название группового чата:');
|
||||
if(!name)return;
|
||||
|
||||
Loading…
Reference in New Issue
Block a user