| | <!DOCTYPE html> |
| | <html lang="en"> |
| | <head> |
| | <meta charset="UTF-8"> |
| | <meta name="viewport" content="width=device-width, initial-scale=1.0"> |
| | <title>Altaire - Battle AI Monsters in Norfolk!</title> |
| | <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css"> |
| | <style> |
| | |
| | :root { |
| | --zx-black: #000000; |
| | --zx-blue: #0000d0; |
| | --zx-red: #d00000; |
| | --zx-magenta: #d000d0; |
| | --zx-green: #00d000; |
| | --zx-cyan: #00d0d0; |
| | --zx-yellow: #d0d000; |
| | --zx-white: #d0d0d0; |
| | --zx-bright-blue: #0000ff; |
| | --zx-bright-red: #ff0000; |
| | --zx-bright-green: #00ff00; |
| | } |
| | |
| | body { |
| | margin: 0; |
| | padding: 0; |
| | background-color: var(--zx-blue); |
| | font-family: 'Courier New', monospace; |
| | color: var(--zx-white); |
| | overflow: hidden; |
| | image-rendering: pixelated; |
| | height: 100vh; |
| | display: flex; |
| | justify-content: center; |
| | align-items: center; |
| | } |
| | |
| | #game-container { |
| | width: 100vw; |
| | height: 100vh; |
| | position: relative; |
| | border: 8px solid var(--zx-black); |
| | background-color: var(--zx-black); |
| | overflow: hidden; |
| | } |
| | |
| | #loading-screen { |
| | position: absolute; |
| | width: 100%; |
| | height: 100%; |
| | background-color: var(--zx-blue); |
| | color: var(--zx-white); |
| | display: flex; |
| | flex-direction: column; |
| | justify-content: center; |
| | align-items: center; |
| | z-index: 100; |
| | } |
| | |
| | #spectrum-loading { |
| | position: absolute; |
| | width: 100%; |
| | height: 100%; |
| | background-color: var(--zx-black); |
| | z-index: 101; |
| | display: flex; |
| | flex-direction: column; |
| | justify-content: center; |
| | align-items: center; |
| | } |
| | |
| | #loading-border { |
| | width: 80%; |
| | height: 20px; |
| | border: 2px solid var(--zx-white); |
| | margin-top: 20px; |
| | position: relative; |
| | overflow: hidden; |
| | } |
| | |
| | #loading-stripes { |
| | position: absolute; |
| | top: 0; |
| | left: 0; |
| | width: 100%; |
| | height: 100%; |
| | background: repeating-linear-gradient( |
| | to right, |
| | var(--zx-bright-green) 0%, |
| | var(--zx-bright-green) 20%, |
| | var(--zx-black) 20%, |
| | var(--zx-black) 40% |
| | ); |
| | background-size: 40px 100%; |
| | animation: stripeMove 0.5s linear infinite; |
| | } |
| | |
| | @keyframes stripeMove { |
| | 0% { background-position: 0 0; } |
| | 100% { background-position: 40px 0; } |
| | } |
| | |
| | #loading-progress { |
| | height: 100%; |
| | width: 0; |
| | background-color: var(--zx-bright-green); |
| | transition: width 0.3s; |
| | position: relative; |
| | z-index: 2; |
| | } |
| | |
| | #game-screen { |
| | position: relative; |
| | width: 100%; |
| | height: 100%; |
| | display: none; |
| | } |
| | |
| | #title-screen { |
| | position: absolute; |
| | width: 100%; |
| | height: 100%; |
| | background-color: var(--zx-black); |
| | color: var(--zx-white); |
| | text-align: center; |
| | display: flex; |
| | flex-direction: column; |
| | justify-content: center; |
| | align-items: center; |
| | z-index: 10; |
| | } |
| | |
| | #title { |
| | font-size: 72px; |
| | color: var(--zx-bright-red); |
| | text-shadow: 0 0 10px var(--zx-red); |
| | margin-bottom: 30px; |
| | transform: rotate(-5deg); |
| | animation: flicker 0.5s infinite alternate; |
| | } |
| | |
| | @keyframes flicker { |
| | 0% { opacity: 1; } |
| | 20% { opacity: 0.8; } |
| | 40% { opacity: 0.7; } |
| | 60% { opacity: 0.9; } |
| | 100% { opacity: 1; } |
| | } |
| | |
| | #press-start { |
| | font-size: 24px; |
| | color: var(--zx-white); |
| | margin-top: 40px; |
| | animation: pulse 2s infinite; |
| | } |
| | |
| | @keyframes pulse { |
| | 0% { opacity: 0.7; } |
| | 50% { opacity: 1; } |
| | 100% { opacity: 0.7; } |
| | } |
| | |
| | #menu { |
| | margin-top: 30px; |
| | } |
| | |
| | .menu-item { |
| | margin: 10px 0; |
| | padding: 8px 20px; |
| | background-color: var(--zx-blue); |
| | color: var(--zx-white); |
| | cursor: pointer; |
| | border: 2px solid var(--zx-cyan); |
| | transition: all 0.2s; |
| | font-size: 20px; |
| | } |
| | |
| | .menu-item:hover { |
| | background-color: var(--zx-bright-blue); |
| | transform: scale(1.05); |
| | } |
| | |
| | #game-area { |
| | position: relative; |
| | width: 100%; |
| | height: 100%; |
| | background-color: var(--zx-black); |
| | } |
| | |
| | #player { |
| | position: absolute; |
| | width: 32px; |
| | height: 32px; |
| | background-color: var(--zx-bright-green); |
| | z-index: 5; |
| | } |
| | |
| | .monster { |
| | position: absolute; |
| | width: 32px; |
| | height: 32px; |
| | z-index: 4; |
| | animation: monsterFloat 2s infinite ease-in-out; |
| | } |
| | |
| | @keyframes monsterFloat { |
| | 0%, 100% { transform: translateY(0); } |
| | 50% { transform: translateY(-5px); } |
| | } |
| | |
| | .projectile { |
| | position: absolute; |
| | width: 8px; |
| | height: 8px; |
| | background-color: var(--zx-yellow); |
| | z-index: 3; |
| | } |
| | |
| | #hud { |
| | position: absolute; |
| | top: 10px; |
| | left: 10px; |
| | padding: 5px 10px; |
| | background-color: rgba(0, 0, 0, 0.7); |
| | border: 2px solid var(--zx-white); |
| | z-index: 10; |
| | font-size: 18px; |
| | } |
| | |
| | #stats { |
| | display: flex; |
| | justify-content: space-between; |
| | flex-wrap: wrap; |
| | } |
| | |
| | .stat { |
| | margin-right: 20px; |
| | color: var(--zx-white); |
| | } |
| | |
| | #health-bar { |
| | width: 200px; |
| | height: 15px; |
| | background-color: var(--zx-red); |
| | margin-top: 5px; |
| | } |
| | |
| | #health-fill { |
| | height: 100%; |
| | width: 100%; |
| | background-color: var(--zx-bright-green); |
| | transition: width 0.3s; |
| | } |
| | |
| | #location-display { |
| | position: absolute; |
| | bottom: 10px; |
| | left: 10px; |
| | background-color: rgba(0, 0, 0, 0.7); |
| | padding: 5px 10px; |
| | border: 2px solid var(--zx-white); |
| | font-size: 18px; |
| | } |
| | |
| | #controls { |
| | position: absolute; |
| | bottom: 10px; |
| | right: 10px; |
| | background-color: rgba(0, 0, 0, 0.7); |
| | padding: 5px 10px; |
| | border: 2px solid var(--zx-white); |
| | font-size: 16px; |
| | } |
| | |
| | #game-over { |
| | position: absolute; |
| | width: 100%; |
| | height: 100%; |
| | background-color: rgba(0, 0, 0, 0.8); |
| | display: none; |
| | flex-direction: column; |
| | justify-content: center; |
| | align-items: center; |
| | z-index: 20; |
| | } |
| | |
| | #game-over h1 { |
| | color: var(--zx-bright-red); |
| | font-size: 72px; |
| | margin-bottom: 20px; |
| | } |
| | |
| | #btn-retry { |
| | margin-top: 20px; |
| | padding: 10px 20px; |
| | background-color: var(--zx-blue); |
| | color: var(--zx-white); |
| | border: 2px solid var(--zx-cyan); |
| | cursor: pointer; |
| | font-size: 20px; |
| | } |
| | |
| | #btn-retry:hover { |
| | background-color: var(--zx-bright-blue); |
| | } |
| | |
| | #victory-screen { |
| | position: absolute; |
| | width: 100%; |
| | height: 100%; |
| | background-color: rgba(0, 0, 0, 0.8); |
| | display: none; |
| | flex-direction: column; |
| | justify-content: center; |
| | align-items: center; |
| | z-index: 20; |
| | } |
| | |
| | #victory-screen h1 { |
| | color: var(--zx-bright-green); |
| | font-size: 72px; |
| | margin-bottom: 20px; |
| | animation: pulse 1s infinite; |
| | } |
| | |
| | #norfolk-map { |
| | width: 80%; |
| | max-width: 800px; |
| | height: 60vh; |
| | max-height: 600px; |
| | background-color: var(--zx-blue); |
| | border: 2px solid var(--zx-white); |
| | position: relative; |
| | margin: 20px 0; |
| | } |
| | |
| | .location-marker { |
| | position: absolute; |
| | width: 16px; |
| | height: 16px; |
| | background-color: var(--zx-red); |
| | border-radius: 50%; |
| | transform: translate(-50%, -50%); |
| | } |
| | |
| | .completed-marker { |
| | background-color: var(--zx-bright-green); |
| | } |
| | |
| | .active-marker { |
| | background-color: var(--zx-yellow); |
| | box-shadow: 0 0 10px var(--zx-yellow); |
| | animation: pulse 0.5s infinite; |
| | } |
| | |
| | #locations-list { |
| | margin-top: 20px; |
| | text-align: left; |
| | max-height: 150px; |
| | overflow-y: auto; |
| | width: 80%; |
| | max-width: 600px; |
| | } |
| | |
| | .location-item { |
| | padding: 8px 15px; |
| | margin: 5px 0; |
| | background-color: rgba(0, 0, 0, 0.5); |
| | border-left: 5px solid var(--zx-blue); |
| | font-size: 18px; |
| | } |
| | |
| | .location-item.completed { |
| | border-left-color: var(--zx-bright-green); |
| | } |
| | |
| | .location-item.active { |
| | border-left-color: var(--zx-yellow); |
| | animation: active-border 1s infinite; |
| | } |
| | |
| | @keyframes active-border { |
| | 0% { border-left-color: var(--zx-yellow); } |
| | 50% { border-left-color: var(--zx-bright-red); } |
| | 100% { border-left-color: var(--zx-yellow); } |
| | } |
| | |
| | #next-location-btn { |
| | margin-top: 20px; |
| | padding: 10px 20px; |
| | background-color: var(--zx-blue); |
| | color: var(--zx-white); |
| | border: 2px solid var(--zx-cyan); |
| | cursor: pointer; |
| | font-size: 20px; |
| | } |
| | |
| | #next-location-btn:hover { |
| | background-color: var(--zx-bright-blue); |
| | } |
| | |
| | #final-boss { |
| | position: absolute; |
| | width: 64px; |
| | height: 64px; |
| | background-color: var(--zx-bright-red); |
| | z-index: 4; |
| | animation: bossGlow 1s infinite alternate; |
| | display: none; |
| | } |
| | |
| | @keyframes bossGlow { |
| | from { box-shadow: 0 0 15px var(--zx-bright-red); } |
| | to { box-shadow: 0 0 30px var(--zx-bright-red); } |
| | } |
| | |
| | #cheats { |
| | position: absolute; |
| | bottom: 10px; |
| | left: 50%; |
| | transform: translateX(-50%); |
| | background-color: rgba(0, 0, 0, 0.7); |
| | padding: 5px 10px; |
| | border: 2px solid var(--zx-red); |
| | z-index: 30; |
| | display: none; |
| | } |
| | |
| | #cheats input { |
| | background-color: var(--zx-black); |
| | color: var(--zx-white); |
| | border: 1px solid var(--zx-red); |
| | padding: 5px 10px; |
| | font-family: 'Courier New', monospace; |
| | font-size: 16px; |
| | } |
| | |
| | #cheats button { |
| | background-color: var(--zx-red); |
| | color: var(--zx-white); |
| | border: none; |
| | padding: 5px 10px; |
| | cursor: pointer; |
| | margin-left: 5px; |
| | font-family: 'Courier New', monospace; |
| | font-size: 16px; |
| | } |
| | |
| | |
| | .scanlines { |
| | position: absolute; |
| | top: 0; |
| | left: 0; |
| | width: 100%; |
| | height: 100%; |
| | background: repeating-linear-gradient( |
| | to bottom, |
| | transparent 0%, |
| | transparent 1px, |
| | rgba(0, 0, 0, 0.3) 2px, |
| | rgba(0, 0, 0, 0.3) 3px |
| | ); |
| | pointer-events: none; |
| | z-index: 5; |
| | } |
| | |
| | |
| | .pixel-grid { |
| | position: absolute; |
| | top: 0; |
| | left: 0; |
| | width: 100%; |
| | height: 100%; |
| | background-image: |
| | linear-gradient(to right, rgba(192, 192, 192, 0.1) 1px, transparent 1px), |
| | linear-gradient(to bottom, rgba(192, 192, 192, 0.1) 1px, transparent 1px); |
| | background-size: 4px 4px; |
| | pointer-events: none; |
| | z-index: 6; |
| | } |
| | |
| | |
| | .crt-effect::before { |
| | content: ""; |
| | position: absolute; |
| | top: 0; |
| | left: 0; |
| | right: 0; |
| | bottom: 0; |
| | background: radial-gradient( |
| | ellipse at center, |
| | rgba(0, 0, 0, 0) 0%, |
| | rgba(0, 0, 0, 0) 45%, |
| | rgba(0, 0, 0, 0.3) 46%, |
| | rgba(0, 0, 0, 0.3) 54%, |
| | rgba(0, 0, 0, 0) 55%, |
| | rgba(0, 0, 0, 0) 100% |
| | ); |
| | pointer-events: none; |
| | z-index: 7; |
| | } |
| | |
| | |
| | #loading-header { |
| | font-family: 'Courier New', monospace; |
| | text-align: center; |
| | margin-bottom: 20px; |
| | } |
| | |
| | #loading-header h1 { |
| | color: var(--zx-yellow); |
| | font-size: 28px; |
| | margin-bottom: 5px; |
| | } |
| | |
| | #loading-header h2 { |
| | color: var(--zx-white); |
| | font-size: 16px; |
| | margin-top: 0; |
| | } |
| | |
| | #loading-info { |
| | position: absolute; |
| | bottom: 30px; |
| | left: 0; |
| | width: 100%; |
| | text-align: center; |
| | font-family: 'Courier New', monospace; |
| | color: var(--zx-white); |
| | } |
| | |
| | #loading-details { |
| | position: absolute; |
| | top: 30%; |
| | left: 20px; |
| | color: var(--zx-white); |
| | font-family: 'Courier New', monospace; |
| | font-size: 14px; |
| | } |
| | |
| | #loading-message { |
| | position: absolute; |
| | bottom: 60px; |
| | width: 100%; |
| | text-align: center; |
| | color: var(--zx-white); |
| | font-family: 'Courier New', monospace; |
| | font-size: 16px; |
| | } |
| | |
| | #spectrum-progress-bar { |
| | position: relative; |
| | width: 80%; |
| | max-width: 400px; |
| | height: 30px; |
| | background: var(--zx-blue); |
| | border: 2px solid var(--zx-white); |
| | margin: 20px 0; |
| | } |
| | |
| | #spectrum-progress { |
| | height: 100%; |
| | width: 0; |
| | background: var(--zx-bright-green); |
| | transition: width 0.3s; |
| | } |
| | |
| | #spectrum-stripes { |
| | position: absolute; |
| | top: 0; |
| | left: 0; |
| | width: 100%; |
| | height: 100%; |
| | background: repeating-linear-gradient(to right, |
| | var(--zx-bright-green) 0px, |
| | var(--zx-bright-green) 4px, |
| | var(--zx-blue) 4px, |
| | var(--zx-blue) 8px); |
| | } |
| | </style> |
| | </head> |
| | <body> |
| | <div id="game-container" class="crt-effect"> |
| | |
| | <div id="spectrum-loading"> |
| | <div id="loading-header"> |
| | <h1>ALT<span style="color: var(--zx-bright-red);">AI</span>RE</h1> |
| | <h2>Battle AI Monsters in Norfolk</h2> |
| | </div> |
| | |
| | <div id="loading-details"> |
| | <div>Loading Altaire...</div> |
| | <div>By AI Developer</div> |
| | <div>2023</div> |
| | <div>ZX Spectrum 48K</div> |
| | </div> |
| | |
| | <div id="spectrum-progress-bar"> |
| | <div id="spectrum-progress"></div> |
| | <div id="spectrum-stripes"></div> |
| | </div> |
| | |
| | <div id="loading-message">Searching for Altaire...</div> |
| | <div id="loading-info">Press any key to break</div> |
| | </div> |
| |
|
| | <div id="loading-screen" style="display: none;"> |
| | <h1>ALT<span style="color: var(--zx-bright-red);">AI</span>RE</h1> |
| | <p>Initializing digital defenses...</p> |
| | <div id="loading-border"> |
| | <div id="loading-stripes"></div> |
| | <div id="loading-progress"></div> |
| | </div> |
| | </div> |
| |
|
| | <div id="game-screen"> |
| | <div id="title-screen"> |
| | <div id="title">ALT<span style="color: var(--zx-bright-red);">AI</span>RE</div> |
| | <p>BATTLE AI MONSTERS IN NORFOLK</p> |
| | <div id="menu"> |
| | <div class="menu-item" id="start-game">START GAME</div> |
| | <div class="menu-item" id="locations">NORFOLK LOCATIONS</div> |
| | <div class="menu-item" id="instructions">INSTRUCTIONS</div> |
| | </div> |
| | <div id="press-start">PRESS START TO DEFEND NORFOLK</div> |
| | </div> |
| |
|
| | <div id="game-area"> |
| | <div id="hud"> |
| | <div id="stats"> |
| | <div class="stat">HEALTH: <span id="health-value">100%</span></div> |
| | <div class="stat">SCORE: <span id="score-value">0</span></div> |
| | <div class="stat">LEVEL: <span id="level-value">1</span></div> |
| | <div class="stat">MONSTERS: <span id="monsters-left">5</span></div> |
| | </div> |
| | <div id="health-bar"> |
| | <div id="health-fill"></div> |
| | </div> |
| | </div> |
| |
|
| | <div id="player"></div> |
| | <div id="final-boss"></div> |
| |
|
| | <div id="location-display"> |
| | CURRENT LOCATION: <span id="location-name">GREAT YARMOUTH</span> |
| | </div> |
| |
|
| | <div id="controls"> |
| | WASD: MOVE | SPACE: SHOOT | ESC: MENU | `: CHEATS |
| | </div> |
| |
|
| | <div id="cheats"> |
| | <input type="text" id="cheat-input" placeholder="Enter cheat code"> |
| | <button id="submit-cheat">SUBMIT</button> |
| | </div> |
| | </div> |
| |
|
| | <div id="game-over"> |
| | <h1>GAME OVER</h1> |
| | <p>THE AI HAVE OVERRUN NORFOLK!</p> |
| | <p>YOUR FINAL SCORE: <span id="final-score">0</span></p> |
| | <div id="btn-retry">TRY AGAIN</div> |
| | </div> |
| |
|
| | <div id="victory-screen"> |
| | <h1>VICTORY!</h1> |
| | <p>YOU'VE SAVED NORFOLK FROM THE AI MENACE!</p> |
| | <div id="norfolk-map"></div> |
| | <div id="locations-list"></div> |
| | <p>TOTAL SCORE: <span id="total-score">0</span></p> |
| | <div id="next-location-btn" style="display: none;">NEXT LOCATION</div> |
| | </div> |
| | </div> |
| |
|
| | <div class="scanlines"></div> |
| | <div class="pixel-grid"></div> |
| | </div> |
| |
|
| | <script> |
| | |
| | const gameState = { |
| | health: 100, |
| | score: 0, |
| | level: 1, |
| | monstersKilled: 0, |
| | monstersSpawned: 0, |
| | gameActive: false, |
| | playerX: 0, |
| | playerY: 0, |
| | playerWidth: 32, |
| | playerHeight: 32, |
| | keys: {}, |
| | projectiles: [], |
| | monsters: [], |
| | currentLocation: 0, |
| | locationsCompleted: [], |
| | cheatCodes: { |
| | 'NORFOLK': () => { gameState.health = 100; updateHealth(); }, |
| | 'YARMOUTH': () => { gameState.score += 1000; updateScore(); }, |
| | 'KINGSLYNN': () => { spawnMonsters(3); }, |
| | 'SUFFOLK': () => { document.getElementById('game-over').style.display = 'none'; gameState.gameActive = true; } |
| | } |
| | }; |
| | |
| | |
| | const norfolkLocations = [ |
| | { name: 'GREAT YARMOUTH', x: 80, y: 240, monsters: 5, boss: false }, |
| | { name: 'NORWICH', x: 120, y: 160, monsters: 8, boss: false }, |
| | { name: 'CROMER', x: 60, y: 80, monsters: 6, boss: false }, |
| | { name: 'HOLT', x: 100, y: 60, monsters: 7, boss: false }, |
| | { name: 'FAKENHAM', x: 150, y: 100, monsters: 6, boss: false }, |
| | { name: 'DEREHAM', x: 180, y: 140, monsters: 7, boss: false }, |
| | { name: 'ATTLEBOROUGH', x: 210, y: 180, monsters: 8, boss: false }, |
| | { name: 'WYMONDHAM', x: 190, y: 200, monsters: 7, boss: false }, |
| | { name: 'LOWESTOFT', x: 100, y: 230, monsters: 6, boss: false }, |
| | { name: 'BECCLES', x: 130, y: 250, monsters: 5, boss: false }, |
| | { name: 'KING\'S LYNN', x: 50, y: 150, monsters: 10, boss: true } |
| | ]; |
| | |
| | |
| | const gameContainer = document.getElementById('game-container'); |
| | const spectrumLoading = document.getElementById('spectrum-loading'); |
| | const spectrumProgress = document.getElementById('spectrum-progress'); |
| | const loadingScreen = document.getElementById('loading-screen'); |
| | const loadingProgress = document.getElementById('loading-progress'); |
| | const loadingMessage = document.getElementById('loading-message'); |
| | const gameScreen = document.getElementById('game-screen'); |
| | const titleScreen = document.getElementById('title-screen'); |
| | const gameArea = document.getElementById('game-area'); |
| | const player = document.getElementById('player'); |
| | const finalBoss = document.getElementById('final-boss'); |
| | const hud = document.getElementById('hud'); |
| | const healthValue = document.getElementById('health-value'); |
| | const healthFill = document.getElementById('health-fill'); |
| | const scoreValue = document.getElementById('score-value'); |
| | const levelValue = document.getElementById('level-value'); |
| | const monstersLeft = document.getElementById('monsters-left'); |
| | const locationName = document.getElementById('location-name'); |
| | const gameOverScreen = document.getElementById('game-over'); |
| | const finalScore = document.getElementById('final-score'); |
| | const btnRetry = document.getElementById('btn-retry'); |
| | const victoryScreen = document.getElementById('victory-screen'); |
| | const totalScore = document.getElementById('total-score'); |
| | const norfolkMap = document.getElementById('norfolk-map'); |
| | const locationsList = document.getElementById('locations-list'); |
| | const nextLocationBtn = document.getElementById('next-location-btn'); |
| | const cheatInput = document.getElementById('cheat-input'); |
| | const submitCheat = document.getElementById('submit-cheat'); |
| | const cheatsPanel = document.getElementById('cheats'); |
| | |
| | |
| | document.getElementById('start-game').addEventListener('click', startGame); |
| | document.getElementById('locations').addEventListener('click', showLocations); |
| | document.getElementById('instructions').addEventListener('click', showInstructions); |
| | btnRetry.addEventListener('click', restartGame); |
| | nextLocationBtn.addEventListener('click', nextLocation); |
| | submitCheat.addEventListener('click', submitCheatCode); |
| | |
| | |
| | document.addEventListener('keydown', (e) => { |
| | |
| | if (spectrumLoading.style.display !== 'none') { |
| | spectrumLoading.style.display = 'none'; |
| | loadingScreen.style.display = 'flex'; |
| | simulateLoading(); |
| | } |
| | |
| | if (e.key === 'Escape') { |
| | if (titleScreen.style.display === 'flex') { |
| | resumeGame(); |
| | } else { |
| | pauseGame(); |
| | } |
| | } |
| | |
| | if (e.key === '`' || e.key === '~') { |
| | cheatsPanel.style.display = cheatsPanel.style.display === 'none' ? 'block' : 'none'; |
| | if (cheatsPanel.style.display === 'block') { |
| | cheatInput.focus(); |
| | } |
| | } |
| | |
| | gameState.keys[e.key.toUpperCase()] = true; |
| | }); |
| | |
| | document.addEventListener('keyup', (e) => { |
| | gameState.keys[e.key.toUpperCase()] = false; |
| | }); |
| | |
| | |
| | function simulateTapeLoading() { |
| | const loadingMessages = [ |
| | "Searching for Altaire", |
| | "Found Altaire", |
| | "Reading block 1 of 10", |
| | "Reading block 2 of 10", |
| | "Reading block 3 of 10", |
| | "Reading block 4 of 10", |
| | "Reading block 5 of 10", |
| | "Reading block 6 of 10", |
| | "Reading block 7 of 10", |
| | "Reading block 8 of 10", |
| | "Reading block 9 of 10", |
| | "Reading block 10 of 10", |
| | "Loading complete" |
| | ]; |
| | |
| | let progress = 0; |
| | let messageIndex = 0; |
| | |
| | const interval = setInterval(() => { |
| | progress += Math.random() * 10; |
| | if (progress >= 100) progress = 100; |
| | |
| | spectrumProgress.style.width = `${progress}%`; |
| | loadingMessage.textContent = `${loadingMessages[messageIndex]} ...`; |
| | |
| | |
| | if (progress >= messageIndex * (100 / (loadingMessages.length - 1))) { |
| | if (messageIndex < loadingMessages.length - 1) { |
| | messageIndex++; |
| | } |
| | } |
| | |
| | |
| | if (Math.random() < 0.05) { |
| | loadingMessage.textContent = "*** Tape loading error ***"; |
| | setTimeout(() => { |
| | loadingMessage.textContent = `${loadingMessages[messageIndex]} ...`; |
| | }, 500); |
| | } |
| | |
| | if (progress === 100) { |
| | clearInterval(interval); |
| | setTimeout(() => { |
| | |
| | spectrumLoading.style.display = 'none'; |
| | loadingScreen.style.display = 'flex'; |
| | simulateLoading(); |
| | }, 1000); |
| | } |
| | }, 300); |
| | } |
| | |
| | function simulateLoading() { |
| | let progress = 0; |
| | const interval = setInterval(() => { |
| | progress += Math.random() * 10; |
| | if (progress > 100) progress = 100; |
| | loadingProgress.style.width = `${progress}%`; |
| | |
| | if (progress === 100) { |
| | clearInterval(interval); |
| | setTimeout(() => { |
| | loadingScreen.style.display = 'none'; |
| | gameScreen.style.display = 'block'; |
| | }, 500); |
| | } |
| | }, 200); |
| | } |
| | |
| | |
| | simulateTapeLoading(); |
| | |
| | function showMainMenu() { |
| | titleScreen.innerHTML = ` |
| | <div id="title">ALT<span style="color: var(--zx-bright-red);">AI</span>RE</div> |
| | <p>BATTLE AI MONSTERS IN NORFOLK</p> |
| | <div id="menu"> |
| | <div class="menu-item" id="start-game">START GAME</div> |
| | <div class="menu-item" id="locations">NORFOLK LOCATIONS</div> |
| | <div class="menu-item" id="instructions">INSTRUCTIONS</div> |
| | </div> |
| | <div id="press-start">PRESS START TO DEFEND NORFOLK</div> |
| | `; |
| | |
| | |
| | document.getElementById('start-game').addEventListener('click', startGame); |
| | document.getElementById('locations').addEventListener('click', showLocations); |
| | document.getElementById('instructions').addEventListener('click', showInstructions); |
| | } |
| | |
| | function startGame() { |
| | titleScreen.style.display = 'none'; |
| | gameState.gameActive = true; |
| | |
| | |
| | gameState.health = 100; |
| | gameState.score = 0; |
| | gameState.level = 1; |
| | gameState.monstersKilled = 0; |
| | gameState.monstersSpawned = 0; |
| | gameState.currentLocation = 0; |
| | gameState.locationsCompleted = []; |
| | gameState.projectiles = []; |
| | gameState.monsters = []; |
| | |
| | |
| | document.querySelectorAll('.monster').forEach(m => m.remove()); |
| | document.querySelectorAll('.projectile').forEach(p => p.remove()); |
| | |
| | |
| | updateHealth(); |
| | updateScore(); |
| | levelValue.textContent = gameState.level; |
| | |
| | |
| | gameState.playerX = (gameArea.clientWidth - gameState.playerWidth) / 2; |
| | gameState.playerY = gameArea.clientHeight - gameState.playerHeight - 50; |
| | player.style.left = `${gameState.playerX}px`; |
| | player.style.top = `${gameState.playerY}px`; |
| | |
| | |
| | startLocation(); |
| | |
| | |
| | requestAnimationFrame(gameLoop); |
| | } |
| | |
| | function startLocation() { |
| | const location = norfolkLocations[gameState.currentLocation]; |
| | locationName.textContent = location.name; |
| | |
| | const totalMonsters = location.monsters; |
| | monstersLeft.textContent = totalMonsters; |
| | gameState.monstersKilled = 0; |
| | gameState.monstersSpawned = 0; |
| | |
| | |
| | if (location.boss) { |
| | |
| | finalBoss.style.display = 'block'; |
| | finalBoss.style.left = `${(gameArea.clientWidth - 64) / 2}px`; |
| | finalBoss.style.top = '50px'; |
| | gameState.monsters.push({ |
| | element: finalBoss, |
| | x: (gameArea.clientWidth - 64) / 2, |
| | y: 50, |
| | width: 64, |
| | height: 64, |
| | health: 20, |
| | speed: 2, |
| | isBoss: true |
| | }); |
| | gameState.monstersSpawned = 1; |
| | } else { |
| | spawnMonsters(2); |
| | } |
| | } |
| | |
| | function spawnMonsters(count) { |
| | for (let i = 0; i < count; i++) { |
| | if (gameState.monstersSpawned >= norfolkLocations[gameState.currentLocation].monsters) { |
| | return; |
| | } |
| | |
| | gameState.monstersSpawned++; |
| | |
| | const monster = document.createElement('div'); |
| | monster.className = 'monster'; |
| | |
| | const monsterSize = 24 + Math.random() * 16; |
| | const monsterX = Math.random() * (gameArea.clientWidth - monsterSize); |
| | const monsterY = Math.random() * (gameArea.clientHeight / 2 - monsterSize); |
| | |
| | monster.style.width = `${monsterSize}px`; |
| | monster.style.height = `${monsterSize}px`; |
| | monster.style.left = `${monsterX}px`; |
| | monster.style.top = `${monsterY}px`; |
| | |
| | |
| | const colors = ['var(--zx-red)', 'var(--zx-magenta)', 'var(--zx-cyan)', 'var(--zx-yellow)']; |
| | monster.style.backgroundColor = colors[Math.floor(Math.random() * colors.length)]; |
| | |
| | gameArea.appendChild(monster); |
| | |
| | gameState.monsters.push({ |
| | element: monster, |
| | x: monsterX, |
| | y: monsterY, |
| | width: monsterSize, |
| | height: monsterSize, |
| | health: 2, |
| | speedX: (Math.random() - 0.5) * 4, |
| | speedY: Math.random() * 2 + 1, |
| | isBoss: false |
| | }); |
| | |
| | monstersLeft.textContent = norfolkLocations[gameState.currentLocation].monsters - gameState.monstersKilled; |
| | } |
| | } |
| | |
| | function gameLoop() { |
| | if (!gameState.gameActive) return; |
| | |
| | |
| | const moveSpeed = 5; |
| | if (gameState.keys['W'] || gameState.keys['ARROWUP']) { |
| | gameState.playerY = Math.max(0, gameState.playerY - moveSpeed); |
| | } |
| | if (gameState.keys['S'] || gameState.keys['ARROWDOWN']) { |
| | gameState.playerY = Math.min(gameArea.clientHeight - gameState.playerHeight, gameState.playerY + moveSpeed); |
| | } |
| | if (gameState.keys['A'] || gameState.keys['ARROWLEFT']) { |
| | gameState.playerX = Math.max(0, gameState.playerX - moveSpeed); |
| | } |
| | if (gameState.keys['D'] || gameState.keys['ARROWRIGHT']) { |
| | gameState.playerX = Math.min(gameArea.clientWidth - gameState.playerWidth, gameState.playerX + moveSpeed); |
| | } |
| | |
| | |
| | player.style.left = `${gameState.playerX}px`; |
| | player.style.top = `${gameState.playerY}px`; |
| | |
| | |
| | if (gameState.keys[' ']) { |
| | if (gameState.lastShot === undefined || Date.now() - gameState.lastShot > 300) { |
| | shoot(); |
| | gameState.lastShot = Date.now(); |
| | } |
| | } |
| | |
| | |
| | updateProjectiles(); |
| | |
| | |
| | updateMonsters(); |
| | |
| | |
| | checkCollisions(); |
| | |
| | |
| | if (gameState.monsters.length < 3 && gameState.monstersSpawned < norfolkLocations[gameState.currentLocation].monsters) { |
| | spawnMonsters(1); |
| | } |
| | |
| | requestAnimationFrame(gameLoop); |
| | } |
| | |
| | function shoot() { |
| | const projectile = document.createElement('div'); |
| | projectile.className = 'projectile'; |
| | |
| | const projectileX = gameState.playerX + gameState.playerWidth / 2 - 4; |
| | const projectileY = gameState.playerY; |
| | |
| | projectile.style.left = `${projectileX}px`; |
| | projectile.style.top = `${projectileY}px`; |
| | |
| | gameArea.appendChild(projectile); |
| | |
| | gameState.projectiles.push({ |
| | element: projectile, |
| | x: projectileX, |
| | y: projectileY, |
| | width: 8, |
| | height: 8, |
| | speed: 8 |
| | }); |
| | } |
| | |
| | function updateProjectiles() { |
| | for (let i = gameState.projectiles.length - 1; i >= 0; i--) { |
| | const projectile = gameState.projectiles[i]; |
| | projectile.y -= projectile.speed; |
| | projectile.element.style.top = `${projectile.y}px`; |
| | |
| | |
| | if (projectile.y < 0) { |
| | gameArea.removeChild(projectile.element); |
| | gameState.projectiles.splice(i, 1); |
| | } |
| | } |
| | } |
| | |
| | function updateMonsters() { |
| | for (let i = gameState.monsters.length - 1; i >= 0; i--) { |
| | const monster = gameState.monsters[i]; |
| | |
| | if (!monster.isBoss) { |
| | |
| | monster.x += monster.speedX; |
| | monster.y += monster.speedY; |
| | |
| | |
| | if (monster.x <= 0 || monster.x + monster.width >= gameArea.clientWidth) { |
| | monster.speedX *= -1; |
| | } |
| | |
| | |
| | if (monster.y + monster.height >= gameArea.clientHeight) { |
| | monster.speedY *= -1; |
| | } |
| | |
| | monster.element.style.left = `${monster.x}px`; |
| | monster.element.style.top = `${monster.y}px`; |
| | } else { |
| | |
| | if (monster.moveDirection === undefined) { |
| | monster.moveDirection = Math.random() < 0.5 ? 1 : -1; |
| | monster.moveCounter = Math.random() * 100 + 50; |
| | } |
| | |
| | monster.x += monster.speed * monster.moveDirection; |
| | monster.moveCounter--; |
| | |
| | if (monster.moveCounter <= 0 || |
| | monster.x <= 0 || |
| | monster.x + monster.width >= gameArea.clientWidth) { |
| | monster.moveDirection *= -1; |
| | monster.moveCounter = Math.random() * 100 + 50; |
| | |
| | |
| | if (Math.random() < 0.2) { |
| | bossShoot(monster); |
| | } |
| | } |
| | |
| | monster.element.style.left = `${monster.x}px`; |
| | } |
| | } |
| | } |
| | |
| | function bossShoot(boss) { |
| | for (let i = 0; i < 3; i++) { |
| | setTimeout(() => { |
| | const projectile = document.createElement('div'); |
| | projectile.className = 'projectile'; |
| | projectile.style.backgroundColor = 'var(--zx-bright-red)'; |
| | |
| | const projectileX = boss.x + boss.width / 2 - 4; |
| | const projectileY = boss.y + boss.height; |
| | |
| | projectile.style.left = `${projectileX}px`; |
| | projectile.style.top = `${projectileY}px`; |
| | |
| | gameArea.appendChild(projectile); |
| | |
| | gameState.projectiles.push({ |
| | element: projectile, |
| | x: projectileX, |
| | y: projectileY, |
| | width: 8, |
| | height: 8, |
| | speed: -5, |
| | fromMonster: true |
| | }); |
| | }, i * 300); |
| | } |
| | } |
| | |
| | function checkCollisions() { |
| | |
| | for (let i = gameState.projectiles.length - 1; i >= 0; i--) { |
| | const projectile = gameState.projectiles[i]; |
| | |
| | if (projectile.fromMonster) { |
| | |
| | if (checkCollision(projectile, { |
| | x: gameState.playerX, |
| | y: gameState.playerY, |
| | width: gameState.playerWidth, |
| | height: gameState.playerHeight |
| | })) { |
| | gameArea.removeChild(projectile.element); |
| | gameState.projectiles.splice(i, 1); |
| | takeDamage(10); |
| | continue; |
| | } |
| | } else { |
| | |
| | for (let j = gameState.monsters.length - 1; j >= 0; j--) { |
| | const monster = gameState.monsters[j]; |
| | |
| | if (checkCollision(projectile, monster)) { |
| | gameArea.removeChild(projectile.element); |
| | gameState.projectiles.splice(i, 1); |
| | |
| | monster.health--; |
| | if (monster.health <= 0) { |
| | gameArea.removeChild(monster.element); |
| | gameState.monsters.splice(j, 1); |
| | gameState.monstersKilled++; |
| | gameState.score += monster.isBoss ? 500 : 100; |
| | updateScore(); |
| | |
| | |
| | if (monster.isBoss) { |
| | const explosion = document.createElement('div'); |
| | explosion.style.position = 'absolute'; |
| | explosion.style.left = `${monster.x}px`; |
| | explosion.style.top = `${monster.y}px`; |
| | explosion.style.width = `${monster.width}px`; |
| | explosion.style.height = `${monster.height}px`; |
| | explosion.style.backgroundColor = 'var(--zx-bright-red)'; |
| | explosion.style.zIndex = '100'; |
| | explosion.style.borderRadius = '50%'; |
| | explosion.style.animation = 'flicker 0.1s 10 alternate'; |
| | gameArea.appendChild(explosion); |
| | |
| | setTimeout(() => { |
| | gameArea.removeChild(explosion); |
| | }, 1000); |
| | } |
| | } |
| | break; |
| | } |
| | } |
| | } |
| | } |
| | |
| | |
| | for (let i = gameState.monsters.length - 1; i >= 0; i--) { |
| | const monster = gameState.monsters[i]; |
| | if (!monster.isBoss && checkCollision({ |
| | x: gameState.playerX, |
| | y: gameState.playerY, |
| | width: gameState.playerWidth, |
| | height: gameState.playerHeight |
| | }, monster)) { |
| | takeDamage(5); |
| | |
| | |
| | monster.speedX = (monster.x < gameState.playerX) ? -3 : 3; |
| | monster.speedY = -2; |
| | } |
| | } |
| | |
| | |
| | monstersLeft.textContent = norfolkLocations[gameState.currentLocation].monsters - gameState.monstersKilled; |
| | |
| | |
| | if (gameState.monstersKilled >= norfolkLocations[gameState.currentLocation].monsters) { |
| | locationCleared(); |
| | } |
| | } |
| | |
| | function checkCollision(obj1, obj2) { |
| | return obj1.x < obj2.x + obj2.width && |
| | obj1.x + obj1.width > obj2.x && |
| | obj1.y < obj2.y + obj2.height && |
| | obj1.y + obj1.height > obj2.y; |
| | } |
| | |
| | function takeDamage(amount) { |
| | gameState.health -= amount; |
| | updateHealth(); |
| | |
| | |
| | player.style.backgroundColor = 'var(--zx-bright-red)'; |
| | setTimeout(() => { |
| | player.style.backgroundColor = 'var(--zx-bright-green)'; |
| | }, 100); |
| | |
| | if (gameState.health <= 0) { |
| | gameOver(); |
| | } |
| | } |
| | |
| | function updateHealth() { |
| | const healthPercent = Math.max(0, gameState.health); |
| | healthValue.textContent = `${healthPercent}%`; |
| | healthFill.style.width = `${healthPercent}%`; |
| | |
| | |
| | if (healthPercent > 60) { |
| | healthFill.style.backgroundColor = 'var(--zx-bright-green)'; |
| | } else if (healthPercent > 30) { |
| | healthFill.style.backgroundColor = 'var(--zx-yellow)'; |
| | } else { |
| | healthFill.style.backgroundColor = 'var(--zx-bright-red)'; |
| | } |
| | } |
| | |
| | function updateScore() { |
| | scoreValue.textContent = gameState.score; |
| | } |
| | |
| | function locationCleared() { |
| | gameState.gameActive = false; |
| | gameState.locationsCompleted.push(gameState.currentLocation); |
| | |
| | |
| | gameArea.style.backgroundColor = 'var(--zx-bright-green)'; |
| | setTimeout(() => { |
| | gameArea.style.backgroundColor = 'var(--zx-black)'; |
| | }, 100); |
| | setTimeout(() => { |
| | gameArea.style.backgroundColor = 'var(--zx-bright-green)'; |
| | }, 200); |
| | setTimeout(() => { |
| | gameArea.style.backgroundColor = 'var(--zx-black)'; |
| | }, 300); |
| | |
| | if (gameState.currentLocation === norfolkLocations.length - 1) { |
| | |
| | victory(); |
| | } else { |
| | |
| | nextLocationBtn.style.display = 'block'; |
| | } |
| | } |
| | |
| | function nextLocation() { |
| | gameState.currentLocation++; |
| | gameState.level++; |
| | levelValue.textContent = gameState.level; |
| | |
| | |
| | gameState.playerX = (gameArea.clientWidth - gameState.playerWidth) / 2; |
| | gameState.playerY = gameArea.clientHeight - gameState.playerHeight - 50; |
| | player.style.left = `${gameState.playerX}px`; |
| | player.style.top = `${gameState.playerY}px`; |
| | |
| | |
| | victoryScreen.style.display = 'none'; |
| | nextLocationBtn.style.display = 'none'; |
| | |
| | |
| | gameState.health = Math.min(100, gameState.health + 25); |
| | updateHealth(); |
| | |
| | |
| | startLocation(); |
| | |
| | |
| | gameState.gameActive = true; |
| | requestAnimationFrame(gameLoop); |
| | } |
| | |
| | function gameOver() { |
| | gameState.gameActive = false; |
| | gameOverScreen.style.display = 'flex'; |
| | finalScore.textContent = gameState.score; |
| | } |
| | |
| | function victory() { |
| | gameState.gameActive = false; |
| | victoryScreen.style.display = 'flex'; |
| | totalScore.textContent = gameState.score; |
| | nextLocationBtn.style.display = 'none'; |
| | |
| | |
| | norfolkMap.innerHTML = ''; |
| | |
| | |
| | const coastline = document.createElement('div'); |
| | coastline.style.position = 'absolute'; |
| | coastline.style.backgroundColor = 'var(--zx-green)'; |
| | coastline.style.width = '80%'; |
| | coastline.style.height = '80%'; |
| | coastline.style.top = '10%'; |
| | coastline.style.left = '10%'; |
| | coastline.style.borderRadius = '0 120px 120px 0'; |
| | norfolkMap.appendChild(coastline); |
| | |
| | |
| | for (let i = 0; i < norfolkLocations.length; i++) { |
| | const marker = document.createElement('div'); |
| | marker.className = 'location-marker'; |
| | |
| | if (gameState.locationsCompleted.includes(i)) { |
| | marker.classList.add('completed-marker'); |
| | } |
| | |
| | if (i === gameState.currentLocation) { |
| | marker.classList.add('active-marker'); |
| | } |
| | |
| | marker.style.left = `${norfolkLocations[i].x * (norfolkMap.clientWidth / 400)}px`; |
| | marker.style.top = `${norfolkLocations[i].y * (norfolkMap.clientHeight / 300)}px`; |
| | marker.title = norfolkLocations[i].name; |
| | |
| | norfolkMap.appendChild(marker); |
| | } |
| | |
| | |
| | locationsList.innerHTML = ''; |
| | for (let i = 0; i < norfolkLocations.length; i++) { |
| | const item = document.createElement('div'); |
| | item.className = 'location-item'; |
| | |
| | if (gameState.locationsCompleted.includes(i)) { |
| | item.classList.add('completed'); |
| | item.innerHTML = `<i class="fas fa-check"></i> ${norfolkLocations[i].name} (CLEARED)`; |
| | } else if (i === gameState.currentLocation) { |
| | item.classList.add('active'); |
| | item.innerHTML = `<i class="fas fa-skull-crossbones"></i> ${norfolkLocations[i].name} (CURRENT)`; |
| | } else { |
| | item.innerHTML = `<i class="fas fa-lock"></i> ${norfolkLocations[i].name}`; |
| | } |
| | |
| | locationsList.appendChild(item); |
| | } |
| | } |
| | |
| | function restartGame() { |
| | gameOverScreen.style.display = 'none'; |
| | startGame(); |
| | } |
| | |
| | function pauseGame() { |
| | gameState.gameActive = false; |
| | titleScreen.style.display = 'flex'; |
| | document.getElementById('start-game').textContent = 'RESUME GAME'; |
| | } |
| | |
| | function resumeGame() { |
| | titleScreen.style.display = 'none'; |
| | gameState.gameActive = true; |
| | requestAnimationFrame(gameLoop); |
| | } |
| | |
| | function showLocations() { |
| | titleScreen.innerHTML = ` |
| | <h2 style="font-size: 36px;">NORFOLK LOCATIONS</h2> |
| | <div style="max-width: 800px; margin: 20px auto; display: flex; flex-wrap: wrap; justify-content: center;"> |
| | ${norfolkLocations.map((loc, i) => ` |
| | <div style="margin: 10px; padding: 10px; background: var(--zx-blue); width: 200px; border: 2px solid var(--zx-cyan);"> |
| | <div style="font-size: 18px; font-weight: bold;">${loc.name}</div> |
| | <div>${loc.monsters} monsters${loc.boss ? ' + FINAL BOSS' : ''}</div> |
| | <div style="width: 100%; height: 5px; background: var(--zx-red); margin-top: 5px;"> |
| | <div style="height: 100%; width: ${(loc.monsters / 10) * 100}%; background: var(--zx-bright-green);"></div> |
| | </div> |
| | </div> |
| | `).join('')} |
| | </div> |
| | <div class="menu-item" id="back-to-menu" style="margin-top: 20px;">BACK</div> |
| | `; |
| | document.getElementById('back-to-menu').addEventListener('click', () => { |
| | showMainMenu(); |
| | }); |
| | } |
| | |
| | function showInstructions() { |
| | titleScreen.innerHTML = ` |
| | <h2 style="font-size: 36px;">HOW TO PLAY</h2> |
| | <div style="max-width: 800px; text-align: left; margin: 20px auto; font-size: 18px;"> |
| | <p><strong>OBJECTIVE:</strong> Defeat all AI monsters invading Norfolk's locations!</p> |
| | |
| | <div style="display: flex; flex-wrap: wrap; justify-content: space-around; margin: 30px 0;"> |
| | <div style="width: 150px; margin: 15px; text-align: center;"> |
| | <div style="width: 80px; height: 80px; background: var(--zx-bright-green); margin: 0 auto 10px;"></div> |
| | <p>PLAYER SHIP</p> |
| | <p>Move with WASD or Arrow Keys</p> |
| | <p>Press SPACE to shoot</p> |
| | </div> |
| | |
| | <div style="width: 150px; margin: 15px; text-align: center;"> |
| | <div style="width: 60px; height: 60px; background: var(--zx-red); margin: 0 auto 10px; border-radius: 50%;"></div> |
| | <p>REGULAR MONSTERS</p> |
| | <p>2 hits to defeat</p> |
| | <p>Avoid contact</p> |
| | </div> |
| | |
| | <div style="width: 150px; margin: 15px; text-align: center;"> |
| | <div style="width: 64px; height: 64px; background: var(--zx-bright-red); margin: 0 auto 10px; animation: bossGlow 1s infinite alternate;"></div> |
| | <p>FINAL BOSS</p> |
| | <p>20 hits to defeat</p> |
| | <p>Shoots projectiles</p> |
| | </div> |
| | </div> |
| | |
| | <p><strong>CONTROLS:</strong></p> |
| | <ul> |
| | <li>WASD or Arrow Keys - Move your ship</li> |
| | <li>SPACE - Fire projectiles</li> |
| | <li>ESC - Pause game/open menu</li> |
| | <li>` - Toggle cheat console</li> |
| | </ul> |
| | |
| | <p><strong>GAMEPLAY:</strong></p> |
| | <ul> |
| | <li>Clear all monsters in each Norfolk location</li> |
| | <li>Defeat 10 locations to reach the final boss</li> |
| | <li>Health regenerates between locations</li> |
| | <li>Watch out for enemy projectiles</li> |
| | </ul> |
| | </div> |
| | <div class="menu-item" id="back-to-menu" style="margin-top: 20px;">BACK TO MAIN MENU</div> |
| | `; |
| | document.getElementById('back-to-menu').addEventListener('click', () => { |
| | showMainMenu(); |
| | }); |
| | } |
| | |
| | function submitCheatCode() { |
| | const code = cheatInput.value.toUpperCase(); |
| | if (gameState.cheatCodes[code]) { |
| | gameState.cheatCodes[code](); |
| | cheatInput.value = ''; |
| | cheatsPanel.style.display = 'none'; |
| | } else { |
| | cheatInput.value = ''; |
| | alert('Invalid cheat code!'); |
| | } |
| | } |
| | </script> |
| | </body> |
| | </html> |