| <!DOCTYPE html> |
| <html lang="en"> |
| <head> |
| <meta charset="UTF-8"> |
| <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no"> |
| <title>Mini Among Us Enhanced</title> |
| <script src="https://cdn.tailwindcss.com"></script> |
| <style> |
| |
| * { |
| margin: 0; |
| padding: 0; |
| box-sizing: border-box; |
| touch-action: manipulation; |
| -webkit-tap-highlight-color: transparent; |
| } |
| |
| body { |
| font-family: 'Arial', sans-serif; |
| background-color: #0a0a1a; |
| color: white; |
| overflow: hidden; |
| height: 100vh; |
| width: 100vw; |
| position: relative; |
| } |
| |
| |
| #game-container { |
| position: relative; |
| width: 100%; |
| height: 100%; |
| overflow: hidden; |
| } |
| |
| |
| #game-map { |
| position: absolute; |
| width: 2500px; |
| height: 2500px; |
| background-color: #0d1b2a; |
| transform-origin: 0 0; |
| background-image: |
| linear-gradient(#4cc9f033 1px, transparent 1px), |
| linear-gradient(90deg, #4cc9f033 1px, transparent 1px); |
| background-size: 50px 50px; |
| } |
| |
| |
| .room { |
| position: absolute; |
| background-color: #1b263b; |
| border: 4px solid #415a77; |
| box-shadow: 0 0 15px rgba(65, 90, 119, 0.5); |
| } |
| |
| .room-wall { |
| position: absolute; |
| background-color: #415a77; |
| } |
| |
| .room-door { |
| position: absolute; |
| background-color: #778da9; |
| z-index: 5; |
| } |
| |
| .room-label { |
| position: absolute; |
| color: white; |
| font-size: 18px; |
| text-align: center; |
| width: 100%; |
| top: 15px; |
| pointer-events: none; |
| text-shadow: 1px 1px 3px rgba(0, 0, 0, 0.8); |
| font-weight: bold; |
| } |
| |
| |
| .player { |
| position: absolute; |
| width: 45px; |
| height: 45px; |
| background-color: #f72585; |
| border-radius: 50%; |
| border: 3px solid white; |
| z-index: 10; |
| transition: transform 0.15s ease; |
| } |
| |
| .player::after { |
| content: ''; |
| position: absolute; |
| width: 22px; |
| height: 17px; |
| background-color: white; |
| border-radius: 50%; |
| top: 9px; |
| left: 11px; |
| transform: rotate(10deg); |
| } |
| |
| .player-shadow { |
| position: absolute; |
| width: 45px; |
| height: 15px; |
| background-color: rgba(0, 0, 0, 0.3); |
| border-radius: 50%; |
| bottom: -5px; |
| left: 0; |
| z-index: 9; |
| } |
| |
| |
| .npc { |
| position: absolute; |
| width: 45px; |
| height: 45px; |
| border-radius: 50%; |
| border: 3px solid white; |
| z-index: 9; |
| } |
| |
| .npc::after { |
| content: ''; |
| position: absolute; |
| width: 22px; |
| height: 17px; |
| background-color: white; |
| border-radius: 50%; |
| top: 9px; |
| left: 11px; |
| transform: rotate(10deg); |
| } |
| |
| .npc-shadow { |
| position: absolute; |
| width: 45px; |
| height: 15px; |
| background-color: rgba(0, 0, 0, 0.3); |
| border-radius: 50%; |
| bottom: -5px; |
| left: 0; |
| z-index: 8; |
| } |
| |
| |
| .task { |
| position: absolute; |
| width: 35px; |
| height: 35px; |
| background-color: #f8f9fa; |
| border-radius: 8px; |
| display: flex; |
| justify-content: center; |
| align-items: center; |
| font-size: 22px; |
| cursor: pointer; |
| z-index: 8; |
| box-shadow: 0 0 15px rgba(248, 249, 250, 0.7); |
| transition: transform 0.2s ease; |
| } |
| |
| .task:hover, .task:active { |
| transform: scale(1.1); |
| } |
| |
| |
| #task-popup { |
| position: absolute; |
| top: 50%; |
| left: 50%; |
| transform: translate(-50%, -50%); |
| width: 85%; |
| max-width: 350px; |
| background-color: #1b263b; |
| border: 4px solid #415a77; |
| border-radius: 15px; |
| padding: 25px; |
| z-index: 100; |
| display: none; |
| text-align: center; |
| box-shadow: 0 0 30px rgba(65, 90, 119, 0.8); |
| } |
| |
| #task-popup h3 { |
| margin-bottom: 20px; |
| color: #f72585; |
| font-size: 22px; |
| } |
| |
| #task-popup p { |
| margin-bottom: 20px; |
| font-size: 16px; |
| line-height: 1.4; |
| } |
| |
| #task-progress { |
| width: 100%; |
| height: 20px; |
| background-color: #415a77; |
| border-radius: 10px; |
| margin: 15px 0; |
| overflow: hidden; |
| } |
| |
| #progress-bar { |
| height: 100%; |
| width: 0%; |
| background-color: #4cc9f0; |
| transition: width 0.3s ease; |
| } |
| |
| #task-popup button { |
| background-color: #4cc9f0; |
| color: white; |
| border: none; |
| padding: 12px 25px; |
| border-radius: 8px; |
| margin-top: 15px; |
| font-size: 18px; |
| cursor: pointer; |
| transition: background-color 0.2s ease; |
| font-weight: bold; |
| } |
| |
| #task-popup button:hover, #task-popup button:active { |
| background-color: #3aa8d8; |
| } |
| |
| |
| #controls { |
| position: absolute; |
| bottom: 30px; |
| left: 30px; |
| width: 140px; |
| height: 140px; |
| z-index: 20; |
| } |
| |
| .control-btn { |
| position: absolute; |
| width: 45px; |
| height: 45px; |
| background-color: rgba(255, 255, 255, 0.25); |
| border-radius: 50%; |
| display: flex; |
| justify-content: center; |
| align-items: center; |
| font-size: 24px; |
| color: white; |
| user-select: none; |
| transition: background-color 0.2s ease; |
| } |
| |
| .control-btn:active { |
| background-color: rgba(255, 255, 255, 0.4); |
| } |
| |
| #up-btn { top: 0; left: 47px; } |
| #left-btn { top: 47px; left: 0; } |
| #right-btn { top: 47px; right: 0; } |
| #down-btn { bottom: 0; left: 47px; } |
| |
| |
| #task-list { |
| position: absolute; |
| top: 25px; |
| right: 25px; |
| background-color: rgba(27, 38, 59, 0.9); |
| border: 3px solid #415a77; |
| border-radius: 12px; |
| padding: 15px; |
| max-width: 180px; |
| z-index: 20; |
| box-shadow: 0 0 20px rgba(0, 0, 0, 0.5); |
| } |
| |
| #task-list h3 { |
| font-size: 16px; |
| margin-bottom: 10px; |
| color: #f72585; |
| text-align: center; |
| } |
| |
| .task-item { |
| font-size: 14px; |
| margin-bottom: 8px; |
| display: flex; |
| align-items: center; |
| padding: 5px; |
| border-radius: 5px; |
| transition: background-color 0.2s ease; |
| } |
| |
| .task-item:hover { |
| background-color: rgba(65, 90, 119, 0.3); |
| } |
| |
| .task-item.completed { |
| text-decoration: line-through; |
| opacity: 0.6; |
| } |
| |
| .task-checkbox { |
| width: 15px; |
| height: 15px; |
| border: 2px solid white; |
| border-radius: 3px; |
| margin-right: 8px; |
| display: inline-block; |
| } |
| |
| .task-item.completed .task-checkbox { |
| background-color: #4cc9f0; |
| border-color: #4cc9f0; |
| } |
| |
| |
| #game-status { |
| position: absolute; |
| top: 25px; |
| left: 25px; |
| background-color: rgba(27, 38, 59, 0.9); |
| border: 3px solid #415a77; |
| border-radius: 12px; |
| padding: 15px; |
| z-index: 20; |
| box-shadow: 0 0 20px rgba(0, 0, 0, 0.5); |
| } |
| |
| #game-status h3 { |
| font-size: 16px; |
| margin-bottom: 10px; |
| color: #froyo85; |
| text-align: center; |
| } |
| |
| #tasks-completed { |
| font-size: 14px; |
| text-align: center; |
| } |
| |
| |
| #mini-map { |
| position: absolute; |
| bottom: 30px; |
| right: 30px; |
| width: 120px; |
| height: 120px; |
| background-color: rgba(13, 27, 42, 0.8); |
| border: 3px solid #415a77; |
| border-radius: 10px; |
| z-index: 20; |
| overflow: hidden; |
| box-shadow: 0 0 15px rgba(0, 0, 0, 0.5); |
| } |
| |
| #mini-map-view { |
| position: absolute; |
| width: 100%; |
| height: 100%; |
| transform-origin: 0 0; |
| } |
| |
| .mini-map-room { |
| position: absolute; |
| background-color: rgba(65, 90, 119, 0.5); |
| border: 1px solid #778da9; |
| } |
| |
| .mini-map-player { |
| position: absolute; |
| width: 6px; |
| height: 6px; |
| background-color: #f72585; |
| border-radius: 50%; |
| border: 1px solid white; |
| transform: translate(-3px, -3px); |
| z-index: 2; |
| } |
| |
| .mini-map-npc { |
| position: absolute; |
| width: 4px; |
| height: 4px; |
| background-color: #4cc9f0; |
| border-radius: 50%; |
| border: 1px solid white; |
| transform: translate(-2px, -2px); |
| z-index: 1; |
| } |
| |
| .mini-map-task { |
| position: absolute; |
| width: 4px; |
| height: 4px; |
| background-color: white; |
| border-radius: 50%; |
| transform: translate(-2px, -2px); |
| z-index: 1; |
| } |
| |
| |
| #win-screen { |
| position: absolute; |
| top: 0; |
| left: 0; |
| width: 100%; |
| height: 100%; |
| background-color: rgba(0, 0, 0, 0.9); |
| display: flex; |
| flex-direction: column; |
| justify-content: center; |
| align-items: center; |
| z-index: 200; |
| display: none; |
| } |
| |
| #win-screen h2 { |
| font-size: 36px; |
| color: #f72585; |
| margin-bottom: 30px; |
| text-align: center; |
| text-shadow: 0 0 10px rgba(247, 37, 133, 0.7); |
| } |
| |
| #win-screen p { |
| font-size: 18px; |
| margin-bottom: 30px; |
| text-align: center; |
| max-width: 80%; |
| } |
| |
| #win-screen button { |
| background-color: #4cc9f0; |
| color: white; |
| border: none; |
| padding: 18px 36px; |
| border-radius: 10px; |
| font-size: 20px; |
| cursor: pointer; |
| transition: all 0.3s ease; |
| font-weight: bold; |
| box-shadow: 0 0 20px rgba(76, 201, 240, 0.5); |
| } |
| |
| #win-screen button:hover, #win-screen button:active { |
| background-color: #3aa8d8; |
| transform: scale(1.05); |
| } |
| |
| |
| .particle { |
| position: absolute; |
| width: 3px; |
| height: 3px; |
| background-color: white; |
| border-radius: 50%; |
| pointer-events: none; |
| z-index: 7; |
| } |
| |
| |
| #emergency-btn { |
| position: absolute; |
| bottom: 30px; |
| right: 160px; |
| width: 60px; |
| height: 60px; |
| background-color: #f72585; |
| border-radius: 50%; |
| display: flex; |
| justify-content: center; |
| align-items: center; |
| font-size: 24px; |
| color: white; |
| z-index: 20; |
| box-shadow: 0 0 20px rgba(247, 37, 133, 0.5); |
| border: 3px solid white; |
| cursor: pointer; |
| transition: all 0.3s ease; |
| } |
| |
| #emergency-btn:active { |
| transform: scale(0.95); |
| box-shadow: 0 0 10px rgba(247, 37, 133, 0.5); |
| } |
| |
| |
| .tooltip { |
| position: absolute; |
| background-color: rgba(27, 38, 59, 0.9); |
| border: 2px solid #415a77; |
| border-radius: 8px; |
| padding: 8px 12px; |
| font-size: 14px; |
| z-index: 30; |
| pointer-events: none; |
| opacity: 0; |
| transition: opacity 0.3s ease; |
| max-width: 200px; |
| text-align: center; |
| } |
| |
| |
| #loading-screen { |
| position: absolute; |
| top: 0; |
| left: 0; |
| width: 100%; |
| height: 100%; |
| background-color: #0a0a1a; |
| display: flex; |
| flex-direction: column; |
| justify-content: center; |
| align-items: center; |
| z-index: 300; |
| } |
| |
| #loading-screen h1 { |
| font-size: 28px; |
| color: #f72585; |
| margin-bottom: 30px; |
| text-shadow: 0 0 10px rgba(247, 37, 133, 0.7); |
| } |
| |
| #loading-bar { |
| width: 80%; |
| max-width: 300px; |
| height: 20px; |
| background-color: #415a77; |
| border-radius: 10px; |
| overflow: hidden; |
| margin-bottom: 30px; |
| } |
| |
| #loading-progress { |
| height: 100%; |
| width: 0%; |
| ; |
| background-color: #4cc9f0; |
| transition: width 0.3s ease; |
| } |
| |
| #loading-text { |
| font-size: 16px; |
| color: #778da9; |
| } |
| |
| |
| @keyframes float { |
| 0%, 100% { transform: translateY(0); } |
| 50% { transform: translateY(-10px); } |
| } |
| |
| .floating { |
| animation: float 3s ease-in-out infinite; |
| } |
| |
| |
| #sabotage-menu { |
| position: absolute; |
| top: 50%; |
| left: 50%; |
| transform: translate(-50%, -50%); |
| width: 85%; |
| max-width: 350px; |
| background-color: #1b263b; |
| border: 4px solid #f72585; |
| border-radius: 15px; |
| padding: 25px; |
| z-index: 100; |
| display: none; |
| text-align: center; |
| box-shadow: 0 0 30px rgba(247, 37, 133, 0.8); |
| } |
| |
| #sabotage-menu h3 { |
| margin-bottom: 20px; |
| color: #f72585; |
| font-size: 22px; |
| } |
| |
| .sabotage-option { |
| background-color: #415a77; |
| color: white; |
| border: none; |
| padding: 12px; |
| border-radius: 8px; |
| margin: 8px 0; |
| font-size: 16px; |
| cursor: pointer; |
| transition: all 0.2s ease; |
| width: 100%; |
| } |
| |
| .sabotage-option:hover { |
| background-color: #4cc9f0; |
| transform: scale(1.02); |
| } |
| |
| |
| #countdown-timer { |
| position: absolute; |
| top: 25px; |
| left: 50%; |
| transform: translateX(-50%); |
| background-color: rgba(27, 38, 59, 0.9); |
| border: 3px solid #f72585; |
| border-radius: 12px; |
| padding: 10px 20px; |
| z-index: 20; |
| box-shadow: 0 0 20px rgba(247, 37, 133, 0.5); |
| font-size: 18px; |
| font-weight: bold; |
| color: white; |
| display: none; |
| } |
| |
| |
| #kill-btn { |
| position: absolute; |
| bottom: 30px; |
| right: 230px; |
| width: 60px; |
| height: 60px; |
| background-color: #f72585; |
| border-radius: 50%; |
| display: flex; |
| justify-content: center; |
| align-items: center; |
| font-size: 24px; |
| color: white; |
| z-index: 20; |
| box-shadow: 0 0 20px rgba(247, 37, 133, 0.5); |
| border: 3px solid white; |
| cursor: pointer; |
| transition: all 0.3s ease; |
| display: none; |
| } |
| |
| #kill-btn:active { |
| transform: scale(0.95); |
| box-shadow: 0 0 10px rgba(247, 37, 133, 0.5); |
| } |
| |
| |
| #vent-btn { |
| position: absolute; |
| bottom: 30px; |
| right: 300px; |
| width: 60px; |
| height: 60px; |
| background-color: #4cc9f0; |
| border-radius: 50%; |
| display: flex; |
| justify-content: center; |
| align-items: center; |
| font-size: 24px; |
| color: white; |
| z-index: 20; |
| box-shadow: 0 0 20px rgba(76, 201, 240, 0.5); |
| border: 3px solid white; |
| cursor: pointer; |
| transition: all 0.3s ease; |
| display: none; |
| } |
| |
| #vent-btn:active { |
| transform: scale(0.95); |
| box-shadow: 0 0 10px rgba(76, 201, 240, 0.5); |
| } |
| |
| |
| #role-reveal { |
| position: absolute; |
| top: 50%; |
| left: 50%; |
| transform: translate(-50%, -50%); |
| width: 85%; |
| max-width: 350px; |
| background-color: #1b263b; |
| border: 4px solid #f72585; |
| border-radius: 15px; |
| padding: 25px; |
| z-index: 100; |
| display: none; |
| text-align: center; |
| box-shadow: 0 0 30px rgba(247, 37, 133, 0.8); |
| } |
| |
| #role-reveal h3 { |
| margin-bottom: 20px; |
| color: #f72585; |
| font-size: 22px; |
| } |
| |
| #role-reveal p { |
| margin-bottom: 20px; |
| font-size: 16px; |
| line-height: 1.4; |
| } |
| |
| #role-reveal button { |
| background-color: #4cc9f0; |
| color: white; |
| border: none; |
| padding: 12px 25px; |
| border-radius: 8px; |
| margin-top: 15px; |
| font-size: 18px; |
| cursor: pointer; |
| transition: background-color 0.2s ease; |
| font-weight: bold; |
| } |
| |
| #role-reveal button:hover { |
| background-color: #3aa8d8; |
| } |
| </style> |
| </head> |
| <body> |
| <div id="loading-screen"> |
| <h1>MINI AMONG US</h1> |
| <div id="loading-bar"> |
| <div id="loading-progress"></div> |
| </div> |
| <div id="loading-text">Loading game assets...</div> |
| </div> |
|
|
| <div id="game-container"> |
| <div id="game-map"> |
| |
| </div> |
|
|
| |
| <div id="controls"> |
| <div id="up-btn" class="control-btn">↑</div> |
| <div id="left-btn" class="control-btn">←</div> |
| <div id="right-btn" class="control-btn">→</div> |
| <div id="down-btn" class="control-btn">↓</div> |
| </div> |
|
|
| <div id="task-list"> |
| <h3>Tasks</h3> |
| <div id="tasks-container"> |
| |
| </div> |
| </div> |
|
|
| <div id="game-status"> |
| <h3>Status</h3> |
| <div id="tasks-completed">0/5 tasks completed</div> |
| </div> |
|
|
| <div id="mini-map"> |
| <div id="mini-map-view"></div> |
| </div> |
|
|
| <div id="emergency-btn">!</div> |
| <div id="kill-btn">⚔️</div> |
| <div id="vent-btn">🕳️</div> |
|
|
| <div id="countdown-timer">00:30</div> |
|
|
| |
| <div id="task-popup"> |
| <h3 id="task-title">Fix Wiring</h3> |
| <p id="task-description">Connect the wires to match the colors</p> |
| <div id="task-progress"> |
| <div id="progress-bar"></div> |
| </div> |
| <button id="task-cancel-btn">Cancel</button> |
| <button id="task-complete-btn">Complete</button> |
| </div> |
|
|
| <div id="sabotage-menu"> |
| <h3>Sabotage</h3> |
| <button class="sabotage-option" data-sabotage="lights">Cut Lights</button> |
| <button class="sabotage-option" data-sabotage="oxygen">O2 Sabotage</button> |
| <button class="sabotage-option" data-sabotage="reactor">Reactor Meltdown</button> |
| <button class="sabotage-option" data-sabotage="comms">Disable Comms</button> |
| <button id="sabotage-close-btn">Close</button> |
| </div> |
|
|
| <div id="role-reveal"> |
| <h3 id="role-title">Your Role</h3> |
| <p id="role-description">You are a Crewmate. Complete all tasks to win!</p> |
| <button id="role-confirm-btn">Start Game</button> |
| </div> |
|
|
| <div id="win-screen"> |
| <h2 id="win-title">Victory!</h2> |
| <p id="win-message">All tasks completed!</p> |
| <button id="play-again-btn">Play Again</button> |
| </div> |
|
|
| <div class="tooltip"></div> |
| </div> |
|
|
| <script> |
| |
| const gameState = { |
| player: { |
| x: 0, |
| y: 0, |
| speed: 5, |
| isMoving: false, |
| direction: { x: 0, y: 0 }, |
| isImpostor: false, |
| inVent: false, |
| killCooldown: 0 |
| }, |
| tasks: [], |
| completedTasks: 0, |
| totalTasks: 5, |
| npcs: [], |
| rooms: [], |
| walls: [], |
| doors: [], |
| scale: 1, |
| offsetX: 0, |
| offsetY: 0, |
| currentTask: null, |
| sabotageActive: false, |
| sabotageType: null, |
| sabotageTimer: 0, |
| gameTime: 300, |
| gameStarted: false |
| }; |
| |
| |
| const elements = { |
| gameContainer: document.getElementById('game-container'), |
| gameMap: document.getElementById('game-map'), |
| miniMap: document.getElementById('mini-map'), |
| miniMapView: document.getElementById('mini-map-view'), |
| loadingScreen: document.getElementById('loading-screen'), |
| loadingProgress: document.getElementById('loading-progress'), |
| loadingText: document.getElementById('loading-text'), |
| taskList: document.getElementById('task-list'), |
| tasksContainer: document.getElementById('tasks-container'), |
| tasksCompleted: document.getElementById('tasks-completed'), |
| taskPopup: document.getElementById('task-popup'), |
| taskTitle: document.getElementById('task-title'), |
| taskDescription: document.getElementById('task-description'), |
| progressBar: document.getElementById('progress-bar'), |
| taskCancelBtn: document.getElementById('task-cancel-btn'), |
| taskCompleteBtn: document.getElementById('task-complete-btn'), |
| emergencyBtn: document.getElementById('emergency-btn'), |
| killBtn: document.getElementById('kill-btn'), |
| ventBtn: document.getElementById('vent-btn'), |
| sabotageMenu: document.getElementById('sabotage-menu'), |
| sabotageOptions: document.querySelectorAll('.sabotage-option'), |
| sabotageCloseBtn: document.getElementById('sabotage-close-btn'), |
| roleReveal: document.getElementById('role-reveal'), |
| roleTitle: document.getElementById('role-title'), |
| roleDescription: document.getElementById('role-description'), |
| roleConfirmBtn: document.getElementById('role-confirm-btn'), |
| winScreen: document.getElementById('win-screen'), |
| winTitle: document.getElementById('win-title'), |
| winMessage: document.getElementById('win-message'), |
| playAgainBtn: document.getElementById('play-again-btn'), |
| countdownTimer: document.getElementById('countdown-timer'), |
| upBtn: document.getElementById('up-btn'), |
| leftBtn: document.getElementById('left-btn'), |
| rightBtn: document.getElementById('right-btn'), |
| downBtn: document.getElementById('down-btn'), |
| tooltip: document.querySelector('.tooltip') |
| }; |
| |
| |
| function initGame() { |
| |
| let progress = 0; |
| const loadingInterval = setInterval(() => { |
| progress += 5; |
| elements.loadingProgress.style.width = `${progress}%`; |
| elements.loadingText.textContent = |
| progress < 50 ? "Loading game assets..." : |
| progress < 80 ? "Generating map..." : |
| "Almost ready..."; |
| |
| if (progress >= 100) { |
| clearInterval(loadingInterval); |
| setTimeout(() => { |
| elements.loadingScreen.style.display = 'none'; |
| setupGame(); |
| }, 500); |
| } |
| }, 100); |
| |
| |
| setupEventListeners(); |
| } |
| |
| |
| function setupGame() { |
| |
| createRooms(); |
| |
| |
| createPlayer(); |
| |
| |
| createNPCs(5); |
| |
| |
| createTasks(gameState.totalTasks); |
| |
| |
| assignRoles(); |
| |
| |
| requestAnimationFrame(gameLoop); |
| |
| |
| setInterval(updateGameTimer, 1000); |
| } |
| |
| |
| function createRooms() { |
| |
| const rooms = [ |
| { id: 'cafeteria', x: 500, y: 500, width: 400, height: 300 }, |
| { id: 'admin', x: 500, y: 850, width: 300, height: 300 }, |
| { id: 'medbay', x: 850, y: 500, width: 300, height: 300 }, |
| { id: 'electrical', x: 1200, y: 500, width: 300, height: 300 }, |
| { id: 'storage', x: 1200, y: 850, width: 300, height: 300 }, |
| { id: 'reactor', x: 1550, y: 850, width: 300, height: 300 }, |
| { id: 'security', x: 850, y: 850, width: 300, height: 300 }, |
| { id: 'navigation', x: 500, y: 1200, width: 300, height: 300 }, |
| { id: 'shields', x: 850, y: 1200, width: 300, height: 300 }, |
| { id: 'communications', x: 1200, y: 1200, width: 300, height: 300 } |
| ]; |
| |
| |
| rooms.forEach(room => { |
| const roomElement = document.createElement('div'); |
| roomElement.className = 'room'; |
| roomElement.id = `room-${room.id}`; |
| roomElement.style.left = `${room.x}px`; |
| roomElement.style.top = `${room.y}px`; |
| roomElement.style.width = `${room.width}px`; |
| roomElement.style.height = `${room.height}px`; |
| |
| const label = document.createElement('div'); |
| label.className = 'room-label'; |
| label.textContent = room.id.toUpperCase(); |
| roomElement.appendChild(label); |
| |
| elements.gameMap.appendChild(roomElement); |
| gameState.rooms.push(room); |
| |
| |
| createWalls(room); |
| }); |
| |
| |
| createDoors(); |
| |
| |
| gameState.player.x = 700; |
| gameState.player.y = 650; |
| |
| |
| updateViewport(); |
| } |
| |
| |
| function createWalls(room) { |
| |
| const topWall = { |
| x: room.x, |
| y: room.y, |
| width: room.width, |
| height: 20 |
| }; |
| createWallElement(topWall); |
| |
| |
| const bottomWall = { |
| x: room.x, |
| y: room.y + room.height - 20, |
| width: room.width, |
| height: 20 |
| }; |
| createWallElement(bottomWall); |
| |
| |
| const leftWall = { |
| x: room.x, |
| y: room.y, |
| width: 20, |
| height: room.height |
| }; |
| createWallElement(leftWall); |
| |
| |
| const rightWall = { |
| x: room.x + room.width - 20, |
| y: room.y, |
| width: 20, |
| height: room.height |
| }; |
| createWallElement(rightWall); |
| |
| |
| gameState.walls.push(topWall, bottomWall, leftWall, rightWall); |
| } |
| |
| |
| function createWallElement(wall) { |
| const wallElement = document.createElement('div'); |
| wallElement.className = 'room-wall'; |
| wallElement.style.left = `${wall.x}px`; |
| wallElement.style.top = `${wall.y}px`; |
| wallElement.style.width = `${wall.width}px`; |
| wallElement.style.height = `${wall.height}px`; |
| elements.gameMap.appendChild(wallElement); |
| } |
| |
| |
| function createDoors() { |
| |
| createDoor(700, 800, 40, 20, 'horizontal'); |
| |
| |
| createDoor(900, 650, 20, 40, 'vertical'); |
| |
| |
| createDoor(1150, 650, 20, 40, 'vertical'); |
| |
| |
| createDoor(1350, 800, 40, 20, 'horizontal'); |
| |
| |
| createDoor(1500, 1000, 20, 40, 'vertical'); |
| |
| |
| createDoor(800, 850, 20, 40, 'vertical'); |
| |
| |
| createDoor(1000, 1150, 40, 20, 'horizontal'); |
| |
| |
| createDoor(800, 1200, 20, 40, 'vertical'); |
| |
| |
| createDoor(1150, 1350, 40, 20, 'horizontal'); |
| |
| |
| updateMiniMap(); |
| } |
| |
| |
| function createDoor(x, y, width, height, orientation) { |
| const door = { |
| x, y, width, height, orientation |
| }; |
| |
| const doorElement = document.createElement('div'); |
| doorElement.className = 'room-door'; |
| doorElement.style.left = `${x}px`; |
| doorElement.style.top = `${y}px`; |
| doorElement.style.width = `${width}px`; |
| doorElement.style.height = `${height}px`; |
| elements.gameMap.appendChild(doorElement); |
| |
| gameState.doors.push(door); |
| return door; |
| } |
| |
| |
| function createPlayer() { |
| const playerElement = document.createElement('div'); |
| playerElement.className = 'player'; |
| playerElement.id = 'player'; |
| playerElement.style.left = `${gameState.player.x}px`; |
| playerElement.style.top = `${gameState.player.y}px`; |
| |
| const shadowElement = document.createElement('div'); |
| shadowElement.className = 'player-shadow'; |
| playerElement.appendChild(shadowElement); |
| |
| elements.gameMap.appendChild(playerElement); |
| } |
| |
| |
| function createNPCs(count) { |
| const colors = ['#4cc9f0', '#4361ee', '#3a0ca3', '#7209b7', '#f72585', '#b5179e']; |
| |
| for (let i = 0; i < count; i++) { |
| const npc = { |
| id: `npc-${i}`, |
| x: 600 + Math.random() * 200, |
| y: 600 + Math.random() * 200, |
| color: colors[i % colors.length], |
| isImpostor: false, |
| speed: 1 + Math.random() * 2, |
| direction: { x: 0, y: 0 }, |
| moveTimer: 0, |
| currentRoom: 'cafeteria' |
| }; |
| |
| const npcElement = document.createElement('div'); |
| npcElement.className = 'npc'; |
| npcElement.id = npc.id; |
| npcElement.style.left = `${npc.x}px`; |
| npcElement.style.top = `${npc.y}px`; |
| npcElement.style.backgroundColor = npc.color; |
| |
| const shadowElement = document.createElement('div'); |
| shadowElement.className = 'npc-shadow'; |
| npcElement.appendChild(shadowElement); |
| |
| elements.gameMap.appendChild(npcElement); |
| gameState.npcs.push(npc); |
| } |
| } |
| |
| |
| function createTasks(count) { |
| const taskTypes = [ |
| { name: 'Fix Wiring', description: 'Connect the wires to match the colors', duration: 5, emoji: '🔌' }, |
| { name: 'Download Data', description: 'Download important data from the terminal', duration: 7, emoji: '💾' }, |
| { name: 'Align Engine', description: 'Align the engine output to the correct position', duration: 8, emoji: '⚙️' }, |
| { name: 'Clear Asteroids', description: 'Shoot the asteroids before they hit the ship', duration: 10, emoji: '☄️' }, |
| { name: 'Calibrate Distributor', description: 'Calibrate the distributor to optimal levels', duration: 6, emoji: '📊' }, |
| { name: 'Empty Garbage', description: 'Empty the garbage to prevent overflow', duration: 4, emoji: '🗑️' }, |
| { name: 'Fuel Engines', description: 'Fill the engines with the correct fuel mixture', duration: 7, emoji: '⛽' }, |
| { name: 'Inspect Sample', description: 'Analyze the sample in the medbay scanner', duration: 9, emoji: '🧪' }, |
| { name: 'Prime Shields', description: 'Prime the shields to full capacity', duration: 8, emoji: '🛡️' }, |
| { name: 'Stabilize Steering', description: 'Stabilize the steering system', duration: 5, emoji: '🧭' } |
| ]; |
| |
| |
| const roomTasks = { |
| 'cafeteria': ['Empty Garbage'], |
| 'admin': ['Download Data'], |
| 'medbay': ['Inspect Sample'], |
| 'electrical': ['Fix Wiring'], |
| 'storage': ['Fuel Engines'], |
| 'reactor': ['Align Engine'], |
| 'security': ['Download Data'], |
| 'navigation': ['Clear Asteroids', 'Stabilize Steering'], |
| 'shields': ['Prime Shields'], |
| 'communications': ['Download Data'] |
| }; |
| |
| |
| let createdTasks = 0; |
| while (createdTasks < count) { |
| for (const roomId in roomTasks) { |
| if (createdTasks >= count) break; |
| |
| const room = gameState.rooms.find(r => r.id === roomId); |
| if (!room) continue; |
| |
| const availableTasks = roomTasks[roomId]; |
| const taskType = availableTasks[Math.floor(Math.random() * availableTasks.length)]; |
| const taskDef = taskTypes.find(t => t.name === taskType); |
| |
| if (!taskDef) continue; |
| |
| |
| const x = room.x + 50 + Math.random() * (room.width - 100); |
| const y = room.y + 50 + Math.random() * (room.height - 100); |
| |
| const task = { |
| id: `task-${createdTasks}`, |
| name: taskDef.name, |
| description: taskDef.description, |
| emoji: taskDef.emoji, |
| duration: taskDef.duration, |
| x, |
| y, |
| roomId, |
| completed: false, |
| progress: 0 |
| }; |
| |
| const taskElement = document.createElement('div'); |
| taskElement.className = 'task'; |
| taskElement.id = task.id; |
| taskElement.style.left = `${x}px`; |
| taskElement.style.top = `${y}px`; |
| taskElement.textContent = taskDef.emoji; |
| taskElement.dataset.taskId = task.id; |
| |
| elements.gameMap.appendChild(taskElement); |
| gameState.tasks.push(task); |
| createdTasks++; |
| |
| |
| addTaskToList(task); |
| } |
| } |
| } |
| |
| |
| function addTaskToList(task) { |
| const taskItem = document.createElement('div'); |
| taskItem.className = 'task-item'; |
| taskItem.dataset.taskId = task.id; |
| |
| const checkbox = document.createElement('div'); |
| checkbox.className = 'task-checkbox'; |
| |
| const taskName = document.createElement('span'); |
| taskName.textContent = task.name; |
| |
| taskItem.appendChild(checkbox); |
| taskItem.appendChild(taskName); |
| elements.tasksContainer.appendChild(taskItem); |
| } |
| |
| |
| function assignRoles() { |
| |
| const impostorIndex = Math.floor(Math.random() * gameState.npcs.length); |
| gameState.npcs[impostorIndex].isImpostor = true; |
| |
| |
| gameState.player.isImpostor = Math.random() < 0.1; |
| |
| |
| showRoleReveal(); |
| } |
| |
| |
| function showRoleReveal() { |
| if (gameState.player.isImpostor) { |
| elements.roleTitle.textContent = "IMPOSTOR"; |
| elements.roleDescription.textContent = "Sabotage the ship and eliminate crewmates without being caught!"; |
| elements.roleTitle.style.color = "#f72585"; |
| |
| |
| elements.killBtn.style.display = 'flex'; |
| elements.ventBtn.style.display = 'flex'; |
| } else { |
| elements.roleTitle.textContent = "CREWMATE"; |
| elements.roleDescription.textContent = "Complete all tasks and identify the impostor to win!"; |
| elements.roleTitle.style.color = "#4cc9f0"; |
| } |
| |
| elements.roleReveal.style.display = 'block'; |
| } |
| |
| |
| function setupEventListeners() { |
| |
| elements.upBtn.addEventListener('touchstart', () => startMoving(0, -1)); |
| elements.upBtn.addEventListener('touchend', () => stopMoving(0, -1)); |
| elements.upBtn.addEventListener('mousedown', () => startMoving(0, -1)); |
| elements.upBtn.addEventListener('mouseup', () => stopMoving(0, -1)); |
| elements.upBtn.addEventListener('mouseleave', () => stopMoving(0, -1)); |
| |
| elements.leftBtn.addEventListener('touchstart', () => startMoving(-1, 0)); |
| elements.leftBtn.addEventListener('touchend', () => stopMoving(-1, 0)); |
| elements.leftBtn.addEventListener('mousedown', () => startMoving(-1, 0)); |
| elements.leftBtn.addEventListener('mouseup', () => stopMoving(-1, 0)); |
| elements.leftBtn.addEventListener('mouseleave', () => stopMoving(-1, 0)); |
| |
| elements.rightBtn.addEventListener('touchstart', () => startMoving(1, 0)); |
| elements.rightBtn.addEventListener('touchend', () => stopMoving(1, 0)); |
| elements.rightBtn.addEventListener('mousedown', () => startMoving(1, 0)); |
| elements.rightBtn.addEventListener('mouseup', () => stopMoving(1, 0)); |
| elements.rightBtn.addEventListener('mouseleave', () => stopMoving(1, 0)); |
| |
| elements.downBtn.addEventListener('touchstart', () => startMoving(0, 1)); |
| elements.downBtn.addEventListener('touchend', () => stopMoving(0, 1)); |
| elements.downBtn.addEventListener('mousedown', () => startMoving(0, 1)); |
| elements.downBtn.addEventListener('mouseup', () => stopMoving(0, 1)); |
| elements.downBtn.addEventListener('mouseleave', () => stopMoving(0, 1)); |
| |
| |
| document.addEventListener('keydown', (e) => { |
| if (gameState.currentTask) return; |
| |
| switch(e.key) { |
| case 'ArrowUp': |
| case 'w': |
| case 'W': |
| startMoving(0, -1); |
| break; |
| case 'ArrowLeft': |
| case 'a': |
| case 'A': |
| startMoving(-1, 0); |
| break; |
| case 'ArrowRight': |
| case 'd': |
| case 'D': |
| startMoving(1, 0); |
| break; |
| case 'ArrowDown': |
| case 's': |
| case 'S': |
| startMoving(0, 1); |
| break; |
| } |
| }); |
| |
| document.addEventListener('keyup', (e) => { |
| switch(e.key) { |
| case 'ArrowUp': |
| case 'w': |
| case 'W': |
| stopMoving(0, -1); |
| break; |
| case 'ArrowLeft': |
| case 'a': |
| case 'A': |
| stopMoving(-1, 0); |
| break; |
| case 'ArrowRight': |
| case 'd': |
| case 'D': |
| stopMoving(1, 0); |
| break; |
| case 'ArrowDown': |
| case 's': |
| case 'S': |
| stopMoving(0, 1); |
| break; |
| } |
| }); |
| |
| |
| document.addEventListener('click', (e) => { |
| if (e.target.classList.contains('task') && !gameState.currentTask) { |
| const taskId = e.target.dataset.taskId; |
| startTask(taskId); |
| } |
| }); |
| |
| |
| elements.taskCancelBtn.addEventListener('click', cancelTask); |
| elements.taskCompleteBtn.addEventListener('click', completeTask); |
| |
| |
| elements.emergencyBtn.addEventListener('click', showEmergencyMeeting); |
| |
| |
| elements.sabotageOptions.forEach(option => { |
| option.addEventListener('click', () => startSabotage(option.dataset.sabotage)); |
| }); |
| elements.sabotageCloseBtn.addEventListener('click', () => { |
| elements.sabotageMenu.style.display = 'none'; |
| }); |
| |
| |
| elements.roleConfirmBtn.addEventListener('click', () => { |
| elements.roleReveal.style.display = 'none'; |
| gameState.gameStarted = true; |
| elements.countdownTimer.style.display = 'block'; |
| }); |
| |
| |
| elements.playAgainBtn.addEventListener('click', resetGame); |
| |
| |
| elements.killBtn.addEventListener('click', attemptKill); |
| |
| |
| elements.ventBtn.addEventListener('click', toggleVent); |
| } |
| |
| |
| function startMoving(x, y) { |
| if (gameState.currentTask || gameState.player.inVent) return; |
| |
| gameState.player.isMoving = true; |
| gameState.player.direction.x += x; |
| gameState.player.direction.y += y; |
| } |
| |
| |
| function stopMoving(x, y) { |
| gameState.player.direction.x -= x; |
| gameState.player.direction.y -= y; |
| |
| if (gameState.player.direction.x === 0 && gameState.player.direction.y === 0) { |
| gameState.player.isMoving = false; |
| } |
| } |
| |
| |
| function gameLoop() { |
| |
| if (gameState.player.isMoving && !gameState.currentTask && !gameState.player.inVent) { |
| movePlayer(); |
| } |
| |
| |
| moveNPCs(); |
| |
| |
| if (gameState.player.killCooldown > 0) { |
| gameState.player.killCooldown--; |
| } |
| |
| |
| if (gameState.sabotageActive && gameState.sabotageTimer > 0) { |
| gameState.sabotageTimer--; |
| if (gameState.sabotageTimer <= 0) { |
| endSabotage(); |
| } |
| } |
| |
| |
| updateViewport(); |
| |
| |
| updateMiniMap(); |
| |
| |
| requestAnimationFrame(gameLoop); |
| } |
| |
| |
| function movePlayer() { |
| const newX = gameState.player.x + (gameState.player.direction.x * gameState.player.speed); |
| const newY = gameState.player.y + (gameState.player.direction.y * gameState.player.speed); |
| |
| |
| if (!checkWallCollision(newX, newY)) { |
| gameState.player.x = newX; |
| gameState.player.y = newY; |
| |
| |
| const playerElement = document.getElementById('player'); |
| if (playerElement) { |
| playerElement.style.left = `${gameState.player.x}px`; |
| playerElement.style.top = `${gameState.player.y}px`; |
| } |
| } |
| |
| |
| checkCurrentRoom(); |
| } |
| |
| |
| function checkWallCollision(x, y) { |
| const playerSize = 45; |
| |
| |
| for (const wall of gameState.walls) { |
| if (x < wall.x + wall.width && |
| x + playerSize > wall.x && |
| y < wall.y + wall.height && |
| y + playerSize > wall.y) { |
| return true; |
| } |
| } |
| |
| return false; |
| } |
| |
| |
| function checkCurrentRoom() { |
| for (const room of gameState.rooms) { |
| if (gameState.player.x >= room.x && |
| gameState.player.x <= room.x + room.width && |
| gameState.player.y >= room.y && |
| gameState.player.y <= room.y + room.height) { |
| |
| |
| const miniRoom = document.querySelector(`.mini-map-room[data-room="${room.id}"]`); |
| if (miniRoom) { |
| document.querySelectorAll('.mini-map-room').forEach(r => { |
| r.style.backgroundColor = 'rgba(65, 90, 119, 0.5)'; |
| }); |
| miniRoom.style.backgroundColor = 'rgba(76, 201, 240, 0.7)'; |
| } |
| |
| return room.id; |
| } |
| } |
| return null; |
| } |
| |
| |
| function moveNPCs() { |
| gameState.npcs.forEach(npc => { |
| |
| if (npc.moveTimer <= 0) { |
| |
| npc.direction = { |
| x: Math.random() * 2 - 1, |
| y: Math.random() * 2 - 1 |
| }; |
| |
| |
| const length = Math.sqrt(npc.direction.x * npc.direction.x + npc.direction.y * npc.direction.y); |
| if (length > 0) { |
| npc.direction.x /= length; |
| npc.direction.y /= length; |
| } |
| |
| npc.moveTimer = 60 + Math.random() * 120; |
| } else { |
| npc.moveTimer--; |
| } |
| |
| |
| const newX = npc.x + (npc.direction.x * npc.speed); |
| const newY = npc.y + (npc.direction.y * npc.speed); |
| |
| |
| let collided = false; |
| for (const wall of gameState.walls) { |
| if (newX < wall.x + wall.width && |
| newX + 45 > wall.x && |
| newY < wall.y + wall.height && |
| newY + 45 > wall.y) { |
| collided = true; |
| break; |
| } |
| } |
| |
| if (!collided) { |
| npc.x = newX; |
| npc.y = newY; |
| } else { |
| npc.direction.x *= -1; |
| npc.direction.y *= -1; |
| npc.moveTimer = 0; |
| } |
| |
| |
| const npcElement = document.getElementById(npc.id); |
| if (npcElement) { |
| npcElement.style.left = `${npc.x}px`; |
| npcElement.style.top = `${npc.y}px`; |
| } |
| }); |
| } |
| |
| |
| function updateViewport() { |
| const containerWidth = elements.gameContainer.clientWidth; |
| const containerHeight = elements.gameContainer.clientHeight; |
| |
| |
| const scaleX = containerWidth / 1000; |
| const scaleY = containerHeight / 1000; |
| gameState.scale = Math.min(scaleX, scaleY, 1); |
| |
| |
| gameState.offsetX = (containerWidth / 2) - (gameState.player.x * gameState.scale); |
| gameState.offsetY = (containerHeight / 2) - (gameState.player.y * gameState.scale); |
| |
| |
| elements.gameMap.style.transform = `translate(${gameState.offsetX}px, ${gameState.offsetY}px) scale(${gameState.scale})`; |
| } |
| |
| |
| function updateMiniMap() { |
| |
| elements.miniMapView.innerHTML = ''; |
| |
| |
| const miniMapScale = 0.04; |
| |
| |
| gameState.rooms.forEach(room => { |
| const miniRoom = document.createElement('div'); |
| miniRoom.className = 'mini-map-room'; |
| miniRoom.dataset.room = room.id; |
| miniRoom.style.left = `${room.x * miniMapScale}px`; |
| miniRoom.style.top = `${room.y * miniMapScale}px`; |
| miniRoom.style.width = `${room.width * miniMapScale}px`; |
| miniRoom.style.height = `${room.height * miniMapScale}px`; |
| elements.miniMapView.appendChild(miniRoom); |
| }); |
| |
| |
| const miniPlayer = document.createElement('div'); |
| miniPlayer.className = 'mini-map-player'; |
| miniPlayer.style.left = `${gameState.player.x * miniMapScale}px`; |
| miniPlayer.style.top = `${gameState.player.y * miniMapScale}px`; |
| elements.miniMapView.appendChild(miniPlayer); |
| |
| |
| gameState.npcs.forEach(npc => { |
| const miniNPC = document.createElement('div'); |
| miniNPC.className = 'mini-map-npc'; |
| miniNPC.style.left = `${npc.x * miniMapScale}px`; |
| miniNPC.style.top = `${npc.y * miniMapScale}px`; |
| miniNPC.style.backgroundColor = npc.isImpostor ? '#f72585' : '#4cc9f0'; |
| elements.miniMapView.appendChild(miniNPC); |
| }); |
| |
| |
| gameState.tasks.forEach(task => { |
| if (!task.completed) { |
| const miniTask = document.createElement('div'); |
| miniTask.className = 'mini-map-task'; |
| miniTask.style.left = `${task.x * miniMapScale}px`; |
| miniTask.style.top = `${task.y * miniMapScale}px`; |
| elements.miniMapView.appendChild(miniTask); |
| } |
| }); |
| |
| |
| const currentRoom = checkCurrentRoom(); |
| if (currentRoom) { |
| const miniRoom = document.querySelector(`.mini-map-room[data-room="${currentRoom}"]`); |
| if (miniRoom) { |
| miniRoom.style.backgroundColor = 'rgba(76, 201, 240, 0.7)'; |
| } |
| } |
| } |
| |
| |
| function startTask(taskId) { |
| const task = gameState.tasks.find(t => t.id === taskId); |
| if (!task || task.completed) return; |
| |
| gameState.currentTask = task; |
| |
| |
| elements.taskTitle.textContent = task.name; |
| elements.taskDescription.textContent = task.description; |
| elements.progressBar.style.width = '0%'; |
| elements.taskPopup.style.display = 'block'; |
| |
| |
| task.progress = 0; |
| const progressInterval = setInterval(() => { |
| task.progress++; |
| elements.progressBar.style.width = `${(task.progress / task.duration) * 100}%`; |
| |
| if (task.progress >= task.duration) { |
| clearInterval(progressInterval); |
| elements.taskCompleteBtn.disabled = false; |
| } |
| }, 1000); |
| |
| |
| task.progressInterval = progressInterval; |
| } |
| |
| |
| function cancelTask() { |
| if (!gameState.currentTask) return; |
| |
| clearInterval(gameState.currentTask.progressInterval); |
| gameState.currentTask.progress = 0; |
| gameState.currentTask = null; |
| elements.taskPopup.style.display = 'none'; |
| } |
| |
| |
| function completeTask() { |
| if (!gameState.currentTask) return; |
| |
| clearInterval(gameState.currentTask.progressInterval); |
| gameState.currentTask.completed = true; |
| gameState.completedTasks++; |
| |
| |
| const taskItem = document.querySelector(`.task-item[data-task-id="${gameState.currentTask.id}"]`); |
| if (taskItem) { |
| taskItem.classList.add('completed'); |
| } |
| |
| |
| const taskElement = document.getElementById(gameState.currentTask.id); |
| if (taskElement) { |
| taskElement.style.display = 'none'; |
| } |
| |
| |
| elements.tasksCompleted.textContent = `${gameState.completedTasks}/${gameState.totalTasks} tasks completed`; |
| |
| |
| if (gameState.completedTasks >= gameState.totalTasks && !gameState.player.isImpostor) { |
| showWinScreen(true); |
| } |
| |
| gameState.currentTask = null; |
| elements.taskPopup.style.display = 'none'; |
| } |
| |
| |
| function showEmergencyMeeting() { |
| |
| showTooltip("Emergency meeting called!", elements.emergencyBtn); |
| |
| |
| } |
| |
| |
| function showSabotageMenu() { |
| if (!gameState.player.isImpostor || gameState.sabotageActive) return; |
| |
| elements.sabotageMenu.style.display = 'block'; |
| } |
| |
| |
| function startSabotage(type) { |
| if (!gameState.player.isImpostor || gameState.sabotageActive) return; |
| |
| gameState.sabotageActive = true; |
| gameState.sabotageType = type; |
| |
| |
| switch(type) { |
| case 'lights': |
| gameState.sabotageTimer = 30; |
| darkenLights(); |
| break; |
| case 'oxygen': |
| gameState.sabotageTimer = 45; |
| showTooltip("O2 Sabotage! Fix it in admin!", elements.gameContainer); |
| break; |
| case 'reactor': |
| gameState.sabotageTimer = 60; |
| showTooltip("Reactor Meltdown! Fix it in reactor!", elements.gameContainer); |
| break; |
| case 'comms': |
| gameState.sabotageTimer = 30; |
| disableComms(); |
| break; |
| } |
| |
| elements.sabotageMenu.style.display = 'none'; |
| } |
| |
| |
| function endSabotage() { |
| if (!gameState.sabotageActive) return; |
| |
| switch(gameState.sabotageType) { |
| case 'lights': |
| restoreLights(); |
| break; |
| case 'comms': |
| restoreComms(); |
| break; |
| } |
| |
| gameState.sabotageActive = false; |
| gameState.sabotageType = null; |
| } |
| |
| |
| function darkenLights() { |
| elements.gameMap.style.filter = 'brightness(0.3)'; |
| } |
| |
| |
| function restoreLights() { |
| elements.gameMap.style.filter = 'brightness(1)'; |
| } |
| |
| |
| function disableComms() { |
| |
| elements.taskList.style.opacity = '0.5'; |
| } |
| |
| |
| function restoreComms() { |
| elements.taskList.style.opacity = '1'; |
| } |
| |
| |
| function attemptKill() { |
| if (!gameState.player.isImpostor || gameState.player.killCooldown > 0) return; |
| |
| |
| for (const npc of gameState.npcs) { |
| if (!npc.isImpostor) { |
| const distance = Math.sqrt( |
| Math.pow(gameState.player.x - npc.x, 2) + |
| Math.pow(gameState.player.y - npc.y, 2) |
| ); |
| |
| if (distance < 60) { |
| |
| const npcElement = document.getElementById(npc.id); |
| if (npcElement) { |
| npcElement.style.display = 'none'; |
| } |
| |
| |
| gameState.npcs = gameState.npcs.filter(n => n.id !== npc.id); |
| |
| |
| gameState.player.killCooldown = 120; |
| |
| |
| const crewmatesLeft = gameState.npcs.filter(n => !n.isImpostor).length; |
| if (crewmatesLeft === 0) { |
| showWinScreen(false); |
| } |
| |
| |
| createBloodParticles(npc.x, npc.y); |
| |
| break; |
| } |
| } |
| } |
| } |
| |
| |
| function toggleVent() { |
| if (!gameState.player.isImpostor) return; |
| |
| gameState.player.inVent = !gameState.player.inVent; |
| |
| if (gameState.player.inVent) { |
| |
| const playerElement = document.getElementById('player'); |
| if (playerElement) { |
| playerElement.style.opacity = '0.5'; |
| } |
| |
| |
| gameState.player.speed = 10; |
| |
| |
| } else { |
| |
| const playerElement = document.getElementById('player'); |
| if (playerElement) { |
| playerElement.style.opacity = '1'; |
| } |
| |
| |
| gameState.player.speed = 5; |
| } |
| } |
| |
| |
| function createBloodParticles(x, y) { |
| for (let i = 0; i < 20; i++) { |
| const particle = document.createElement('div'); |
| particle.className = 'particle'; |
| particle.style.left = `${x + Math.random() * 30 - 15}px`; |
| particle.style.top = `${y + Math.random() * 30 - 15}px`; |
| particle.style.backgroundColor = '#f72585'; |
| |
| |
| const size = 2 + Math.random() * 4; |
| particle.style.width = `${size}px`; |
| particle.style.height = `${size}px`; |
| |
| elements.gameMap.appendChild(particle); |
| |
| |
| const angle = Math.random() * Math.PI * 2; |
| const speed = 1 + Math.random() * 3; |
| let distance = 0; |
| const maxDistance = 20 + Math.random() * 30; |
| |
| const animate = () => { |
| distance += speed; |
| particle.style.left = `${parseFloat(particle.style.left) + Math.cos(angle) * speed}px`; |
| particle.style.top = `${parseFloat(particle.style.top) + Math.sin(angle) * speed}px`; |
| particle.style.opacity = `${1 - (distance / maxDistance)}`; |
| |
| if (distance < maxDistance) { |
| requestAnimationFrame(animate); |
| } else { |
| particle.remove(); |
| } |
| }; |
| |
| animate(); |
| } |
| } |
| |
| |
| function showTooltip(text, element) { |
| elements.tooltip.textContent = text; |
| elements.tooltip.style.opacity = '1'; |
| |
| const rect = element.getBoundingClientRect(); |
| elements.tooltip.style.left = `${rect.left + rect.width / 2}px`; |
| elements.tooltip.style.top = `${rect.top - 40}px`; |
| elements.tooltip.style.transform = 'translate(-50%, -100%)'; |
| |
| setTimeout(() => { |
| elements.tooltip.style.opacity = '0'; |
| }, 2000); |
| } |
| |
| |
| function updateGameTimer() { |
| if (!gameState.gameStarted) return; |
| |
| gameState.gameTime--; |
| |
| const minutes = Math.floor(gameState.gameTime / 60); |
| const seconds = gameState.gameTime % 60; |
| |
| elements.countdownTimer.textContent = |
| `${minutes.toString().padStart(2, '0')}:${seconds.toString().padStart(2, '0')}`; |
| |
| |
| if (gameState.gameTime <= 30) { |
| elements.countdownTimer.style.color = |
| Math.floor(Date.now() / 500) % 2 === 0 ? '#f72585' : 'white'; |
| } |
| |
| |
| if (gameState.gameTime <= 0) { |
| showWinScreen(false); |
| } |
| } |
| |
| |
| function showWinScreen(crewmatesWin) { |
| if (crewmatesWin) { |
| elements.winTitle.textContent = "CREWMATES WIN!"; |
| elements.winMessage.textContent = "All tasks completed and impostor defeated!"; |
| elements.winTitle.style.color = "#4cc9f0"; |
| } else { |
| elements.winTitle.textContent = "IMPOSTOR WINS!"; |
| elements.winMessage.textContent = "All crewmates have been eliminated!"; |
| elements.winTitle.style.color = "#f72585"; |
| } |
| |
| elements.winScreen.style.display = 'flex'; |
| gameState.gameStarted = false; |
| } |
| |
| |
| function resetGame() { |
| |
| elements.gameMap.innerHTML = ''; |
| |
| |
| gameState.player = { |
| x: 700, |
| y: 650, |
| speed: 5, |
| isMoving: false, |
| direction: { x: 0, y: 0 }, |
| isImpostor: false, |
| inVent: false, |
| killCooldown: 0 |
| }; |
| gameState.tasks = []; |
| gameState.completedTasks = 0; |
| gameState.npcs = []; |
| gameState.rooms = []; |
| gameState.walls = []; |
| gameState.doors = []; |
| gameState.currentTask = null; |
| gameState.sabotageActive = false; |
| gameState.sabotageType = null; |
| gameState.sabotageTimer = 0; |
| gameState.gameTime = 300; |
| gameState.gameStarted = false; |
| |
| |
| elements.taskList.innerHTML = '<h3>Tasks</h3><div id="tasks-container"></div>'; |
| elements.tasksCompleted.textContent = '0/5 tasks completed'; |
| elements.taskPopup.style.display = 'none'; |
| elements.sabotageMenu.style.display = 'none'; |
| elements.winScreen.style.display = 'none'; |
| elements.countdownTimer.style.display = 'none'; |
| elements.killBtn.style.display = 'none'; |
| elements.ventBtn.style.display = 'none'; |
| elements.gameMap.style.filter = 'brightness(1)'; |
| elements.taskList.style.opacity = '1'; |
| |
| |
| elements.roleReveal.style.display = 'none'; |
| |
| |
| setupGame(); |
| } |
| |
| |
| initGame(); |
| </script> |
| <p style="border-radius: 8px; text-align: center; font-size: 12px; color: #fff; margin-top: 16px;position: fixed; left: 8px; bottom: 8px; z-index: 10; background: rgba(0, 0, 0, 0.8); padding: 4px 8px;">Made with <img src="https://enzostvs-deepsite.hf.space/logo.svg" alt="DeepSite Logo" style="width: 16px; height: 16px; vertical-align: middle;display:inline-block;margin-right:3px;filter:brightness(0) invert(1);"><a href="https://enzostvs-deepsite.hf.space" style="color: #fff;text-decoration: underline;" target="_blank" >DeepSite</a> - 🧬 <a href="https://enzostvs-deepsite.hf.space?remix=kate-line/amogos" style="color: #fff;text-decoration: underline;" target="_blank" >Remix</a></p></body> |
| </html> |