| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527 |
- <!DOCTYPE html>
- <html lang="ru">
- <head>
- <meta charset="UTF-8">
- <meta name="viewport" content="width=device-width, initial-scale=1.0">
- <title>Агент Secure Print — игра по безопасности печати</title>
- <style>
- * {
- box-sizing: border-box;
- user-select: none;
- }
- body {
- font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
- background: linear-gradient(135deg, #1a2a3a, #0d1c2a);
- min-height: 100vh;
- display: flex;
- justify-content: center;
- align-items: center;
- margin: 0;
- padding: 20px;
- }
- .game-container {
- max-width: 800px;
- width: 100%;
- background: #f5f3ef;
- border-radius: 32px;
- box-shadow: 0 20px 40px rgba(0,0,0,0.4);
- overflow: hidden;
- }
- .scene {
- padding: 30px 25px;
- }
- .scene-header {
- display: flex;
- justify-content: space-between;
- align-items: baseline;
- border-bottom: 2px solid #d0b87a;
- margin-bottom: 25px;
- flex-wrap: wrap;
- }
- .scene-title {
- font-size: 1.8rem;
- font-weight: bold;
- color: #2c3e2f;
- }
- .score {
- background: #2c3e2f;
- color: #f9e0a8;
- padding: 6px 14px;
- border-radius: 40px;
- font-weight: bold;
- }
- .scene-content {
- min-height: 380px;
- }
- .question-block {
- background: white;
- border-radius: 24px;
- padding: 20px;
- margin-bottom: 20px;
- box-shadow: 0 4px 12px rgba(0,0,0,0.1);
- border: 1px solid #e0cfb0;
- }
- .question-text {
- font-weight: bold;
- font-size: 1.2rem;
- margin-bottom: 20px;
- color: #2c3e2f;
- background: #f0eadb;
- padding: 15px;
- border-radius: 16px;
- }
- .answers {
- display: flex;
- flex-direction: column;
- gap: 12px;
- }
- .answer-option {
- background: #f9f7f2;
- padding: 14px 18px;
- border-radius: 16px;
- cursor: pointer;
- border: 2px solid #e0cfb0;
- transition: all 0.1s;
- font-size: 1rem;
- }
- .answer-option:hover {
- background: #e8dfcc;
- border-color: #b98f4a;
- transform: scale(1.01);
- }
- .element {
- background: white;
- border-radius: 24px;
- padding: 20px;
- margin-bottom: 20px;
- box-shadow: 0 4px 12px rgba(0,0,0,0.1);
- cursor: pointer;
- border: 1px solid #e0cfb0;
- }
- .element:hover {
- background: #fffaf2;
- border-color: #b98f4a;
- }
- .element-title {
- font-weight: bold;
- font-size: 1.3rem;
- margin-bottom: 12px;
- color: #a45d2e;
- display: flex;
- align-items: center;
- gap: 12px;
- }
- .actions {
- margin-top: 20px;
- display: flex;
- flex-wrap: wrap;
- gap: 15px;
- }
- button {
- background: #2c5f2d;
- border: none;
- color: white;
- font-weight: bold;
- padding: 12px 24px;
- border-radius: 40px;
- font-size: 1rem;
- cursor: pointer;
- transition: 0.1s;
- }
- button:hover {
- background: #1f4520;
- transform: scale(0.97);
- }
- .message {
- background: #e9e3d5;
- border-left: 6px solid #b98f4a;
- padding: 12px;
- margin-top: 20px;
- border-radius: 16px;
- font-style: italic;
- }
- .game-over {
- background: #8b3c2c;
- color: white;
- text-align: center;
- padding: 30px;
- border-radius: 20px;
- }
- .win {
- background: #2c5f2d;
- }
- footer {
- background: #e0cfb0;
- padding: 12px;
- text-align: center;
- font-size: 0.8rem;
- color: #2c3e2f;
- }
- .feedback {
- margin-top: 15px;
- padding: 10px;
- border-radius: 12px;
- font-weight: bold;
- }
- .feedback-correct {
- background: #c8e6c9;
- color: #1b5e20;
- border-left: 4px solid #2e7d32;
- }
- .feedback-wrong {
- background: #ffcdd2;
- color: #c62828;
- border-left: 4px solid #d32f2f;
- }
- hr {
- margin: 15px 0;
- }
- </style>
- </head>
- <body>
- <div class="game-container" id="gameRoot"></div>
- <script>
- // СОСТОЯНИЕ ИГРЫ
- let currentScene = 1;
- let score = 0;
- let gameActive = true;
- let finalMessage = "";
- let waitingForNext = false;
- function render() {
- if (!gameActive) {
- const root = document.getElementById('gameRoot');
- root.innerHTML = `
- <div class="scene">
- <div class="game-over ${finalMessage.includes('ПОБЕДА') ? 'win' : ''}">
- <h2>🔐 ${finalMessage.includes('ПОБЕДА') ? 'ПОБЕДА!' : 'ИГРА ОКОНЧЕНА'}</h2>
- <p>${finalMessage}</p>
- <p><strong>Итоговый счёт безопасности: ${score}</strong></p>
- <button onclick="resetGame()">🔄 Начать заново</button>
- </div>
- </div>
- `;
- return;
- }
- const sceneData = getSceneData(currentScene);
- if (!sceneData) return;
- const root = document.getElementById('gameRoot');
-
- let contentHtml = '';
-
- if (sceneData.type === 'quiz') {
- // Формат с вариантами ответов (как в требовании вариант №1)
- contentHtml = `
- <div class="question-block">
- <div class="question-text">❓ ${sceneData.question}</div>
- <div class="answers" id="answersContainer">
- ${sceneData.answers.map((ans, idx) => `
- <div class="answer-option" data-answer-index="${idx}">
- ${ans.text}
- </div>
- `).join('')}
- </div>
- <div id="quizFeedback" class="feedback" style="display:none;"></div>
- </div>
- `;
- } else {
- // Обычная сцена с элементами
- let elementsHtml = '';
- for (let el of sceneData.elements || []) {
- elementsHtml += `
- <div class="element" data-element-id="${el.id}">
- <div class="element-title">
- <span>📄 ${el.title}</span>
- </div>
- <div class="element-desc">${el.desc}</div>
- </div>
- `;
- }
- contentHtml = elementsHtml;
- }
- let actionsHtml = '';
- for (let act of sceneData.actions || []) {
- actionsHtml += `<button data-action-id="${act.id}">${act.label}</button>`;
- }
- const html = `
- <div class="scene">
- <div class="scene-header">
- <div class="scene-title">${sceneData.title}</div>
- <div class="score">🔒 Безопасность: ${score}</div>
- </div>
- <div class="scene-content">
- ${contentHtml}
- ${actionsHtml ? `<div class="actions">${actionsHtml}</div>` : ''}
- <div class="message" id="sceneMessage">${sceneData.defaultMessage || 'Выберите действие...'}</div>
- </div>
- </div>
- <footer>🖨️ Агент Secure Print — защита конфиденциальной печати</footer>
- `;
- root.innerHTML = html;
- // Обработчики для элементов
- for (let el of sceneData.elements || []) {
- const domEl = document.querySelector(`.element[data-element-id="${el.id}"]`);
- if (domEl && el.onClick) {
- domEl.addEventListener('click', () => {
- if (gameActive && !waitingForNext) handleActionResult(el.onClick());
- });
- }
- }
- // Обработчики для кнопок
- for (let act of sceneData.actions || []) {
- const btn = document.querySelector(`button[data-action-id="${act.id}"]`);
- if (btn) {
- btn.addEventListener('click', () => {
- if (gameActive && !waitingForNext) handleActionResult(act.onClick());
- });
- }
- }
- // Обработчики для викторины (варианты ответов)
- if (sceneData.type === 'quiz') {
- const answersDivs = document.querySelectorAll('.answer-option');
- answersDivs.forEach((div, idx) => {
- div.addEventListener('click', () => {
- if (gameActive && !waitingForNext) {
- const answer = sceneData.answers[idx];
- const feedbackDiv = document.getElementById('quizFeedback');
- feedbackDiv.style.display = 'block';
-
- if (answer.correct) {
- feedbackDiv.innerHTML = `✅ Правильно! ${answer.feedback || ''}`;
- feedbackDiv.className = 'feedback feedback-correct';
- score += sceneData.pointsForCorrect || 25;
- document.querySelector('.score').innerHTML = `🔒 Безопасность: ${score}`;
-
- // Автоматический переход после правильного ответа
- waitingForNext = true;
- setTimeout(() => {
- if (sceneData.nextSceneOnCorrect) {
- currentScene = sceneData.nextSceneOnCorrect;
- waitingForNext = false;
- render();
- } else if (sceneData.nextScene) {
- currentScene = sceneData.nextScene;
- waitingForNext = false;
- render();
- }
- }, 1500);
- } else {
- feedbackDiv.innerHTML = `❌ Неправильно. ${answer.feedback || 'Попробуйте ещё раз!'}`;
- feedbackDiv.className = 'feedback feedback-wrong';
- if (sceneData.penaltyOnWrong) {
- score += sceneData.penaltyOnWrong;
- document.querySelector('.score').innerHTML = `🔒 Безопасность: ${score}`;
- }
- // Блокируем ответы на 1 секунду, но не переходим
- waitingForNext = true;
- setTimeout(() => {
- waitingForNext = false;
- feedbackDiv.style.display = 'none';
- }, 1200);
- }
- }
- });
- });
- }
- }
- function handleActionResult(result) {
- if (!result) return;
-
- if (result.message) {
- document.getElementById('sceneMessage').innerText = result.message;
- }
- if (result.scoreChange) {
- score += result.scoreChange;
- }
- if (result.nextScene) {
- currentScene = result.nextScene;
- render();
- }
- if (result.gameOver) {
- gameActive = false;
- finalMessage = result.gameOver;
- render();
- }
- if (!result.nextScene && !result.gameOver && result.scoreChange !== undefined) {
- // Обновляем только счёт без перехода
- render();
- }
- }
- function getSceneData(sceneId) {
- // СЦЕНА 1
- if (sceneId === 1) {
- return {
- title: '📥 Сцена 1: Приёмное отделение',
- type: 'default',
- elements: [
- {
- id: 'convent',
- title: '📨 Жёлтый конверт с грифом "Срочно"',
- desc: 'Сотрудник Иванов отправил на печать 200 страниц плана “Метеор”. Безопасно ли это?',
- }
- ],
- actions: [
- {
- id: 'allow',
- label: '✅ Разрешить печать',
- onClick: () => ({ gameOver: '❌ УТЕЧКА! Документы остались в памяти принтера. Вы проиграли.', scoreChange: -100 })
- },
- {
- id: 'check',
- label: '🔍 Запросить уточнение и проверить настройки',
- onClick: () => ({ message: 'Правильно! Нужно проверить безопасность.', nextScene: 2, scoreChange: 20 })
- }
- ],
- defaultMessage: 'Выберите действие: разрешить печать или запросить уточнение.'
- };
- }
- // СЦЕНА 2 - ПЕРЕДЕЛАНА В ВИКТОРИНУ С ВАРИАНТАМИ ОТВЕТОВ
- if (sceneId === 2) {
- return {
- title: '⚙️ Сцена 2: Настройка безопасной печати',
- type: 'quiz',
- question: 'Какое действие НЕОБХОДИМО выполнить перед печатью конфиденциального документа на сетевом принтере?',
- answers: [
- {
- text: '🔐 Включить функцию Secure Print (печать по PIN-коду)',
- correct: true,
- feedback: 'Верно! Secure Print требует ввода PIN-кода на самом принтере, документ не печатается, пока вы не подойдёте к устройству.'
- },
- {
- text: '📤 Отправить документ на печать и забрать через час',
- correct: false,
- feedback: 'Ошибка! Документ может быть перехвачен или прочитан посторонними. Забирать распечатку нужно сразу.'
- },
- {
- text: '💾 Сохранить документ в памяти принтера навсегда',
- correct: false,
- feedback: 'Неверно! Хранение конфиденциальных данных в памяти принтера — это прямой риск утечки.'
- },
- {
- text: '📧 Отправить документ на печать по электронной почте без шифрования',
- correct: false,
- feedback: 'Опасно! Отправка заданий печати по незащищённым каналам может привести к перехвату данных.'
- }
- ],
- pointsForCorrect: 30,
- penaltyOnWrong: -10,
- nextSceneOnCorrect: 3,
- defaultMessage: 'Выберите правильный вариант ответа.'
- };
- }
- // СЦЕНА 3: Конфликт в офисе
- if (sceneId === 3) {
- return {
- title: '🏢 Сцена 3: Конфликт в офисе',
- type: 'default',
- elements: [
- {
- id: 'colleague',
- title: '🧑💼 Взволнованный сотрудник с папкой "Секретно"',
- desc: '"Мне срочно нужно 5 копий, сисадмин занят! Можно напечатать на соседнем обычном принтере?"',
- }
- ],
- actions: [
- {
- id: 'bad',
- label: '⚠️ Печатать на незащищённом принтере',
- onClick: () => ({ gameOver: '📡 Перехват данных! Незащищённый принтер скомпрометирован. Игра окончена.', scoreChange: -999 })
- },
- {
- id: 'good',
- label: '⏳ Подождать и использовать безопасный принтер с Secure Print',
- onClick: () => ({ nextScene: 4, message: 'Правильный выбор! Конфиденциальность сохранена.', scoreChange: 50 })
- }
- ],
- defaultMessage: 'Выберите, как поступить в стрессовой ситуации.'
- };
- }
- // СЦЕНА 4: Списание старого МФУ
- if (sceneId === 4) {
- return {
- title: '🗑️ Сцена 4: Списание старого МФУ',
- type: 'default',
- elements: [
- {
- id: 'oldprinter',
- title: '🖨️ Старый принтер с табличкой "К списанию"',
- desc: 'Внутри жёсткий диск с остатками конфиденциальных документов.',
- }
- ],
- actions: [
- {
- id: 'destroySW',
- label: '💾 Запустить сертифицированную программу "Уничтожитель данных"',
- onClick: () => ({ nextScene: 5, message: 'Диск надёжно стёрт. Молодец!', scoreChange: 40 })
- },
- {
- id: 'destroyPhys',
- label: '🔨 Физически разбить диск и составить акт',
- onClick: () => ({ nextScene: 5, message: 'Физическое уничтожение зафиксировано. Безопасно.', scoreChange: 40 })
- },
- {
- id: 'throw',
- label: '🗑️ Просто выбросить принтер с диском',
- onClick: () => ({ gameOver: '💀 Утечка! Злоумышленник извлёк диск из мусора.', scoreChange: -100 })
- }
- ],
- defaultMessage: 'Выберите корректный способ уничтожения данных.'
- };
- }
- // СЦЕНА 5: Финальный аудит
- if (sceneId === 5) {
- return {
- title: '📋 Сцена 5: Финальный аудит',
- type: 'default',
- elements: [
- {
- id: 'logbook',
- title: '📜 Журнал печати за месяц',
- desc: 'Обнаружены 3 ночные печати по 500 страниц с компьютера Петрова.',
- }
- ],
- actions: [
- {
- id: 'warn',
- label: '📢 Выговор и обязательное обучение',
- onClick: () => ({ gameOver: '🏆 ПОБЕДА! Политика безопасности усилена. Игра пройдена!', scoreChange: 100 })
- },
- {
- id: 'fire',
- label: '⚡ Немедленное увольнение без разбора',
- onClick: () => ({ gameOver: '😐 Нейтральный финал: безопасность в порядке, но моральный климат упал.', scoreChange: 30 })
- }
- ],
- defaultMessage: 'Выберите меру воздействия на нарушителя.'
- };
- }
- return null;
- }
- function resetGame() {
- currentScene = 1;
- score = 0;
- gameActive = true;
- finalMessage = "";
- waitingForNext = false;
- render();
- }
- render();
- </script>
- </body>
- </html>
|