Files
sigra/templates/index.html

932 lines
29 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<!DOCTYPE html>
<html lang="ru">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Уроки французского - Своя игра</title>
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Playfair+Display:wght@400;700&family=Source+Sans+Pro:wght@400;600&display=swap" rel="stylesheet">
<style>
:root {
--bg-primary: #1a1a2e;
--bg-secondary: #16213e;
--surface: #0f3460;
--primary: #e94560;
--text: #eaeaea;
--gold: #ffd700;
--success: #4ade80;
--error: #f87171;
}
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family: 'Source Sans Pro', sans-serif;
background: var(--bg-primary);
color: var(--text);
min-height: 100vh;
background-image:
radial-gradient(ellipse at top, #1a1a2e 0%, #0f0f1a 100%),
repeating-linear-gradient(45deg, transparent, transparent 10px, rgba(233, 69, 96, 0.03) 10px, rgba(233, 69, 96, 0.03) 20px);
}
header {
text-align: center;
padding: 2rem 1rem;
background: linear-gradient(180deg, var(--bg-secondary) 0%, transparent 100%);
}
h1 {
font-family: 'Playfair Display', serif;
font-size: 2.5rem;
color: var(--primary);
text-shadow: 0 0 30px rgba(233, 69, 96, 0.5);
margin-bottom: 0.5rem;
}
.subtitle {
font-size: 1.1rem;
color: rgba(234, 234, 234, 0.7);
font-style: italic;
}
.score-panel {
display: flex;
justify-content: center;
gap: 2rem;
margin: 1.5rem 0;
flex-wrap: wrap;
}
.score-item {
background: var(--surface);
padding: 0.75rem 2rem;
border-radius: 50px;
font-size: 1.2rem;
box-shadow: 0 4px 20px rgba(0, 0, 0, 0.3);
border: 1px solid rgba(233, 69, 96, 0.3);
}
.score-value {
color: var(--gold);
font-weight: 600;
margin-left: 0.5rem;
}
.restart-btn {
background: var(--primary);
color: white;
border: none;
padding: 0.5rem 1rem;
border-radius: 25px;
cursor: pointer;
font-size: 0.9rem;
transition: all 0.3s ease;
}
.restart-btn:hover {
background: #c73e54;
transform: scale(1.05);
}
.team-setup {
text-align: center;
margin: 1rem 0;
}
.team-setup select {
background: var(--surface);
color: var(--text);
padding: 0.5rem;
border: 1px solid var(--primary);
border-radius: 4px;
margin-left: 0.5rem;
}
.team-setup select option {
background: var(--surface);
}
.team-names {
margin: 1rem 0;
}
.team-names input {
margin: 1rem 0;
}
.team-names input {
background: var(--surface);
color: var(--text);
padding: 0.5rem;
border: 1px solid var(--primary);
border-radius: 4px;
margin: 0 0.5rem;
width: 120px;
}
.start-btn {
background: var(--success);
color: var(--bg-primary);
border: none;
padding: 0.5rem 1.5rem;
border-radius: 25px;
cursor: pointer;
font-size: 1rem;
font-weight: 600;
margin-left: 1rem;
}
.start-btn:hover {
transform: scale(1.05);
}
.score-panel {
display: flex;
justify-content: center;
gap: 1rem;
margin: 1.5rem 0;
flex-wrap: wrap;
}
.score-item {
background: var(--surface);
padding: 0.75rem 1.5rem;
border-radius: 50px;
font-size: 1.2rem;
box-shadow: 0 4px 20px rgba(0, 0, 0, 0.3);
border: 1px solid rgba(233, 69, 96, 0.3);
display: flex;
align-items: center;
gap: 0.5rem;
}
.team-active {
border-color: var(--gold);
box-shadow: 0 0 15px rgba(255, 215, 0, 0.5);
}
.team-selector {
display: flex;
gap: 0.5rem;
justify-content: center;
margin-bottom: 1rem;
}
.team-btn {
padding: 0.5rem 1rem;
border-radius: 20px;
border: 2px solid var(--primary);
background: var(--surface);
color: var(--text);
cursor: pointer;
font-weight: 600;
}
.team-btn.active {
background: var(--primary);
color: white;
}
.team-btn:hover {
background: var(--bg-secondary);
}
.game-container {
max-width: 1400px;
margin: 0 auto;
padding: 1rem;
overflow-x: auto;
}
.game-board {
display: grid;
grid-template-columns: repeat(5, 1fr);
column-gap: 0.5rem;
row-gap: 0.5rem;
max-width: 1000px;
margin: 0 auto;
}
.category-header {
background: linear-gradient(135deg, var(--primary) 0%, #c73e54 100%);
color: white;
padding: 0.5rem;
text-align: center;
font-family: 'Playfair Display', serif;
font-weight: 700;
font-size: 1rem;
border-radius: 8px 8px 0 0;
min-height: 80px;
display: flex;
align-items: center;
justify-content: center;
box-shadow: 0 4px 15px rgba(233, 69, 96, 0.4);
}
.question-cell {
background: var(--surface);
aspect-ratio: 1.4;
display: flex;
align-items: center;
justify-content: center;
font-size: 1rem;
font-weight: 600;
color: var(--gold);
cursor: pointer;
transition: all 0.3s ease;
border: 1px solid rgba(233, 69, 96, 0.2);
border-radius: 4px;
min-height: 50px;
padding: 0.25rem;
}
.question-cell:hover:not(.answered) {
transform: scale(1.05);
background: var(--bg-secondary);
box-shadow: 0 0 25px rgba(233, 69, 96, 0.4);
border-color: var(--primary);
}
.question-cell.answered {
background: rgba(15, 52, 96, 0.5);
color: rgba(234, 234, 234, 0.3);
cursor: not-allowed;
border-color: transparent;
}
.modal-overlay {
display: none;
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: rgba(0, 0, 0, 0.85);
z-index: 1000;
justify-content: center;
align-items: center;
animation: fadeIn 0.3s ease;
}
.modal-overlay.active {
display: flex;
}
@keyframes fadeIn {
from { opacity: 0; }
to { opacity: 1; }
}
.modal {
background: linear-gradient(145deg, var(--bg-secondary) 0%, var(--surface) 100%);
border-radius: 20px;
padding: 2.5rem;
max-width: 600px;
width: 90%;
box-shadow: 0 20px 60px rgba(0, 0, 0, 0.5), 0 0 40px rgba(233, 69, 96, 0.2);
border: 1px solid rgba(233, 69, 96, 0.3);
animation: slideUp 0.4s ease;
}
@keyframes slideUp {
from { transform: translateY(30px); opacity: 0; }
to { transform: translateY(0); opacity: 1; }
}
.modal h2 {
font-family: 'Playfair Display', serif;
color: var(--primary);
margin-bottom: 1.5rem;
font-size: 1.5rem;
}
.modal-cost {
position: absolute;
top: -15px;
right: 20px;
background: var(--gold);
color: var(--bg-primary);
padding: 0.5rem 1rem;
border-radius: 20px;
font-weight: 700;
box-shadow: 0 4px 15px rgba(255, 215, 0, 0.4);
}
.modal-body {
position: relative;
}
.question-text {
font-size: 1.2rem;
margin-bottom: 2rem;
line-height: 1.6;
}
.options {
display: grid;
gap: 1rem;
}
.option {
background: var(--bg-primary);
padding: 1rem 1.5rem;
border-radius: 10px;
cursor: pointer;
transition: all 0.3s ease;
border: 2px solid transparent;
font-size: 1rem;
}
.option:hover {
border-color: var(--primary);
transform: translateX(5px);
}
.option.correct {
background: var(--success);
color: var(--bg-primary);
border-color: var(--success);
}
.option.wrong {
background: var(--error);
color: var(--bg-primary);
border-color: var(--error);
}
.result-message {
text-align: center;
padding: 1.5rem;
border-radius: 10px;
margin-top: 1.5rem;
font-weight: 600;
display: none;
}
.result-message.show {
display: block;
animation: popIn 0.3s ease;
}
.result-message.success {
background: rgba(74, 222, 128, 0.2);
color: var(--success);
border: 1px solid var(--success);
}
.result-message.error {
background: rgba(248, 113, 113, 0.2);
color: var(--error);
border: 1px solid var(--error);
}
@keyframes popIn {
from { transform: scale(0.8); opacity: 0; }
to { transform: scale(1); opacity: 1; }
}
.close-btn {
display: none;
margin: 1.5rem auto 0;
padding: 0.75rem 2rem;
background: var(--primary);
color: white;
border: none;
border-radius: 25px;
cursor: pointer;
font-size: 1rem;
transition: all 0.3s ease;
}
.close-btn.show {
display: block;
}
.close-btn:hover {
transform: scale(1.05);
box-shadow: 0 4px 20px rgba(233, 69, 96, 0.4);
}
.final-screen {
display: none;
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: var(--bg-primary);
z-index: 2000;
justify-content: center;
align-items: center;
flex-direction: column;
}
.final-screen.active {
display: flex;
animation: fadeIn 0.5s ease;
}
.final-screen h2 {
font-family: 'Playfair Display', serif;
font-size: 3rem;
color: var(--gold);
margin-bottom: 1rem;
text-shadow: 0 0 30px rgba(255, 215, 0, 0.5);
}
.podium {
display: flex;
justify-content: center;
align-items: flex-end;
gap: 20px;
margin: 2rem 0;
min-height: 300px;
}
.podium-place {
display: flex;
flex-direction: column;
align-items: center;
width: 150px;
}
.podium-team {
background: var(--surface);
padding: 1rem;
border-radius: 10px;
text-align: center;
width: 100%;
border: 2px solid var(--primary);
}
.podium-team-name {
font-size: 1rem;
font-weight: bold;
margin-bottom: 0.5rem;
}
.podium-team-score {
font-size: 1.5rem;
color: var(--gold);
}
.podium-block {
background: linear-gradient(180deg, var(--primary) 0%, #c73e54 100%);
width: 100%;
border-radius: 8px 8px 0 0;
display: flex;
align-items: center;
justify-content: center;
font-size: 2rem;
font-weight: bold;
color: white;
}
.podium-1 .podium-block {
height: 150px;
background: linear-gradient(180deg, #ffd700 0%, #daa520 100%);
}
.podium-2 .podium-block {
height: 100px;
background: linear-gradient(180deg, #c0c0c0 0%, #a8a8a8 100%);
}
.podium-3 .podium-block {
height: 70px;
background: linear-gradient(180deg, #cd7f32 0%, #b87333 100%);
}
.podium-1 { order: 2; }
.podium-2 { order: 1; }
.podium-3 { order: 3; }
.all-teams {
display: flex;
flex-direction: column;
gap: 10px;
width: 100%;
max-width: 400px;
}
.team-result {
display: flex;
align-items: center;
justify-content: space-between;
background: var(--surface);
padding: 15px 20px;
border-radius: 10px;
border: 2px solid var(--primary);
}
.team-place {
font-size: 1.5rem;
width: 40px;
}
.team-name {
flex: 1;
font-size: 1.2rem;
font-weight: bold;
text-align: center;
}
.team-points {
font-size: 1.2rem;
color: var(--gold);
font-weight: bold;
}
.-gradient(180deg, var(--primary) 0%, #c73e54 100%);
width: 100%;
border-radius: 8px 8px 0 0;
display: flex;
align-items: center;
justify-content: center;
font-size: 2rem;
font-weight: bold;
color: white;
}
.podium-1 .podium-block {
height: 150px;
background: linear-gradient(180deg, #ffd700 0%, #daa520 100%);
}
.podium-2 .podium-block {
height: 100px;
background: linear-gradient(180deg, #c0c0c0 0%, #a8a8a8 100%);
}
.podium-3 .podium-block {
height: 70px;
background: linear-gradient(180deg, #cd7f32 0%, #b87333 100%);
}
.podium-1 .podium-block {
order: 2;
}
.podium-2 .podium-block {
order: 1;
}
.podium-3 .podium-block {
order: 3;
}
.play-again-btn {
padding: 1rem 3rem;
background: var(--primary);
color: white;
border: none;
border-radius: 50px;
font-size: 1.2rem;
cursor: pointer;
transition: all 0.3s ease;
margin-top: 2rem;
}
.play-again-btn:hover {
transform: scale(1.1);
box-shadow: 0 10px 30px rgba(233, 69, 96, 0.5);
}
@media (max-width: 768px) {
h1 { font-size: 1.8rem; }
.category-header { font-size: 0.85rem; min-height: 60px; padding: 0.5rem; }
.question-cell { font-size: 1.1rem; min-height: 50px; }
.modal { padding: 1.5rem; }
.question-text { font-size: 1rem; }
}
</style>
</head>
<body>
<header>
<h1>Своя игра</h1>
<p class="subtitle">Рассказ В.Г. Распутина «Уроки французского»</p>
<div class="team-setup" id="teamSetup">
<label>Количество команд:
<select id="teamCount" onchange="setupTeams()">
<option value="1">1 команда</option>
<option value="2">2 команды</option>
<option value="3">3 команды</option>
</select>
</label>
<div id="teamNames"></div>
<button class="start-btn" id="startBtn" onclick="startGame()">Начать игру</button>
</div>
<div class="score-panel" id="scorePanel">
<div class="team-selector" id="teamSelector"></div>
<div class="score-item">Вопросов: <span class="score-value" id="answered">0</span>/<span id="totalQuestions">30</span></div>
<button class="restart-btn" onclick="if(confirm('Вы точно хотите начать заново?')) restartGame()">Начать заново</button>
</div>
</header>
<div class="game-container">
<div class="game-board" id="gameBoard">
{% for cat_idx, category in enumerate(questions.categories) %}
<div>
<div class="category-header">{{ category.name }}</div>
{% for q_idx, q in enumerate(category.questions) %}
<div class="question-cell"
data-cat="{{ cat_idx }}"
data-q="{{ q_idx }}">
{{ q.cost }}
</div>
{% endfor %}
</div>
{% endfor %}
</div>
</div>
<div class="modal-overlay" id="modalOverlay">
<div class="modal">
<div class="modal-body">
<span class="modal-cost" id="modalCost">100</span>
<h2>Вопрос</h2>
<p class="question-text" id="questionText"></p>
<div class="options" id="options"></div>
<div class="result-message" id="resultMessage"></div>
<button class="close-btn" id="closeBtn">Продолжить</button>
</div>
</div>
</div>
<div class="final-screen" id="finalScreen">
<h2>Игра окончена!</h2>
<div class="podium" id="podium"></div>
<button class="play-again-btn" onclick="location.reload()">Играть снова</button>
</div>
<script>
var allQuestions = {{ questions | tojson }};
var STORAGE_KEY = 'francuzskiy_game';
var currentTeam = 0;
var teams = [];
setupTeams();
function setupTeams() {
var count = parseInt(document.getElementById('teamCount').value);
var container = document.getElementById('teamNames');
container.innerHTML = '';
for (var i = 0; i < count; i++) {
var input = document.createElement('input');
input.placeholder = 'Команда ' + (i + 1);
input.value = 'Команда ' + (i + 1);
input.id = 'teamName' + i;
container.appendChild(input);
}
}
function startGame() {
document.getElementById('startBtn').style.display = 'none';
var count = parseInt(document.getElementById('teamCount').value);
teams = [];
for (var i = 0; i < count; i++) {
teams.push({
name: document.getElementById('teamName' + i).value || 'Команда ' + (i + 1),
score: 0
});
}
var totalQuestions = 30;
document.getElementById('totalQuestions').textContent = totalQuestions;
currentTeam = 0;
updateScoreDisplay();
initGame();
}
function selectTeam(index) {
currentTeam = index;
var btns = document.querySelectorAll('.team-btn');
for (var i = 0; i < btns.length; i++) {
btns[i].classList.toggle('active', i === index);
}
}
function updateScoreDisplay() {
var container = document.getElementById('teamSelector');
var html = '';
for (var i = 0; i < teams.length; i++) {
html += '<button class="team-btn' + (i === currentTeam ? ' active' : '') + '" onclick="selectTeam(' + i + ')">' + teams[i].name + ': <span id="score' + i + '">' + teams[i].score + '</span></button>';
}
container.innerHTML = html;
}
function loadGameState() {
var saved = localStorage.getItem(STORAGE_KEY);
if (saved) {
return JSON.parse(saved);
}
return { score: 0, answered: [], userAnswers: {} };
}
function saveGameState(state) {
localStorage.setItem(STORAGE_KEY, JSON.stringify(state));
}
function initGame() {
const state = loadGameState();
let answeredCount = 0;
state.answered.forEach(function(key) {
const [cat, q] = key.split('_');
const cell = document.querySelector('.question-cell[data-cat="' + cat + '"][data-q="' + q + '"]');
if (cell) {
cell.classList.add('answered');
answeredCount++;
}
});
document.getElementById('answered').textContent = answeredCount;
}
const modalOverlay = document.getElementById('modalOverlay');
const questionText = document.getElementById('questionText');
const optionsContainer = document.getElementById('options');
const modalCost = document.getElementById('modalCost');
const resultMessage = document.getElementById('resultMessage');
const answeredEl = document.getElementById('answered');
const finalScreen = document.getElementById('finalScreen');
const finalScore = document.getElementById('finalScore');
let currentCat = 0;
let currentQ = 0;
let correctAnswer = 0;
let currentCost = 0;
let canAnswer = true;
initGame();
var cells = document.querySelectorAll('.question-cell');
for (var i = 0; i < cells.length; i++) {
cells[i].onclick = function() {
var cat = parseInt(this.dataset.cat, 10);
var q = parseInt(this.dataset.q, 10);
var questionData = allQuestions.categories[cat].questions[q];
var isAnswered = this.classList.contains('answered');
openQuestionModal(cat, q, questionData.question, questionData.options, questionData.answer, questionData.cost, this, isAnswered);
};
}
function openQuestionModal(catIndex, qIndex, question, options, answer, cost, cell, viewOnly) {
currentCat = catIndex;
currentQ = qIndex;
correctAnswer = answer;
currentCost = cost;
questionText.textContent = question;
modalCost.textContent = cost;
optionsContainer.innerHTML = '';
var userAnswerData = null;
if (viewOnly) {
var state = loadGameState();
var key = catIndex + '_' + qIndex;
userAnswerData = state.userAnswers[key];
}
options.forEach(function(opt, i) {
var btn = document.createElement('div');
btn.className = 'option';
btn.textContent = opt;
if (viewOnly) {
btn.style.pointerEvents = 'none';
if (i === answer) {
btn.classList.add('correct');
}
if (userAnswerData && i === userAnswerData.answer && i !== answer) {
btn.classList.add('wrong');
}
} else {
btn.onclick = function() { selectAnswer(i, cell); };
}
optionsContainer.appendChild(btn);
});
resultMessage.className = 'result-message';
resultMessage.textContent = '';
if (viewOnly) {
resultMessage.textContent = 'Этот вопрос уже отыгран';
resultMessage.className = 'result-message show';
}
var closeBtnEl = document.getElementById('closeBtn');
closeBtnEl.className = 'close-btn show';
canAnswer = !viewOnly;
modalOverlay.classList.add('active');
}
function selectAnswer(index, cell) {
if (!canAnswer) return;
canAnswer = false;
const options = document.querySelectorAll('.option');
options.forEach((opt, i) => {
opt.style.pointerEvents = 'none';
if (i === correctAnswer) opt.classList.add('correct');
});
const isCorrect = index === correctAnswer;
if (!isCorrect) {
options[index].classList.add('wrong');
}
const state = loadGameState();
const key = `${currentCat}_${currentQ}`;
if (!state.answered.includes(key)) {
state.answered.push(key);
}
state.userAnswers[key] = { team: currentTeam, answer: index };
if (isCorrect) {
teams[currentTeam].score += currentCost;
resultMessage.textContent = 'Правильно! ' + teams[currentTeam].name + ' получает +' + currentCost + ' очков';
} else {
teams[currentTeam].score -= currentCost;
resultMessage.textContent = 'Неправильно! ' + teams[currentTeam].name + ' теряет -' + currentCost + ' очков';
}
saveGameState(state);
updateScoreDisplay();
answeredEl.textContent = state.answered.length;
if (teams.length > 1) {
currentTeam = (currentTeam + 1) % teams.length;
selectTeam(currentTeam);
}
resultMessage.className = 'result-message show ' + (isCorrect ? 'success' : 'error');
cell.classList.add('answered');
var closeBtnEl = document.getElementById('closeBtn');
closeBtnEl.classList.add('show');
setTimeout(function() { checkGameEnd(); }, 500);
}
document.getElementById('closeBtn').onclick = function() {
modalOverlay.classList.remove('active');
};
function checkGameEnd() {
var totalQuestions = parseInt(document.getElementById('totalQuestions').textContent);
var state = loadGameState();
if (state.answered.length >= totalQuestions) {
var sortedTeams = teams.slice().sort(function(a, b) { return b.score - a.score; });
var podiumHtml = '<div class="all-teams">';
for (var i = 0; i < sortedTeams.length; i++) {
var place = i + 1;
var medal = '';
if (place === 1) medal = '🥇';
else if (place === 2) medal = '🥈';
else if (place === 3) medal = '🥉';
else medal = place + '.';
podiumHtml += '<div class="team-result">';
podiumHtml += '<span class="team-place">' + medal + '</span>';
podiumHtml += '<span class="team-name">' + sortedTeams[i].name + '</span>';
podiumHtml += '<span class="team-points">' + sortedTeams[i].score + ' очков</span>';
podiumHtml += '</div>';
}
podiumHtml += '</div>';
document.getElementById('podium').innerHTML = podiumHtml;
setTimeout(function() {
finalScreen.classList.add('active');
}, 500);
}
}
document.querySelector('.play-again-btn').onclick = function() {
localStorage.removeItem(STORAGE_KEY);
location.reload();
};
function restartGame() {
localStorage.removeItem(STORAGE_KEY);
location.reload();
}
</script>
</body>
</html>