Ramesh-vani commited on
Commit
5faa8a4
·
verified ·
1 Parent(s): 06e6aad

Update index.html

Browse files
Files changed (1) hide show
  1. index.html +560 -19
index.html CHANGED
@@ -1,19 +1,560 @@
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>JS Coding Game - Ultra Advanced Template</title>
7
+ <!-- CodeMirror CSS & JS -->
8
+ <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.65.5/codemirror.min.css">
9
+ <script src="https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.65.5/codemirror.min.js"></script>
10
+ <script src="https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.65.5/mode/javascript/javascript.min.js"></script>
11
+ <!-- Matter.js Library -->
12
+ <script src="https://cdnjs.cloudflare.com/ajax/libs/matter-js/0.18.0/matter.min.js"></script>
13
+ <style>
14
+ body { font-family: Arial, sans-serif; padding: 10px; }
15
+ #editor { width: 100%; height: 200px; }
16
+ #runBtn, #stopBtn { padding: 8px 15px; margin-top: 5px; margin-right: 5px; cursor: pointer; }
17
+ #output { background: black; color: lime; padding: 10px; height: 100px; overflow-y: auto; font-family: monospace; }
18
+ #gameCanvas { border: 1px solid black; background: lightblue; display: block; margin-top: 10px; }
19
+ #guide { background: #f4f4f4; border: 1px solid #ccc; padding: 10px; margin-bottom: 15px; }
20
+ #status { margin-top: 10px; font-family: monospace; }
21
+ h3, h4 { margin: 5px 0; }
22
+ .cooldown { color: gray; }
23
+ </style>
24
+ </head>
25
+ <body>
26
+
27
+ <!-- How To Play Guide -->
28
+ <div id="guide">
29
+ <h3>How to Play</h3>
30
+ <p>
31
+ Write JavaScript in the editor to control the hero in a dynamic world. Define an <code>update()</code> function that runs every 100ms when you click "Run Code." Use "Stop" to pause.
32
+ </p>
33
+ <h4>Commands (with Cooldowns)</h4>
34
+ <ul>
35
+ <li><code>console.log("move 5");</code> – Sets x-velocity (no cooldown).</li>
36
+ <li><code>console.log("jump 0.05");</code> – Jumping force (0.5s cooldown).</li>
37
+ <li><code>console.log("attack sword");</code> – Sword attack (1s cooldown).</li>
38
+ <li><code>console.log("attack bow");</code> – Fires arrow (2s cooldown).</li>
39
+ <li><code>console.log("attack bomb");</code> – Throws bomb (3s cooldown).</li>
40
+ <li><code>console.log("heal 20");</code> – Heals hero (5s cooldown, requires potion).</li>
41
+ </ul>
42
+ <h4>Advanced Features</h4>
43
+ <p>Create continuous logic:</p>
44
+ <pre style="background:#eee; padding:10px;">
45
+ function update() {
46
+ if (hero.position.x < portal.position.x) console.log("move 5");
47
+ if (enemies.length > 0 && hero.inventory.potions > 0 && hero.health < 50) console.log("heal 20");
48
+ }
49
+ </pre>
50
+ <p>Key variables:</p>
51
+ <ul>
52
+ <li><code>hero</code> – Hero body (<code>.health</code>, <code>.inventory</code>, <code>.abilities</code>).</li>
53
+ <li><code>enemies</code> – Array of enemies (incl. boss).</li>
54
+ <li><code>items</code> – Array of collectibles.</li>
55
+ <li><code>portal</code> – Exit portal body.</li>
56
+ <li><code>obstacles</code> – Array of hazards.</li>
57
+ <li><code>engine</code>, <code>world</code> – Matter.js instances.</li>
58
+ </ul>
59
+ </div>
60
+
61
+ <!-- Task Display -->
62
+ <h4>Task: <span id="taskText"></span></h4>
63
+
64
+ <!-- Game Status -->
65
+ <div id="status">
66
+ <p>Health: <span id="health">100</span> | Score: <span id="score">0</span> | Potions: <span id="potions">0</span></p>
67
+ <p>Cooldowns: Jump: <span id="cdJump">Ready</span> | Sword: <span id="cdSword">Ready</span> | Bow: <span id="cdBow">Ready</span> | Bomb: <span id="cdBomb">Ready</span> | Heal: <span id="cdHeal">Ready</span></p>
68
+ </div>
69
+
70
+ <!-- Code Editor -->
71
+ <textarea id="editor">
72
+ // Continuous game logic to clear first level (Collect a potion)
73
+ function update() {
74
+ // Target the first potion at x=200
75
+ const targetX = 200;
76
+
77
+ // Move toward the potion
78
+ if (hero.position.x < targetX - 10) {
79
+ console.log("move 5");
80
+ } else if (hero.position.x > targetX + 10) {
81
+ console.log("move -5");
82
+ } else {
83
+ console.log("move 0"); // Stop near the potion
84
+ }
85
+
86
+ // Jump over obstacles if close
87
+ obstacles.forEach(obstacle => {
88
+ const distanceX = Math.abs(hero.position.x - obstacle.position.x);
89
+ const distanceY = hero.position.y - obstacle.position.y;
90
+ if (distanceX < 50 && distanceY > 0) {
91
+ console.log("jump 0.05");
92
+ }
93
+ });
94
+
95
+ // Stop once potion is collected
96
+ if (hero.inventory.potions > 0) {
97
+ console.log("move 0");
98
+ }
99
+ }
100
+ </textarea>
101
+ <br>
102
+ <button id="runBtn">Run Code</button>
103
+ <button id="stopBtn">Stop</button>
104
+
105
+ <!-- Console Output -->
106
+ <h4>Output:</h4>
107
+ <div id="output"></div>
108
+
109
+ <!-- Game Canvas -->
110
+ <canvas id="gameCanvas"></canvas>
111
+
112
+ <script>
113
+ // --- Setup CodeMirror Editor ---
114
+ const editor = CodeMirror.fromTextArea(document.getElementById("editor"), {
115
+ mode: "javascript",
116
+ lineNumbers: true
117
+ });
118
+
119
+ // --- Matter.js Game Setup ---
120
+ const Engine = Matter.Engine,
121
+ Render = Matter.Render,
122
+ World = Matter.World,
123
+ Bodies = Matter.Bodies,
124
+ Body = Matter.Body,
125
+ Events = Matter.Events;
126
+
127
+ const engine = Engine.create();
128
+ const render = Render.create({
129
+ canvas: document.getElementById("gameCanvas"),
130
+ engine: engine,
131
+ options: { width: 800, height: 400, wireframes: false, background: '#87CEEB' }
132
+ });
133
+
134
+ // Collision Categories
135
+ const categoryHero = 0x0001;
136
+ const categoryEnemy = 0x0002;
137
+ const categoryProjectile = 0x0004;
138
+ const categoryWeapon = 0x0008;
139
+ const categoryItem = 0x0010;
140
+ const categoryStatic = 0x0020;
141
+ const categoryHazard = 0x0040;
142
+
143
+ // --- Procedural Level Generation ---
144
+ const ground = Bodies.rectangle(400, 390, 800, 20, {
145
+ isStatic: true,
146
+ render: { fillStyle: 'brown' },
147
+ collisionFilter: { category: categoryStatic }
148
+ });
149
+
150
+ const obstacles = [];
151
+ for (let i = 0; i < 5; i++) {
152
+ const x = 150 + i * 150;
153
+ const y = 350 - Math.random() * 100;
154
+ obstacles.push(Bodies.rectangle(x, y, 50, 20, {
155
+ isStatic: true,
156
+ render: { fillStyle: 'gray' },
157
+ collisionFilter: { category: categoryHazard, mask: categoryHero | categoryEnemy }
158
+ }));
159
+ }
160
+
161
+ const hero = Bodies.rectangle(50, 350, 40, 40, {
162
+ restitution: 0.5,
163
+ render: { fillStyle: 'blue' },
164
+ collisionFilter: { category: categoryHero, mask: 0xFFFF }
165
+ });
166
+ hero.health = 100;
167
+ hero.inventory = { potions: 0 };
168
+ hero.abilities = {
169
+ jump: { cooldown: 500, lastUsed: 0 },
170
+ sword: { cooldown: 1000, lastUsed: 0 },
171
+ bow: { cooldown: 2000, lastUsed: 0 },
172
+ bomb: { cooldown: 3000, lastUsed: 0 },
173
+ heal: { cooldown: 5000, lastUsed: 0 }
174
+ };
175
+
176
+ const sword = Bodies.rectangle(90, 350, 30, 10, {
177
+ isStatic: true,
178
+ render: { visible: false, fillStyle: 'silver' },
179
+ collisionFilter: { category: categoryWeapon }
180
+ });
181
+
182
+ let arrows = [];
183
+ let bombs = [];
184
+ let enemyProjectiles = [];
185
+
186
+ const enemies = [
187
+ Bodies.rectangle(300, 350, 40, 40, {
188
+ render: { fillStyle: 'red' },
189
+ collisionFilter: { category: categoryEnemy }
190
+ }),
191
+ Bodies.rectangle(450, 350, 40, 40, {
192
+ render: { fillStyle: 'red' },
193
+ collisionFilter: { category: categoryEnemy }
194
+ }),
195
+ Bodies.rectangle(700, 350, 60, 60, {
196
+ render: { fillStyle: 'purple' },
197
+ collisionFilter: { category: categoryEnemy },
198
+ isBoss: true, health: 50
199
+ })
200
+ ];
201
+ enemies.forEach(e => e.isEnemy = true);
202
+
203
+ const items = [
204
+ Bodies.circle(200, 300, 10, {
205
+ isStatic: true,
206
+ render: { fillStyle: 'green' },
207
+ collisionFilter: { category: categoryItem },
208
+ isPotion: true
209
+ }),
210
+ Bodies.circle(500, 300, 10, {
211
+ isStatic: true,
212
+ render: { fillStyle: 'green' },
213
+ collisionFilter: { category: categoryItem },
214
+ isPotion: true
215
+ })
216
+ ];
217
+
218
+ const portal = Bodies.rectangle(750, 350, 40, 40, {
219
+ isStatic: true,
220
+ render: { fillStyle: 'cyan' },
221
+ collisionFilter: { category: categoryStatic },
222
+ isPortal: true
223
+ });
224
+
225
+ let score = 0;
226
+
227
+ World.add(engine.world, [ground, ...obstacles, hero, sword, ...enemies, ...items, portal]);
228
+ Engine.run(engine);
229
+ Render.run(render);
230
+
231
+ // --- Expose Variables Globally ---
232
+ window.engine = engine;
233
+ window.world = engine.world;
234
+ window.hero = hero;
235
+ window.enemies = enemies;
236
+ window.items = items;
237
+ window.obstacles = obstacles;
238
+ window.portal = portal;
239
+ window.sword = sword;
240
+
241
+ // --- Task System ---
242
+ const tasks = [
243
+ { id: 1, text: "Collect a potion", condition: () => hero.inventory.potions > 0 },
244
+ { id: 2, text: "Defeat 2 regular enemies", condition: () => enemies.filter(e => !e.isBoss && e.isRemoved).length >= 2 },
245
+ { id: 3, text: "Defeat the boss (purple enemy)", condition: () => enemies.filter(e => e.isBoss)[0].isRemoved },
246
+ { id: 4, text: "Reach the portal with all enemies defeated", condition: () => enemies.every(e => e.isRemoved) && Matter.Bounds.overlaps(hero.bounds, portal.bounds) }
247
+ ];
248
+ let currentTaskIndex = 0;
249
+ function updateTaskText() {
250
+ document.getElementById("taskText").innerText = tasks[currentTaskIndex].text;
251
+ }
252
+ updateTaskText();
253
+
254
+ // --- Advanced Enemy AI ---
255
+ function moveEnemies() {
256
+ enemies.forEach(enemy => {
257
+ if (enemy.isRemoved) return;
258
+ const dx = hero.position.x - enemy.position.x;
259
+ const speed = enemy.isBoss ? 2 : 1.5;
260
+ Body.setVelocity(enemy, { x: dx > 0 ? speed : -speed, y: enemy.velocity.y });
261
+ if (Math.random() < (enemy.isBoss ? 0.02 : 0.01)) Body.applyForce(enemy, enemy.position, { x: 0, y: -0.05 });
262
+ if (Math.random() < (enemy.isBoss ? 0.02 : 0.005)) enemyFireProjectile(enemy);
263
+ });
264
+ }
265
+ setInterval(moveEnemies, 100);
266
+
267
+ function enemyFireProjectile(enemy) {
268
+ const projectile = Bodies.circle(enemy.position.x, enemy.position.y, enemy.isBoss ? 10 : 5, {
269
+ restitution: 0.2,
270
+ render: { fillStyle: enemy.isBoss ? 'magenta' : 'orange' },
271
+ collisionFilter: { category: categoryProjectile },
272
+ isProjectile: true,
273
+ damage: enemy.isBoss ? 20 : 10
274
+ });
275
+ const dx = hero.position.x - enemy.position.x;
276
+ const dy = hero.position.y - enemy.position.y;
277
+ const angle = Math.atan2(dy, dx);
278
+ Body.setVelocity(projectile, { x: 5 * Math.cos(angle), y: 5 * Math.sin(angle) });
279
+ enemyProjectiles.push(projectile);
280
+ World.add(engine.world, projectile);
281
+ setTimeout(() => {
282
+ enemyProjectiles = enemyProjectiles.filter(p => p !== projectile);
283
+ World.remove(engine.world, projectile);
284
+ }, 3000);
285
+ }
286
+
287
+ // --- Command Handlers with Cooldowns ---
288
+ function customConsoleLog(...args) {
289
+ const outputDiv = document.getElementById("output");
290
+ outputDiv.innerHTML += args.join(" ") + "<br>";
291
+ handleGameAction(args.join(" "));
292
+ }
293
+
294
+ function handleGameAction(command) {
295
+ const parts = command.split(" ");
296
+ const action = parts[0];
297
+ const param = parts[1];
298
+ const value = parseFloat(parts[1]);
299
+ const now = Date.now();
300
+
301
+ function isCooldownReady(ability) {
302
+ return now - hero.abilities[ability].lastUsed >= hero.abilities[ability].cooldown;
303
+ }
304
+
305
+ function updateCooldownDisplay(ability, ready) {
306
+ document.getElementById(`cd${ability.charAt(0).toUpperCase() + ability.slice(1)}`).innerText = ready ? "Ready" : `${((hero.abilities[ability].lastUsed + hero.abilities[ability].cooldown - now) / 1000).toFixed(1)}s`;
307
+ }
308
+
309
+ if (action === "move" && !isNaN(value)) {
310
+ Body.setVelocity(hero, { x: value, y: hero.velocity.y });
311
+ } else if (action === "jump" && !isNaN(value) && isCooldownReady("jump")) {
312
+ Body.applyForce(hero, hero.position, { x: 0, y: -value });
313
+ hero.abilities.jump.lastUsed = now;
314
+ updateCooldownDisplay("jump", false);
315
+ } else if (action === "attack") {
316
+ if (param === "sword" && isCooldownReady("sword")) {
317
+ performSwordAttack();
318
+ hero.abilities.sword.lastUsed = now;
319
+ updateCooldownDisplay("sword", false);
320
+ } else if (param === "bow" && isCooldownReady("bow")) {
321
+ fireArrow();
322
+ hero.abilities.bow.lastUsed = now;
323
+ updateCooldownDisplay("bow", false);
324
+ } else if (param === "bomb" && isCooldownReady("bomb")) {
325
+ throwBomb();
326
+ hero.abilities.bomb.lastUsed = now;
327
+ updateCooldownDisplay("bomb", false);
328
+ }
329
+ } else if (action === "heal" && !isNaN(value) && isCooldownReady("heal") && hero.inventory.potions > 0) {
330
+ hero.health = Math.min(100, hero.health + value);
331
+ hero.inventory.potions--;
332
+ hero.abilities.heal.lastUsed = now;
333
+ document.getElementById("health").innerText = hero.health;
334
+ document.getElementById("potions").innerText = hero.inventory.potions;
335
+ updateCooldownDisplay("heal", false);
336
+ }
337
+ }
338
+
339
+ function performSwordAttack() {
340
+ Body.setPosition(sword, { x: hero.position.x + 30, y: hero.position.y });
341
+ sword.render.visible = true;
342
+ enemies.forEach(enemy => {
343
+ if (!enemy.isRemoved && Matter.Bounds.overlaps(sword.bounds, enemy.bounds)) {
344
+ if (enemy.isBoss) {
345
+ enemy.health -= 10;
346
+ if (enemy.health <= 0) {
347
+ World.remove(engine.world, enemy);
348
+ enemy.isRemoved = true;
349
+ score += 50;
350
+ }
351
+ } else {
352
+ World.remove(engine.world, enemy);
353
+ enemy.isRemoved = true;
354
+ score += 10;
355
+ }
356
+ document.getElementById("score").innerText = score;
357
+ }
358
+ });
359
+ setTimeout(() => sword.render.visible = false, 500);
360
+ }
361
+
362
+ function fireArrow() {
363
+ const arrow = Bodies.rectangle(hero.position.x + 20, hero.position.y, 20, 5, {
364
+ restitution: 0.2,
365
+ render: { fillStyle: 'yellow' },
366
+ collisionFilter: { category: categoryWeapon },
367
+ isArrow: true
368
+ });
369
+ Body.setVelocity(arrow, { x: 5, y: 0 });
370
+ arrows.push(arrow);
371
+ World.add(engine.world, arrow);
372
+ setTimeout(() => {
373
+ arrows = arrows.filter(a => a !== arrow);
374
+ World.remove(engine.world, arrow);
375
+ }, 3000);
376
+ }
377
+
378
+ function throwBomb() {
379
+ const bomb = Bodies.circle(hero.position.x, hero.position.y - 10, 10, {
380
+ restitution: 0.5,
381
+ render: { fillStyle: 'black' },
382
+ collisionFilter: { category: categoryWeapon }
383
+ });
384
+ Body.setVelocity(bomb, { x: 3, y: -3 });
385
+ bombs.push(bomb);
386
+ World.add(engine.world, bomb);
387
+ setTimeout(() => {
388
+ bombs = bombs.filter(b => b !== bomb);
389
+ World.remove(engine.world, bomb);
390
+ enemies.forEach(enemy => {
391
+ if (!enemy.isRemoved && Matter.Bounds.overlaps(bomb.bounds, enemy.bounds)) {
392
+ if (enemy.isBoss) {
393
+ enemy.health -= 20;
394
+ if (enemy.health <= 0) {
395
+ World.remove(engine.world, enemy);
396
+ enemy.isRemoved = true;
397
+ score += 50;
398
+ }
399
+ } else {
400
+ World.remove(engine.world, enemy);
401
+ enemy.isRemoved = true;
402
+ score += 10;
403
+ }
404
+ document.getElementById("score").innerText = score;
405
+ }
406
+ });
407
+ }, 2000);
408
+ }
409
+
410
+ // --- Collision Handling ---
411
+ Events.on(engine, 'collisionStart', function(event) {
412
+ const pairs = event.pairs;
413
+ pairs.forEach(pair => {
414
+ const bodyA = pair.bodyA;
415
+ const bodyB = pair.bodyB;
416
+
417
+ // Hero vs Enemy
418
+ if ((bodyA === hero && bodyB.isEnemy) || (bodyB === hero && bodyA.isEnemy)) {
419
+ hero.health -= bodyA.isBoss || bodyB.isBoss ? 30 : 20;
420
+ checkHeroHealth();
421
+ }
422
+
423
+ // Hero vs Enemy Projectile
424
+ if ((bodyA === hero && bodyB.isProjectile) || (bodyB === hero && bodyA.isProjectile)) {
425
+ const projectile = bodyA.isProjectile ? bodyA : bodyB;
426
+ hero.health -= projectile.damage;
427
+ World.remove(engine.world, projectile);
428
+ enemyProjectiles = enemyProjectiles.filter(p => p !== projectile);
429
+ checkHeroHealth();
430
+ }
431
+
432
+ // Enemy vs Hero Weapon
433
+ if ((bodyA.isArrow && bodyB.isEnemy) || (bodyB.isArrow && bodyA.isEnemy)) {
434
+ const arrow = bodyA.isArrow ? bodyA : bodyB;
435
+ const enemy = bodyA.isEnemy ? bodyA : bodyB;
436
+ if (enemy.isBoss) {
437
+ enemy.health -= 5;
438
+ if (enemy.health <= 0) {
439
+ World.remove(engine.world, enemy);
440
+ enemy.isRemoved = true;
441
+ score += 50;
442
+ }
443
+ } else {
444
+ World.remove(engine.world, enemy);
445
+ enemy.isRemoved = true;
446
+ score += 10;
447
+ }
448
+ World.remove(engine.world, arrow);
449
+ arrows = arrows.filter(a => a !== arrow);
450
+ document.getElementById("score").innerText = score;
451
+ }
452
+
453
+ // Hero vs Item
454
+ if ((bodyA === hero && bodyB.isPotion) || (bodyB === hero && bodyA.isPotion)) {
455
+ const potion = bodyA === hero ? bodyB : bodyA;
456
+ hero.inventory.potions++;
457
+ World.remove(engine.world, potion);
458
+ items.splice(items.indexOf(potion), 1);
459
+ document.getElementById("potions").innerText = hero.inventory.potions;
460
+ }
461
+
462
+ // Hero vs Obstacle (Hazard)
463
+ if ((bodyA === hero && bodyB.collisionFilter.category === categoryHazard) || (bodyB === hero && bodyA.collisionFilter.category === categoryHazard)) {
464
+ hero.health -= 5;
465
+ checkHeroHealth();
466
+ }
467
+ });
468
+ });
469
+
470
+ function checkHeroHealth() {
471
+ document.getElementById("health").innerText = hero.health;
472
+ if (hero.health <= 0) {
473
+ alert("Game Over! Hero defeated.");
474
+ resetGame();
475
+ }
476
+ }
477
+
478
+ // --- Game Logic ---
479
+ function checkTaskCompletion() {
480
+ if (tasks[currentTaskIndex].condition()) {
481
+ alert(`✅ Task Completed: ${tasks[currentTaskIndex].text}`);
482
+ currentTaskIndex++;
483
+ if (currentTaskIndex < tasks.length) {
484
+ updateTaskText();
485
+ resetGame(false);
486
+ } else {
487
+ alert("🎉 Victory! You’ve conquered the level!");
488
+ }
489
+ }
490
+ }
491
+
492
+ function resetGame(fullReset = true) {
493
+ hero.health = 100;
494
+ Body.setPosition(hero, { x: 50, y: 350 });
495
+ Body.setVelocity(hero, { x: 0, y: 0 });
496
+ document.getElementById("health").innerText = hero.health;
497
+ if (fullReset) {
498
+ hero.inventory.potions = 0;
499
+ score = 0;
500
+ document.getElementById("score").innerText = score;
501
+ document.getElementById("potions").innerText = hero.inventory.potions;
502
+ enemies.forEach(e => e.isRemoved && World.remove(engine.world, e));
503
+ enemies.length = 0;
504
+ enemies.push(...[
505
+ Bodies.rectangle(300, 350, 40, 40, { render: { fillStyle: 'red' }, collisionFilter: { category: categoryEnemy } }),
506
+ Bodies.rectangle(450, 350, 40, 40, { render: { fillStyle: 'red' }, collisionFilter: { category: categoryEnemy } }),
507
+ Bodies.rectangle(700, 350, 60, 60, { render: { fillStyle: 'purple' }, collisionFilter: { category: categoryEnemy }, isBoss: true, health: 50 })
508
+ ]);
509
+ enemies.forEach(e => e.isEnemy = true);
510
+ World.add(engine.world, enemies);
511
+ items.length = 0;
512
+ items.push(...[
513
+ Bodies.circle(200, 300, 10, { isStatic: true, render: { fillStyle: 'green' }, collisionFilter: { category: categoryItem }, isPotion: true }),
514
+ Bodies.circle(500, 300, 10, { isStatic: true, render: { fillStyle: 'green' }, collisionFilter: { category: categoryItem }, isPotion: true })
515
+ ]);
516
+ World.add(engine.world, items);
517
+ currentTaskIndex = 0;
518
+ updateTaskText();
519
+ }
520
+ }
521
+
522
+ // --- Continuous Code Execution ---
523
+ let updateInterval;
524
+ document.getElementById("runBtn").addEventListener("click", () => {
525
+ if (updateInterval) clearInterval(updateInterval);
526
+ document.getElementById("output").innerHTML = "";
527
+ const userCode = editor.getValue();
528
+ try {
529
+ const oldConsoleLog = console.log;
530
+ console.log = customConsoleLog;
531
+ const userFunction = new Function(userCode + '; return update;');
532
+ const updateFn = userFunction();
533
+ if (typeof updateFn === 'function') {
534
+ updateInterval = setInterval(() => {
535
+ try {
536
+ updateFn();
537
+ checkTaskCompletion();
538
+ const now = Date.now();
539
+ ['jump', 'sword', 'bow', 'bomb', 'heal'].forEach(ability => {
540
+ if (now - hero.abilities[ability].lastUsed >= hero.abilities[ability].cooldown) {
541
+ document.getElementById(`cd${ability.charAt(0).toUpperCase() + ability.slice(1)}`).innerText = "Ready";
542
+ }
543
+ });
544
+ } catch (error) {
545
+ document.getElementById("output").innerHTML += 'Error in update: ' + error.message + '<br>';
546
+ }
547
+ }, 100);
548
+ }
549
+ console.log = oldConsoleLog;
550
+ } catch (error) {
551
+ document.getElementById("output").innerHTML = "Error: " + error.message;
552
+ }
553
+ });
554
+
555
+ document.getElementById("stopBtn").addEventListener("click", () => {
556
+ if (updateInterval) clearInterval(updateInterval);
557
+ });
558
+ </script>
559
+ </body>
560
+ </html>