math3craft commited on
Commit
adaf9e8
·
verified ·
1 Parent(s): 523076d

Add 2 files

Browse files
Files changed (2) hide show
  1. README.md +7 -5
  2. index.html +1352 -19
README.md CHANGED
@@ -1,10 +1,12 @@
1
  ---
2
- title: Math Doom
3
- emoji: 🚀
4
- colorFrom: red
5
- colorTo: green
6
  sdk: static
7
  pinned: false
 
 
8
  ---
9
 
10
- Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
 
1
  ---
2
+ title: math-doom
3
+ emoji: 🐳
4
+ colorFrom: blue
5
+ colorTo: blue
6
  sdk: static
7
  pinned: false
8
+ tags:
9
+ - deepsite
10
  ---
11
 
12
+ Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
index.html CHANGED
@@ -1,19 +1,1352 @@
1
- <!doctype html>
2
- <html>
3
- <head>
4
- <meta charset="utf-8" />
5
- <meta name="viewport" content="width=device-width" />
6
- <title>My static Space</title>
7
- <link rel="stylesheet" href="style.css" />
8
- </head>
9
- <body>
10
- <div class="card">
11
- <h1>Welcome to your static Space!</h1>
12
- <p>You can modify this app directly by editing <i>index.html</i> in the Files and versions tab.</p>
13
- <p>
14
- Also don't forget to check the
15
- <a href="https://huggingface.co/docs/hub/spaces" target="_blank">Spaces documentation</a>.
16
- </p>
17
- </div>
18
- </body>
19
- </html>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
+ <title>Math Doom</title>
7
+ <style>
8
+ body {
9
+ margin: 0;
10
+ padding: 0;
11
+ overflow: hidden;
12
+ font-family: 'Courier New', monospace;
13
+ background-color: #000;
14
+ color: #fff;
15
+ display: flex;
16
+ flex-direction: column;
17
+ height: 100vh;
18
+ }
19
+
20
+ #gameContainer {
21
+ position: relative;
22
+ flex-grow: 1;
23
+ overflow: hidden;
24
+ }
25
+
26
+ #canvas {
27
+ display: block;
28
+ background-color: #000;
29
+ cursor: none;
30
+ }
31
+
32
+ #menu {
33
+ position: absolute;
34
+ top: 0;
35
+ left: 0;
36
+ width: 100%;
37
+ height: 100%;
38
+ background-color: rgba(0, 0, 0, 0.8);
39
+ display: flex;
40
+ flex-direction: column;
41
+ justify-content: center;
42
+ align-items: center;
43
+ z-index: 10;
44
+ }
45
+
46
+ #menu h1 {
47
+ font-size: 3em;
48
+ color: #ff0000;
49
+ margin-bottom: 0.5em;
50
+ text-shadow: 0 0 10px #ff0000;
51
+ }
52
+
53
+ .menu-button {
54
+ background-color: #ff0000;
55
+ color: #000;
56
+ border: none;
57
+ padding: 15px 30px;
58
+ margin: 10px;
59
+ font-size: 1.5em;
60
+ cursor: pointer;
61
+ font-family: 'Courier New', monospace;
62
+ font-weight: bold;
63
+ transition: all 0.3s;
64
+ border-radius: 5px;
65
+ }
66
+
67
+ .menu-button:hover {
68
+ background-color: #ffffff;
69
+ transform: scale(1.05);
70
+ }
71
+
72
+ #hud {
73
+ position: absolute;
74
+ bottom: 20px;
75
+ left: 20px;
76
+ font-size: 1.5em;
77
+ color: #fff;
78
+ text-shadow: 2px 2px 2px #000;
79
+ display: flex;
80
+ flex-direction: column;
81
+ gap: 10px;
82
+ }
83
+
84
+ #health, #ammo, #weapon {
85
+ display: flex;
86
+ align-items: center;
87
+ gap: 10px;
88
+ }
89
+
90
+ #health span, #ammo span {
91
+ color: #ff0000;
92
+ font-weight: bold;
93
+ }
94
+
95
+ #weaponName {
96
+ color: #ffcc00;
97
+ font-weight: bold;
98
+ }
99
+
100
+ #levelInfo {
101
+ position: absolute;
102
+ top: 20px;
103
+ left: 20px;
104
+ font-size: 1.2em;
105
+ color: #fff;
106
+ text-shadow: 2px 2px 2px #000;
107
+ }
108
+
109
+ #levelInfo span {
110
+ color: #00ff00;
111
+ font-weight: bold;
112
+ }
113
+
114
+ #enemyCount {
115
+ position: absolute;
116
+ top: 20px;
117
+ right: 20px;
118
+ font-size: 1.2em;
119
+ color: #fff;
120
+ text-shadow: 2px 2px 2px #000;
121
+ }
122
+
123
+ #enemyCount span {
124
+ color: #ff0000;
125
+ font-weight: bold;
126
+ }
127
+
128
+ #mathPopup {
129
+ position: absolute;
130
+ top: 50%;
131
+ left: 50%;
132
+ transform: translate(-50%, -50%);
133
+ background-color: rgba(0, 0, 0, 0.9);
134
+ border: 2px solid #ff0000;
135
+ padding: 20px;
136
+ border-radius: 10px;
137
+ display: none;
138
+ flex-direction: column;
139
+ align-items: center;
140
+ z-index: 20;
141
+ }
142
+
143
+ #mathPopup p {
144
+ margin: 0 0 20px 0;
145
+ font-size: 1.5em;
146
+ color: #fff;
147
+ }
148
+
149
+ #mathAnswer {
150
+ font-size: 1.2em;
151
+ padding: 10px;
152
+ width: 200px;
153
+ text-align: center;
154
+ margin-bottom: 10px;
155
+ background-color: #333;
156
+ border: 1px solid #ff0000;
157
+ color: #fff;
158
+ }
159
+
160
+ #mathSubmit {
161
+ padding: 10px 20px;
162
+ background-color: #ff0000;
163
+ color: #000;
164
+ border: none;
165
+ font-weight: bold;
166
+ cursor: pointer;
167
+ }
168
+
169
+ #crosshair {
170
+ position: absolute;
171
+ top: 50%;
172
+ left: 50%;
173
+ transform: translate(-50%, -50%);
174
+ width: 20px;
175
+ height: 20px;
176
+ pointer-events: none;
177
+ z-index: 5;
178
+ }
179
+
180
+ #crosshair::before, #crosshair::after {
181
+ content: '';
182
+ position: absolute;
183
+ background-color: #ff0000;
184
+ }
185
+
186
+ #crosshair::before {
187
+ width: 2px;
188
+ height: 20px;
189
+ left: 9px;
190
+ top: 0;
191
+ }
192
+
193
+ #crosshair::after {
194
+ width: 20px;
195
+ height: 2px;
196
+ left: 0;
197
+ top: 9px;
198
+ }
199
+
200
+ #deathScreen {
201
+ position: absolute;
202
+ top: 0;
203
+ left: 0;
204
+ width: 100%;
205
+ height: 100%;
206
+ background-color: rgba(0, 0, 0, 0.8);
207
+ display: none;
208
+ flex-direction: column;
209
+ justify-content: center;
210
+ align-items: center;
211
+ z-index: 15;
212
+ }
213
+
214
+ #deathScreen h1 {
215
+ font-size: 3em;
216
+ color: #ff0000;
217
+ margin-bottom: 0.5em;
218
+ }
219
+
220
+ #winScreen {
221
+ position: absolute;
222
+ top: 0;
223
+ left: 0;
224
+ width: 100%;
225
+ height: 100%;
226
+ background-color: rgba(0, 0, 0, 0.8);
227
+ display: none;
228
+ flex-direction: column;
229
+ justify-content: center;
230
+ align-items: center;
231
+ z-index: 15;
232
+ }
233
+
234
+ #winScreen h1 {
235
+ font-size: 3em;
236
+ color: #00ff00;
237
+ margin-bottom: 0.5em;
238
+ }
239
+
240
+ #damageIndicator {
241
+ position: absolute;
242
+ top: 0;
243
+ left: 0;
244
+ width: 100%;
245
+ height: 100%;
246
+ background-color: rgba(255, 0, 0, 0.3);
247
+ display: none;
248
+ pointer-events: none;
249
+ z-index: 5;
250
+ }
251
+
252
+ #fpsCounter {
253
+ position: absolute;
254
+ top: 10px;
255
+ right: 10px;
256
+ color: #00ff00;
257
+ font-size: 0.8em;
258
+ }
259
+ </style>
260
+ </head>
261
+ <body>
262
+ <div id="gameContainer">
263
+ <canvas id="canvas"></canvas>
264
+ <div id="crosshair"></div>
265
+ <div id="hud">
266
+ <div id="health">HEALTH: <span id="healthValue">100</span></div>
267
+ <div id="ammo">AMMO: <span id="ammoValue">50</span></div>
268
+ <div id="weapon">WEAPON: <span id="weaponName">PISTOL</span></div>
269
+ </div>
270
+ <div id="levelInfo">LEVEL: <span id="levelValue">1</span></div>
271
+ <div id="enemyCount">ENEMIES: <span id="enemyCountValue">5</span></div>
272
+ <div id="mathPopup">
273
+ <p id="mathQuestion">What is 5 + 3?</p>
274
+ <input type="text" id="mathAnswer" placeholder="Enter answer">
275
+ <button id="mathSubmit">SUBMIT</button>
276
+ </div>
277
+ <div id="menu">
278
+ <h1>MATH DOOM</h1>
279
+ <button class="menu-button" id="startButton">START GAME</button>
280
+ <button class="menu-button" id="howToButton">HOW TO PLAY</button>
281
+ </div>
282
+ <div id="deathScreen">
283
+ <h1>YOU DIED</h1>
284
+ <button class="menu-button" id="restartButton">TRY AGAIN</button>
285
+ <button class="menu-button" id="menuButton">MAIN MENU</button>
286
+ </div>
287
+ <div id="winScreen">
288
+ <h1>VICTORY!</h1>
289
+ <button class="menu-button" id="nextLevelButton">NEXT LEVEL</button>
290
+ <button class="menu-button" id="menuButtonWin">MAIN MENU</button>
291
+ </div>
292
+ <div id="damageIndicator"></div>
293
+ </div>
294
+
295
+ <script>
296
+ // Game constants
297
+ const GAME_WIDTH = 800;
298
+ const GAME_HEIGHT = 600;
299
+ const PLAYER_SPEED = 5;
300
+ const ROTATION_SPEED = 0.05;
301
+ const FOV = 60 * Math.PI / 180;
302
+ const WALL_HEIGHT = 100;
303
+ const CEILING_COLOR = '#333333';
304
+ const FLOOR_COLOR = '#222222';
305
+ const WALL_COLORS = ['#880000', '#008800', '#000088', '#888800', '#880088', '#008888'];
306
+ const ENEMY_COLORS = ['#ff0000', '#ff6666', '#cc0000'];
307
+ const PICKUP_COLORS = {
308
+ health: '#00ff00',
309
+ ammo: '#ffff00',
310
+ weapon: '#ffffff'
311
+ };
312
+
313
+ // Game variables
314
+ let canvas, ctx;
315
+ let player = {
316
+ x: 50,
317
+ y: 50,
318
+ angle: 0,
319
+ health: 100,
320
+ maxHealth: 100,
321
+ ammo: 50,
322
+ maxAmmo: 100,
323
+ weapons: ['PISTOL'],
324
+ currentWeapon: 0,
325
+ damageTaken: 0,
326
+ visible: false
327
+ };
328
+ let map = [];
329
+ let enemies = [];
330
+ let pickups = [];
331
+ let rays = [];
332
+ let isMouseLocked = false;
333
+ let isGameRunning = false;
334
+ let currentLevel = 1;
335
+ let enemiesLeft = 0;
336
+ let mouseSensitivity = 0.002;
337
+ let lastTime = 0;
338
+ let fps = 0;
339
+ let deltaTime = 0;
340
+ let keys = {};
341
+ let showFPS = false;
342
+ let mathTimeout = null;
343
+ let currentPickup = null;
344
+
345
+ // Weapon stats
346
+ const weapons = [
347
+ {
348
+ name: 'PISTOL',
349
+ damage: 10,
350
+ fireRate: 500, // ms between shots
351
+ ammoCost: 1,
352
+ range: 500,
353
+ lastShot: 0,
354
+ spread: 0.05,
355
+ ammoType: 'bullet'
356
+ },
357
+ {
358
+ name: 'SHOTGUN',
359
+ damage: 30,
360
+ fireRate: 1000,
361
+ ammoCost: 5,
362
+ range: 300,
363
+ lastShot: 0,
364
+ spread: 0.2,
365
+ ammoType: 'shell'
366
+ },
367
+ {
368
+ name: 'MACHINE GUN',
369
+ damage: 5,
370
+ fireRate: 100,
371
+ ammoCost: 1,
372
+ range: 600,
373
+ lastShot: 0,
374
+ spread: 0.1,
375
+ ammoType: 'bullet'
376
+ }
377
+ ];
378
+
379
+ // Initialize game
380
+ function init() {
381
+ canvas = document.getElementById('canvas');
382
+ ctx = canvas.getContext('2d');
383
+
384
+ canvas.width = GAME_WIDTH;
385
+ canvas.height = GAME_HEIGHT;
386
+
387
+ setupEventListeners();
388
+ generateLevel(1);
389
+ renderMenu();
390
+
391
+ // Start game loop
392
+ requestAnimationFrame(gameLoop);
393
+ }
394
+
395
+ // Set up event listeners
396
+ function setupEventListeners() {
397
+ // Mouse events
398
+ canvas.addEventListener('click', () => {
399
+ if (isGameRunning && !isMouseLocked) {
400
+ canvas.requestPointerLock = canvas.requestPointerLock ||
401
+ canvas.mozRequestPointerLock ||
402
+ canvas.webkitRequestPointerLock;
403
+ canvas.requestPointerLock();
404
+ }
405
+ });
406
+
407
+ document.addEventListener('pointerlockchange', lockChange, false);
408
+ document.addEventListener('mozpointerlockchange', lockChange, false);
409
+ document.addEventListener('webkitpointerlockchange', lockChange, false);
410
+
411
+ function lockChange() {
412
+ isMouseLocked = document.pointerLockElement === canvas ||
413
+ document.mozPointerLockElement === canvas ||
414
+ document.webkitPointerLockElement === canvas;
415
+ }
416
+
417
+ document.addEventListener('mousemove', (e) => {
418
+ if (isMouseLocked && isGameRunning) {
419
+ player.angle += e.movementX * mouseSensitivity;
420
+ }
421
+ });
422
+
423
+ // Keyboard events
424
+ document.addEventListener('keydown', (e) => {
425
+ keys[e.key] = true;
426
+
427
+ if (e.key === 'f') {
428
+ showFPS = !showFPS;
429
+ }
430
+ });
431
+
432
+ document.addEventListener('keyup', (e) => {
433
+ keys[e.key] = false;
434
+ });
435
+
436
+ // Menu buttons
437
+ document.getElementById('startButton').addEventListener('click', startGame);
438
+ document.getElementById('howToButton').addEventListener('click', showHowToPlay);
439
+ document.getElementById('restartButton').addEventListener('click', restartGame);
440
+ document.getElementById('menuButton').addEventListener('click', showMenu);
441
+ document.getElementById('menuButtonWin').addEventListener('click', showMenu);
442
+ document.getElementById('nextLevelButton').addEventListener('click', nextLevel);
443
+ document.getElementById('mathSubmit').addEventListener('click', checkMathAnswer);
444
+ }
445
+
446
+ // Game loop
447
+ function gameLoop(timestamp) {
448
+ deltaTime = timestamp - lastTime;
449
+ lastTime = timestamp;
450
+ fps = 1000 / deltaTime;
451
+
452
+ if (isGameRunning) {
453
+ update();
454
+ render();
455
+ }
456
+
457
+ requestAnimationFrame(gameLoop);
458
+ }
459
+
460
+ // Update game state
461
+ function update() {
462
+ // Player movement
463
+ if (keys['w'] || keys['ArrowUp']) {
464
+ const newX = player.x + Math.cos(player.angle) * PLAYER_SPEED;
465
+ const newY = player.y + Math.sin(player.angle) * PLAYER_SPEED;
466
+ if (!isWall(newX, player.y)) player.x = newX;
467
+ if (!isWall(player.x, newY)) player.y = newY;
468
+ }
469
+ if (keys['s'] || keys['ArrowDown']) {
470
+ const newX = player.x - Math.cos(player.angle) * PLAYER_SPEED;
471
+ const newY = player.y - Math.sin(player.angle) * PLAYER_SPEED;
472
+ if (!isWall(newX, player.y)) player.x = newX;
473
+ if (!isWall(player.x, newY)) player.y = newY;
474
+ }
475
+ if (keys['a'] || keys['ArrowLeft']) {
476
+ const newX = player.x - Math.cos(player.angle + Math.PI/2) * PLAYER_SPEED;
477
+ const newY = player.y - Math.sin(player.angle + Math.PI/2) * PLAYER_SPEED;
478
+ if (!isWall(newX, player.y)) player.x = newX;
479
+ if (!isWall(player.x, newY)) player.y = newY;
480
+ }
481
+ if (keys['d'] || keys['ArrowRight']) {
482
+ const newX = player.x + Math.cos(player.angle + Math.PI/2) * PLAYER_SPEED;
483
+ const newY = player.y + Math.sin(player.angle + Math.PI/2) * PLAYER_SPEED;
484
+ if (!isWall(newX, player.y)) player.x = newX;
485
+ if (!isWall(player.x, newY)) player.y = newY;
486
+ }
487
+
488
+ // Weapon switching
489
+ if (keys['1'] && player.weapons.includes('PISTOL')) {
490
+ player.currentWeapon = 0;
491
+ updateHUD();
492
+ }
493
+ if (keys['2'] && player.weapons.includes('SHOTGUN')) {
494
+ player.currentWeapon = 1;
495
+ updateHUD();
496
+ }
497
+ if (keys['3'] && player.weapons.includes('MACHINE GUN')) {
498
+ player.currentWeapon = 2;
499
+ updateHUD();
500
+ }
501
+
502
+ // Shooting
503
+ if (keys[' '] && Date.now() - weapons[player.currentWeapon].lastShot > weapons[player.currentWeapon].fireRate) {
504
+ if (player.ammo >= weapons[player.currentWeapon].ammoCost) {
505
+ shoot();
506
+ } else {
507
+ // Play empty sound or show message
508
+ }
509
+ }
510
+
511
+ // Enemy AI and actions
512
+ updateEnemies();
513
+
514
+ // Check for pickups
515
+ checkPickups();
516
+
517
+ // Damage indicator
518
+ if (player.damageTaken > 0) {
519
+ const damageIndicator = document.getElementById('damageIndicator');
520
+ damageIndicator.style.display = 'block';
521
+ damageIndicator.style.opacity = player.damageTaken / 100;
522
+ player.damageTaken = Math.max(0, player.damageTaken - 1);
523
+ } else {
524
+ document.getElementById('damageIndicator').style.display = 'none';
525
+ }
526
+
527
+ // Check win/lose conditions
528
+ if (player.health <= 0) {
529
+ gameOver();
530
+ } else if (enemiesLeft <= 0) {
531
+ levelComplete();
532
+ }
533
+ }
534
+
535
+ // Render game
536
+ function render() {
537
+ // Clear canvas
538
+ ctx.clearRect(0, 0, GAME_WIDTH, GAME_HEIGHT);
539
+
540
+ // Draw ceiling and floor
541
+ drawCeilingAndFloor();
542
+
543
+ // Cast rays and draw walls
544
+ castRays();
545
+
546
+ // Draw enemies
547
+ drawEnemies();
548
+
549
+ // Draw pickups
550
+ drawPickups();
551
+
552
+ // Draw minimap (for debugging)
553
+ if (keys['m']) {
554
+ drawMinimap();
555
+ }
556
+
557
+ // FPS counter
558
+ if (showFPS) {
559
+ ctx.fillStyle = '#ffffff';
560
+ ctx.font = '16px Arial';
561
+ ctx.fillText(`FPS: ${Math.round(fps)}`, 10, 20);
562
+ }
563
+ }
564
+
565
+ // Draw ceiling and floor
566
+ function drawCeilingAndFloor() {
567
+ // Ceiling
568
+ ctx.fillStyle = CEILING_COLOR;
569
+ ctx.fillRect(0, 0, GAME_WIDTH, GAME_HEIGHT / 2);
570
+
571
+ // Floor
572
+ ctx.fillStyle = FLOOR_COLOR;
573
+ ctx.fillRect(0, GAME_HEIGHT / 2, GAME_WIDTH, GAME_HEIGHT / 2);
574
+ }
575
+
576
+ // Cast rays for 3D effect
577
+ function castRays() {
578
+ rays = [];
579
+ const rayCount = GAME_WIDTH;
580
+
581
+ for (let x = 0; x < rayCount; x++) {
582
+ const rayAngle = player.angle - FOV / 2 + (x / rayCount) * FOV;
583
+ const ray = castRay(player.x, player.y, rayAngle);
584
+ rays.push(ray);
585
+
586
+ // Draw wall slice
587
+ const distance = ray.distance * Math.cos(ray.angle - player.angle); // Fix fisheye
588
+ const wallHeight = (WALL_HEIGHT / distance) * 277; // Magic number for proper scaling
589
+
590
+ ctx.fillStyle = WALL_COLORS[ray.colorIndex];
591
+ ctx.fillRect(x, (GAME_HEIGHT - wallHeight) / 2, 1, wallHeight);
592
+ }
593
+ }
594
+
595
+ // Cast a single ray
596
+ function castRay(x, y, angle) {
597
+ // Check both vertical and horizontal grid lines
598
+ let vCollision = checkVerticalCollision(x, y, angle);
599
+ let hCollision = checkHorizontalCollision(x, y, angle);
600
+
601
+ // Use the closest collision
602
+ let collision;
603
+ if (!vCollision) {
604
+ collision = hCollision;
605
+ } else if (!hCollision) {
606
+ collision = vCollision;
607
+ } else {
608
+ collision = vCollision.distance < hCollision.distance ? vCollision : hCollision;
609
+ }
610
+
611
+ return {
612
+ x: collision.x,
613
+ y: collision.y,
614
+ distance: collision.distance,
615
+ angle: angle,
616
+ vertical: collision.vertical,
617
+ colorIndex: collision.colorIndex
618
+ };
619
+ }
620
+
621
+ // Check for vertical grid line collisions
622
+ function checkVerticalCollision(x, y, angle) {
623
+ const right = Math.abs(Math.floor((angle - Math.PI / 2) / Math.PI) % 2) === 1;
624
+
625
+ const firstX = right ?
626
+ Math.floor(x / 50) * 50 + 50 :
627
+ Math.ceil(x / 50) * 50 - 50;
628
+ const firstY = y + (firstX - x) * Math.tan(angle);
629
+
630
+ const deltaX = right ? 50 : -50;
631
+ const deltaY = deltaX * Math.tan(angle);
632
+
633
+ let nextX = firstX;
634
+ let nextY = firstY;
635
+
636
+ while (nextX >= 0 && nextX <= map[0].length * 50 &&
637
+ nextY >= 0 && nextY <= map.length * 50) {
638
+ const cellX = right ? Math.floor(nextX / 50) : Math.floor(nextX / 50) - 1;
639
+ const cellY = Math.floor(nextY / 50);
640
+
641
+ if (cellX >= 0 && cellX < map[0].length &&
642
+ cellY >= 0 && cellY < map.length &&
643
+ map[cellY][cellX] > 0) {
644
+ const distance = Math.sqrt(Math.pow(nextX - x, 2) + Math.pow(nextY - y, 2));
645
+ return {
646
+ x: nextX,
647
+ y: nextY,
648
+ distance: distance,
649
+ vertical: true,
650
+ colorIndex: map[cellY][cellX] - 1
651
+ };
652
+ }
653
+
654
+ nextX += deltaX;
655
+ nextY += deltaY;
656
+ }
657
+
658
+ return null;
659
+ }
660
+
661
+ // Check for horizontal grid line collisions
662
+ function checkHorizontalCollision(x, y, angle) {
663
+ const up = Math.abs(Math.floor(angle / Math.PI) % 2) === 0;
664
+
665
+ const firstY = up ?
666
+ Math.floor(y / 50) * 50 - 1 :
667
+ Math.ceil(y / 50) * 50 + 1;
668
+ const firstX = x + (firstY - y) / Math.tan(angle);
669
+
670
+ const deltaY = up ? -50 : 50;
671
+ const deltaX = deltaY / Math.tan(angle);
672
+
673
+ let nextX = firstX;
674
+ let nextY = firstY;
675
+
676
+ while (nextX >= 0 && nextX <= map[0].length * 50 &&
677
+ nextY >= 0 && nextY <= map.length * 50) {
678
+ const cellX = Math.floor(nextX / 50);
679
+ const cellY = up ? Math.floor(nextY / 50) - 1 : Math.floor(nextY / 50);
680
+
681
+ if (cellX >= 0 && cellX < map[0].length &&
682
+ cellY >= 0 && cellY < map.length &&
683
+ map[cellY][cellX] > 0) {
684
+ const distance = Math.sqrt(Math.pow(nextX - x, 2) + Math.pow(nextY - y, 2));
685
+ return {
686
+ x: nextX,
687
+ y: nextY,
688
+ distance: distance,
689
+ vertical: false,
690
+ colorIndex: map[cellY][cellX] - 1
691
+ };
692
+ }
693
+
694
+ nextX += deltaX;
695
+ nextY += deltaY;
696
+ }
697
+
698
+ return null;
699
+ }
700
+
701
+ // Draw enemies
702
+ function drawEnemies() {
703
+ for (let enemy of enemies) {
704
+ if (enemy.health <= 0) continue;
705
+
706
+ // Calculate angle between player and enemy
707
+ const angleToEnemy = Math.atan2(enemy.y - player.y, enemy.x - player.x) - player.angle;
708
+
709
+ // Normalize angle
710
+ let normalizedAngle = angleToEnemy;
711
+ while (normalizedAngle > Math.PI) normalizedAngle -= 2 * Math.PI;
712
+ while (normalizedAngle < -Math.PI) normalizedAngle += 2 * Math.PI;
713
+
714
+ // Check if enemy is in player's FOV
715
+ if (Math.abs(normalizedAngle) < FOV / 2) {
716
+ // Check if there's a wall between player and enemy
717
+ const distToEnemy = Math.sqrt(Math.pow(enemy.x - player.x, 2) + Math.pow(enemy.y - player.y, 2));
718
+ const ray = castRay(player.x, player.y, player.angle + normalizedAngle);
719
+
720
+ if (ray.distance > distToEnemy) {
721
+ // Enemy is visible, draw it
722
+ const enemyHeight = (WALL_HEIGHT * 1.5) / distToEnemy * 277; // Scale similarly to walls
723
+ const screenX = (normalizedAngle + FOV / 2) / FOV * GAME_WIDTH;
724
+
725
+ // Draw enemy
726
+ ctx.fillStyle = ENEMY_COLORS[enemy.type];
727
+ ctx.fillRect(
728
+ screenX - enemyHeight / 4,
729
+ (GAME_HEIGHT - enemyHeight) / 2,
730
+ enemyHeight / 2,
731
+ enemyHeight
732
+ );
733
+
734
+ // Health bar
735
+ ctx.fillStyle = '#ff0000';
736
+ ctx.fillRect(
737
+ screenX - enemyHeight / 4,
738
+ (GAME_HEIGHT - enemyHeight) / 2 - 10,
739
+ enemyHeight / 2,
740
+ 5
741
+ );
742
+ ctx.fillStyle = '#00ff00';
743
+ ctx.fillRect(
744
+ screenX - enemyHeight / 4,
745
+ (GAME_HEIGHT - enemyHeight) / 2 - 10,
746
+ enemyHeight / 2 * (enemy.health / enemy.maxHealth),
747
+ 5
748
+ );
749
+ }
750
+ }
751
+ }
752
+ }
753
+
754
+ // Draw pickups
755
+ function drawPickups() {
756
+ for (let pickup of pickups) {
757
+ // Calculate angle between player and pickup
758
+ const angleToPickup = Math.atan2(pickup.y - player.y, pickup.x - player.x) - player.angle;
759
+
760
+ // Normalize angle
761
+ let normalizedAngle = angleToPickup;
762
+ while (normalizedAngle > Math.PI) normalizedAngle -= 2 * Math.PI;
763
+ while (normalizedAngle < -Math.PI) normalizedAngle += 2 * Math.PI;
764
+
765
+ // Check if pickup is in player's FOV
766
+ if (Math.abs(normalizedAngle) < FOV / 2) {
767
+ // Check if there's a wall between player and pickup
768
+ const distToPickup = Math.sqrt(Math.pow(pickup.x - player.x, 2) + Math.pow(pickup.y - player.y, 2));
769
+ const ray = castRay(player.x, player.y, player.angle + normalizedAngle);
770
+
771
+ if (ray.distance > distToPickup) {
772
+ // Pickup is visible, draw it
773
+ const pickupHeight = (WALL_HEIGHT * 0.5) / distToPickup * 277;
774
+ const screenX = (normalizedAngle + FOV / 2) / FOV * GAME_WIDTH;
775
+
776
+ // Draw pickup
777
+ ctx.fillStyle = pickup.color;
778
+ ctx.beginPath();
779
+ ctx.arc(
780
+ screenX,
781
+ GAME_HEIGHT / 2,
782
+ pickupHeight / 3,
783
+ 0,
784
+ Math.PI * 2
785
+ );
786
+ ctx.fill();
787
+
788
+ // Draw icon based on type
789
+ ctx.fillStyle = '#000000';
790
+ ctx.font = `${pickupHeight / 2}px Arial`;
791
+ ctx.textAlign = 'center';
792
+ ctx.textBaseline = 'middle';
793
+
794
+ let symbol = '';
795
+ if (pickup.type === 'health') symbol = '+';
796
+ else if (pickup.type === 'ammo') symbol = 'A';
797
+ else if (pickup.type === 'weapon') symbol = 'W';
798
+
799
+ ctx.fillText(symbol, screenX, GAME_HEIGHT / 2);
800
+ }
801
+ }
802
+ }
803
+ }
804
+
805
+ // Draw minimap (for debugging)
806
+ function drawMinimap() {
807
+ const minimapSize = 200;
808
+ const cellSize = minimapSize / Math.max(map.length, map[0].length);
809
+
810
+ ctx.fillStyle = 'rgba(0, 0, 0, 0.5)';
811
+ ctx.fillRect(10, 10, minimapSize, minimapSize);
812
+
813
+ // Draw walls
814
+ for (let y = 0; y < map.length; y++) {
815
+ for (let x = 0; x < map[y].length; x++) {
816
+ if (map[y][x] > 0) {
817
+ ctx.fillStyle = WALL_COLORS[map[y][x] - 1];
818
+ ctx.fillRect(10 + x * cellSize, 10 + y * cellSize, cellSize, cellSize);
819
+ }
820
+ }
821
+ }
822
+
823
+ // Draw player
824
+ ctx.fillStyle = '#ffffff';
825
+ ctx.beginPath();
826
+ ctx.arc(
827
+ 10 + player.x / 50 * cellSize,
828
+ 10 + player.y / 50 * cellSize,
829
+ cellSize / 2,
830
+ 0,
831
+ Math.PI * 2
832
+ );
833
+ ctx.fill();
834
+
835
+ // Draw player direction
836
+ ctx.strokeStyle = '#ffffff';
837
+ ctx.lineWidth = 2;
838
+ ctx.beginPath();
839
+ ctx.moveTo(
840
+ 10 + player.x / 50 * cellSize,
841
+ 10 + player.y / 50 * cellSize
842
+ );
843
+ ctx.lineTo(
844
+ 10 + (player.x + Math.cos(player.angle) * 20) / 50 * cellSize,
845
+ 10 + (player.y + Math.sin(player.angle) * 20) / 50 * cellSize
846
+ );
847
+ ctx.stroke();
848
+
849
+ // Draw enemies
850
+ for (let enemy of enemies) {
851
+ if (enemy.health <= 0) continue;
852
+ ctx.fillStyle = ENEMY_COLORS[enemy.type];
853
+ ctx.beginPath();
854
+ ctx.arc(
855
+ 10 + enemy.x / 50 * cellSize,
856
+ 10 + enemy.y / 50 * cellSize,
857
+ cellSize / 2,
858
+ 0,
859
+ Math.PI * 2
860
+ );
861
+ ctx.fill();
862
+ }
863
+
864
+ // Draw pickups
865
+ for (let pickup of pickups) {
866
+ ctx.fillStyle = pickup.color;
867
+ ctx.beginPath();
868
+ ctx.arc(
869
+ 10 + pickup.x / 50 * cellSize,
870
+ 10 + pickup.y / 50 * cellSize,
871
+ cellSize / 3,
872
+ 0,
873
+ Math.PI * 2
874
+ );
875
+ ctx.fill();
876
+ }
877
+ }
878
+
879
+ // Check if a position is a wall
880
+ function isWall(x, y) {
881
+ const cellX = Math.floor(x / 50);
882
+ const cellY = Math.floor(y / 50);
883
+
884
+ if (cellX < 0 || cellX >= map[0].length || cellY < 0 || cellY >= map.length) {
885
+ return true;
886
+ }
887
+
888
+ return map[cellY][cellX] > 0;
889
+ }
890
+
891
+ // Shoot weapon
892
+ function shoot() {
893
+ const weapon = weapons[player.currentWeapon];
894
+
895
+ // Check if player has enough ammo
896
+ if (player.ammo < weapon.ammoCost) return;
897
+
898
+ // Consume ammo
899
+ player.ammo -= weapon.ammoCost;
900
+ weapon.lastShot = Date.now();
901
+
902
+ // Add muzzle flash effect (simple for now)
903
+ ctx.fillStyle = '#ffff00';
904
+ ctx.fillRect(GAME_WIDTH / 2 - 20, GAME_HEIGHT / 2 - 20, 40, 40);
905
+ setTimeout(() => {
906
+ // Flash disappears quickly
907
+ }, 50);
908
+
909
+ // Hit check
910
+ for (let i = 0; i < (player.currentWeapon === 1 ? 5 : 1); i++) { // Shotgun shoots multiple pellets
911
+ const spreadAngle = (Math.random() - 0.5) * weapon.spread;
912
+ const shootAngle = player.angle + spreadAngle;
913
+
914
+ // Cast ray to check for hits
915
+ const hit = castRay(player.x, player.y, shootAngle);
916
+
917
+ // Check if we hit an enemy
918
+ for (let enemy of enemies) {
919
+ if (enemy.health <= 0) continue;
920
+
921
+ // Calculate distance from ray to enemy
922
+ const distanceToEnemy = Math.sqrt(Math.pow(hit.x - enemy.x, 2) + Math.pow(hit.y - enemy.y, 2));
923
+
924
+ // If enemy is close to hit point and not behind a wall
925
+ if (distanceToEnemy < 30 && hit.distance >= Math.sqrt(Math.pow(enemy.x - player.x, 2) + Math.pow(enemy.y - player.y, 2))) {
926
+ // Hit the enemy
927
+ enemy.health -= weapon.damage * (player.currentWeapon === 1 ? 0.8 : 1); // Shotgun pellets do less damage individually
928
+
929
+ // If enemy died, increase score
930
+ if (enemy.health <= 0) {
931
+ enemiesLeft--;
932
+ document.getElementById('enemyCountValue').textContent = enemiesLeft;
933
+ }
934
+ break;
935
+ }
936
+ }
937
+ }
938
+
939
+ updateHUD();
940
+ }
941
+
942
+ // Update enemies
943
+ function updateEnemies() {
944
+ for (let enemy of enemies) {
945
+ if (enemy.health <= 0) continue;
946
+
947
+ // Simple AI: move toward player if visible
948
+ const dx = player.x - enemy.x;
949
+ const dy = player.y - enemy.y;
950
+ const distance = Math.sqrt(dx * dx + dy * dy);
951
+
952
+ // Check if player is visible
953
+ const angleToPlayer = Math.atan2(dy, dx);
954
+ const ray = castRay(enemy.x, enemy.y, angleToPlayer);
955
+
956
+ if (ray.distance >= distance - 10) { // Player is visible
957
+ // Move toward player
958
+ enemy.x += Math.cos(angleToPlayer) * enemy.speed;
959
+ enemy.y += Math.sin(angleToPlayer) * enemy.speed;
960
+
961
+ // Simple attack: damage player if close enough
962
+ if (distance < 50) {
963
+ player.health -= enemy.damage;
964
+ player.damageTaken = 50; // Show damage indicator
965
+ updateHUD();
966
+ }
967
+ } else {
968
+ // Wander randomly
969
+ enemy.x += Math.cos(enemy.direction) * enemy.speed / 2;
970
+ enemy.y += Math.sin(enemy.direction) * enemy.speed / 2;
971
+
972
+ // Randomly change direction
973
+ if (Math.random() < 0.01) {
974
+ enemy.direction = Math.random() * Math.PI * 2;
975
+ }
976
+ }
977
+
978
+ // Collision with walls
979
+ if (isWall(enemy.x, enemy.y)) {
980
+ // Move away from walls
981
+ enemy.x -= Math.cos(angleToPlayer) * enemy.speed;
982
+ enemy.y -= Math.sin(angleToPlayer) * enemy.speed;
983
+ enemy.direction = Math.random() * Math.PI * 2;
984
+ }
985
+ }
986
+ }
987
+
988
+ // Check for pickups
989
+ function checkPickups() {
990
+ if (currentPickup) return; // Already trying to pick up something
991
+
992
+ for (let i = 0; i < pickups.length; i++) {
993
+ const pickup = pickups[i];
994
+ const distance = Math.sqrt(Math.pow(pickup.x - player.x, 2) + Math.pow(pickup.y - player.y, 2));
995
+
996
+ if (distance < 30) { // Close enough to pickup
997
+ currentPickup = pickup;
998
+ showMathPopup(pickup.type);
999
+ break;
1000
+ }
1001
+ }
1002
+ }
1003
+
1004
+ // Show math popup for pickup
1005
+ function showMathPopup(type) {
1006
+ const mathPopup = document.getElementById('mathPopup');
1007
+ const mathQuestion = document.getElementById('mathQuestion');
1008
+ const mathAnswer = document.getElementById('mathAnswer');
1009
+
1010
+ let question, answer;
1011
+ const difficulty = Math.min(currentLevel, 5);
1012
+
1013
+ // Generate math question based on current level (gets harder as level increases)
1014
+ if (difficulty <= 2) {
1015
+ // Addition and subtraction
1016
+ const a = Math.floor(Math.random() * 10) + 1;
1017
+ const b = Math.floor(Math.random() * 10) + 1;
1018
+ const op = Math.random() > 0.5 ? '+' : '-';
1019
+
1020
+ question = `${a} ${op} ${b} = ?`;
1021
+ answer = op === '+' ? a + b : a - b;
1022
+ } else if (difficulty <= 4) {
1023
+ // Multiplication
1024
+ const a = Math.floor(Math.random() * 10) + 1;
1025
+ const b = Math.floor(Math.random() * 5) + 1;
1026
+
1027
+ question = `${a} × ${b} = ?`;
1028
+ answer = a * b;
1029
+ } else {
1030
+ // Division
1031
+ const a = Math.floor(Math.random() * 10) + 1;
1032
+ const b = Math.floor(Math.random() * 5) + 1;
1033
+ const c = a * b;
1034
+
1035
+ question = `${c} ÷ ${b} = ?`;
1036
+ answer = a;
1037
+ }
1038
+
1039
+ mathQuestion.textContent = question;
1040
+ mathAnswer.dataset.answer = answer;
1041
+ mathAnswer.value = '';
1042
+ mathPopup.style.display = 'flex';
1043
+ mathAnswer.focus();
1044
+
1045
+ // Set timeout to auto-close if player doesn't answer
1046
+ if (mathTimeout) clearTimeout(mathTimeout);
1047
+ mathTimeout = setTimeout(() => {
1048
+ mathPopup.style.display = 'none';
1049
+ currentPickup = null;
1050
+ }, 5000);
1051
+ }
1052
+
1053
+ // Check math answer for pickup
1054
+ function checkMathAnswer() {
1055
+ const mathAnswer = document.getElementById('mathAnswer');
1056
+ const correctAnswer = mathAnswer.dataset.answer;
1057
+ const userAnswer = mathAnswer.value.trim();
1058
+
1059
+ document.getElementById('mathPopup').style.display = 'none';
1060
+
1061
+ if (userAnswer === correctAnswer) {
1062
+ // Correct answer - give pickup
1063
+ const pickup = currentPickup;
1064
+
1065
+ if (pickup.type === 'health') {
1066
+ player.health = Math.min(player.health + pickup.value, player.maxHealth);
1067
+ } else if (pickup.type === 'ammo') {
1068
+ player.ammo = Math.min(player.ammo + pickup.value, player.maxAmmo);
1069
+ } else if (pickup.type === 'weapon') {
1070
+ if (!player.weapons.includes(pickup.weaponName)) {
1071
+ player.weapons.push(pickup.weaponName);
1072
+ }
1073
+ player.currentWeapon = weapons.findIndex(w => w.name === pickup.weaponName);
1074
+ player.ammo = Math.min(player.ammo + pickup.ammoBonus, player.maxAmmo);
1075
+ }
1076
+
1077
+ // Remove pickup
1078
+ const index = pickups.indexOf(pickup);
1079
+ if (index !== -1) {
1080
+ pickups.splice(index, 1);
1081
+ }
1082
+
1083
+ updateHUD();
1084
+ }
1085
+
1086
+ currentPickup = null;
1087
+ if (mathTimeout) clearTimeout(mathTimeout);
1088
+ }
1089
+
1090
+ // Update HUD
1091
+ function updateHUD() {
1092
+ document.getElementById('healthValue').textContent = player.health;
1093
+ document.getElementById('ammoValue').textContent = player.ammo;
1094
+ document.getElementById('weaponName').textContent = weapons[player.currentWeapon].name;
1095
+ }
1096
+
1097
+ // Generate level
1098
+ function generateLevel(level) {
1099
+ // Reset game state
1100
+ player.x = 50;
1101
+ player.y = 50;
1102
+ player.angle = 0;
1103
+ player.health = 100;
1104
+ player.ammo = 50;
1105
+
1106
+ // Default weapons
1107
+ player.weapons = ['PISTOL'];
1108
+ player.currentWeapon = 0;
1109
+
1110
+ enemies = [];
1111
+ pickups = [];
1112
+
1113
+ // Create maze-like map
1114
+ map = [];
1115
+ const size = 10 + level * 2; // Increase size with level
1116
+
1117
+ // Initialize empty map
1118
+ for (let y = 0; y < size; y++) {
1119
+ map[y] = [];
1120
+ for (let x = 0; x < size; x++) {
1121
+ map[y][x] = 0;
1122
+ }
1123
+ }
1124
+
1125
+ // Add walls
1126
+ for (let y = 0; y < size; y++) {
1127
+ map[y][0] = 1 + Math.floor(Math.random() * 3);
1128
+ map[y][size-1] = 1 + Math.floor(Math.random() * 3);
1129
+ }
1130
+ for (let x = 0; x < size; x++) {
1131
+ map[0][x] = 1 + Math.floor(Math.random() * 3);
1132
+ map[size-1][x] = 1 + Math.floor(Math.random() * 3);
1133
+ }
1134
+
1135
+ // Add random walls inside
1136
+ for (let i = 0; i < size * 2; i++) {
1137
+ const x = Math.floor(Math.random() * (size - 2)) + 1;
1138
+ const y = Math.floor(Math.random() * (size - 2)) + 1;
1139
+ const length = Math.floor(Math.random() * 3) + 1;
1140
+ const horizontal = Math.random() > 0.5;
1141
+
1142
+ for (let j = 0; j < length; j++) {
1143
+ const nx = horizontal ? x + j : x;
1144
+ const ny = horizontal ? y : y + j;
1145
+
1146
+ if (nx < size && ny < size) {
1147
+ map[ny][nx] = 1 + Math.floor(Math.random() * 3);
1148
+ }
1149
+ }
1150
+ }
1151
+
1152
+ // Make sure player start is open
1153
+ map[1][1] = 0;
1154
+ map[1][2] = 0;
1155
+ map[2][1] = 0;
1156
+
1157
+ // Add enemies
1158
+ enemiesLeft = 3 + level * 2;
1159
+ for (let i = 0; i < enemiesLeft; i++) {
1160
+ let x, y;
1161
+ do {
1162
+ x = Math.floor(Math.random() * (size - 4) + 2) * 50 + 25;
1163
+ y = Math.floor(Math.random() * (size - 4) + 2) * 50 + 25;
1164
+ } while (isWall(x, y));
1165
+
1166
+ enemies.push({
1167
+ x: x,
1168
+ y: y,
1169
+ health: 30 + level * 10,
1170
+ maxHealth: 30 + level * 10,
1171
+ damage: 5 + level,
1172
+ speed: 1 + level * 0.2,
1173
+ direction: Math.random() * Math.PI * 2,
1174
+ type: Math.min(Math.floor(Math.random() * level), ENEMY_COLORS.length - 1)
1175
+ });
1176
+ }
1177
+
1178
+ // Add health pickups
1179
+ for (let i = 0; i < 2 + level; i++) {
1180
+ let x, y;
1181
+ do {
1182
+ x = Math.floor(Math.random() * (size - 4) + 2) * 50 + 25;
1183
+ y = Math.floor(Math.random() * (size - 4) + 2) * 50 + 25;
1184
+ } while (isWall(x, y));
1185
+
1186
+ pickups.push({
1187
+ x: x,
1188
+ y: y,
1189
+ type: 'health',
1190
+ value: 25,
1191
+ color: PICKUP_COLORS.health
1192
+ });
1193
+ }
1194
+
1195
+ // Add ammo pickups
1196
+ for (let i = 0; i < 2 + level; i++) {
1197
+ let x, y;
1198
+ do {
1199
+ x = Math.floor(Math.random() * (size - 4) + 2) * 50 + 25;
1200
+ y = Math.floor(Math.random() * (size - 4) + 2) * 50 + 25;
1201
+ } while (isWall(x, y));
1202
+
1203
+ pickups.push({
1204
+ x: x,
1205
+ y: y,
1206
+ type: 'ammo',
1207
+ value: 20,
1208
+ color: PICKUP_COLORS.ammo
1209
+ });
1210
+ }
1211
+
1212
+ // Add weapon pickups (only if not already has them)
1213
+ if (level > 1 && !player.weapons.includes('SHOTGUN')) {
1214
+ let x, y;
1215
+ do {
1216
+ x = Math.floor(Math.random() * (size - 4) + 2) * 50 + 25;
1217
+ y = Math.floor(Math.random() * (size - 4) + 2) * 50 + 25;
1218
+ } while (isWall(x, y));
1219
+
1220
+ pickups.push({
1221
+ x: x,
1222
+ y: y,
1223
+ type: 'weapon',
1224
+ weaponName: 'SHOTGUN',
1225
+ ammoBonus: 10,
1226
+ color: PICKUP_COLORS.weapon
1227
+ });
1228
+ }
1229
+
1230
+ if (level > 3 && !player.weapons.includes('MACHINE GUN')) {
1231
+ let x, y;
1232
+ do {
1233
+ x = Math.floor(Math.random() * (size - 4) + 2) * 50 + 25;
1234
+ y = Math.floor(Math.random() * (size - 4) + 2) * 50 + 25;
1235
+ } while (isWall(x, y));
1236
+
1237
+ pickups.push({
1238
+ x: x,
1239
+ y: y,
1240
+ type: 'weapon',
1241
+ weaponName: 'MACHINE GUN',
1242
+ ammoBonus: 30,
1243
+ color: PICKUP_COLORS.weapon
1244
+ });
1245
+ }
1246
+
1247
+ // Update HUD
1248
+ document.getElementById('levelValue').textContent = level;
1249
+ document.getElementById('enemyCountValue').textContent = enemiesLeft;
1250
+ updateHUD();
1251
+ }
1252
+
1253
+ // Start game
1254
+ function startGame() {
1255
+ document.getElementById('menu').style.display = 'none';
1256
+ isGameRunning = true;
1257
+ currentLevel = 1;
1258
+ generateLevel(currentLevel);
1259
+
1260
+ // Lock mouse pointer
1261
+ canvas.requestPointerLock = canvas.requestPointerLock ||
1262
+ canvas.mozRequestPointerLock ||
1263
+ canvas.webkitRequestPointerLock;
1264
+ canvas.requestPointerLock();
1265
+ }
1266
+
1267
+ // Restart game
1268
+ function restartGame() {
1269
+ document.getElementById('deathScreen').style.display = 'none';
1270
+ isGameRunning = true;
1271
+ generateLevel(currentLevel);
1272
+
1273
+ // Lock mouse pointer
1274
+ canvas.requestPointerLock = canvas.requestPointerLock ||
1275
+ canvas.mozRequestPointerLock ||
1276
+ canvas.webkitRequestPointerLock;
1277
+ canvas.requestPointerLock();
1278
+ }
1279
+
1280
+ // Next level
1281
+ function nextLevel() {
1282
+ document.getElementById('winScreen').style.display = 'none';
1283
+ isGameRunning = true;
1284
+ currentLevel++;
1285
+ generateLevel(currentLevel);
1286
+
1287
+ // Lock mouse pointer
1288
+ canvas.requestPointerLock = canvas.requestPointerLock ||
1289
+ canvas.mozRequestPointerLock ||
1290
+ canvas.webkitRequestPointerLock;
1291
+ canvas.requestPointerLock();
1292
+ }
1293
+
1294
+ // Show menu
1295
+ function showMenu() {
1296
+ document.getElementById('menu').style.display = 'flex';
1297
+ document.getElementById('deathScreen').style.display = 'none';
1298
+ document.getElementById('winScreen').style.display = 'none';
1299
+ isGameRunning = false;
1300
+
1301
+ // Unlock mouse pointer
1302
+ document.exitPointerLock = document.exitPointerLock ||
1303
+ document.mozExitPointerLock ||
1304
+ document.webkitExitPointerLock;
1305
+ document.exitPointerLock();
1306
+ }
1307
+
1308
+ // Show how to play
1309
+ function showHowToPlay() {
1310
+ alert(
1311
+ "HOW TO PLAY MATH DOOM:\n" +
1312
+ "------------------------\n" +
1313
+ "1. Move with W, A, S, D or arrow keys\n" +
1314
+ "2. Aim with mouse and shoot with SPACE\n" +
1315
+ "3. Switch weapons with 1, 2, 3 keys\n" +
1316
+ "4. To pick up items, solve math problems\n" +
1317
+ "5. Kill all enemies to complete the level\n" +
1318
+ "6. Math problems get harder each level\n" +
1319
+ "\n" +
1320
+ "TIP: Shotgun is good for close range, machine gun for long range!"
1321
+ );
1322
+ }
1323
+
1324
+ // Game over
1325
+ function gameOver() {
1326
+ document.getElementById('deathScreen').style.display = 'flex';
1327
+ isGameRunning = false;
1328
+
1329
+ // Unlock mouse pointer
1330
+ document.exitPointerLock = document.exitPointerLock ||
1331
+ document.mozExitPointerLock ||
1332
+ document.webkitExitPointerLock;
1333
+ document.exitPointerLock();
1334
+ }
1335
+
1336
+ // Level complete
1337
+ function levelComplete() {
1338
+ document.getElementById('winScreen').style.display = 'flex';
1339
+ isGameRunning = false;
1340
+
1341
+ // Unlock mouse pointer
1342
+ document.exitPointerLock = document.exitPointerLock ||
1343
+ document.mozExitPointerLock ||
1344
+ document.webkitExitPointerLock;
1345
+ document.exitPointerLock();
1346
+ }
1347
+
1348
+ // Initialize game when page loads
1349
+ window.onload = init;
1350
+ </script>
1351
+ <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 <a href="https://enzostvs-deepsite.hf.space" style="color: #fff;" target="_blank" >DeepSite</a> <img src="https://enzostvs-deepsite.hf.space/logo.svg" alt="DeepSite Logo" style="width: 16px; height: 16px; vertical-align: middle;"></p></body>
1352
+ </html>