Ramesh-vani commited on
Commit
3154d99
Β·
verified Β·
1 Parent(s): 5faa8a4

Update index.html

Browse files
Files changed (1) hide show
  1. index.html +181 -443
index.html CHANGED
@@ -3,117 +3,91 @@
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 ---
@@ -121,440 +95,204 @@ function update() {
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>
 
3
  <head>
4
  <meta charset="UTF-8" />
5
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
+ <title>JS Coding Game - Professional Template with Monaco Editor</title>
7
+ <!-- Monaco Editor Loader -->
8
+ <script src="https://cdnjs.cloudflare.com/ajax/libs/monaco-editor/0.33.0/min/vs/loader.js"></script>
 
 
9
  <!-- Matter.js Library -->
10
  <script src="https://cdnjs.cloudflare.com/ajax/libs/matter-js/0.18.0/matter.min.js"></script>
11
  <style>
12
+ body { font-family: Arial, sans-serif; margin: 0; padding: 20px; background: #f9f9f9; }
13
+ #container { max-width: 1000px; margin: auto; }
14
+ #guide { background: #ffffff; border: 1px solid #ccc; padding: 15px; margin-bottom: 20px; }
15
+ #guide h3, #guide h4 { margin-top: 0; }
16
+ #taskPanel { background: #ffffff; border: 1px solid #ccc; padding: 10px; margin-bottom: 10px; }
17
+ #editor { width: 100%; height: 250px; border: 1px solid #ccc; }
18
+ #runBtn { padding: 10px 20px; margin-top: 10px; font-size: 16px; cursor: pointer; }
19
+ #gameCanvas { display: block; margin-top: 20px; border: 1px solid #333; background: #e0f7fa; }
 
20
  </style>
21
  </head>
22
  <body>
23
+ <div id="container">
24
+ <!-- How To Play Guide -->
25
+ <div id="guide">
26
+ <h3>How to Play</h3>
27
+ <p>
28
+ Control the hero by writing JavaScript commands in the editor below.
29
+ When you click "Run Code," your commands will be executed and affect the game in real time.
30
+ </p>
31
+ <h4>Basic Commands</h4>
32
+ <ul>
33
+ <li><code>console.log("move 5");</code> – Moves the hero to the right (sets hero’s x velocity to 5).</li>
34
+ <li><code>console.log("jump 0.05");</code> – Makes the hero jump by applying an upward force.</li>
35
+ <li><code>console.log("attack sword");</code> – Attacks with the sword (removes an enemy if in range).</li>
36
+ <li><code>console.log("attack bow");</code> – Fires an arrow from the hero.</li>
37
+ <li><code>console.log("attack bomb");</code> – Throws a bomb that explodes after a short delay.</li>
38
+ </ul>
39
+ <h4>Advanced Logic</h4>
40
+ <p>
41
+ You can access global game variables directly to build advanced logic. For example:
42
+ </p>
43
+ <pre style="background:#eee; padding:10px;">if (hero.velocity.x &gt; 4) {
44
+ console.log("Hero is speeding!");
45
+ }</pre>
46
+ <p>The following game variables are available:</p>
47
+ <ul>
48
+ <li><code>hero</code> – The hero’s Matter.js body (position, velocity, etc.).</li>
49
+ <li><code>engine</code> – The Matter.js engine instance.</li>
50
+ <li><code>world</code> – The Matter.js world containing all game bodies.</li>
51
+ <li><code>enemies</code> – An array of enemy bodies.</li>
52
+ <li><code>sword</code> – The sword body used for melee attacks.</li>
53
+ </ul>
54
+ </div>
55
+
56
+ <!-- Task Display Panel -->
57
+ <div id="taskPanel">
58
+ <strong>Current Task:</strong> <span id="taskText"></span>
59
+ </div>
60
+
61
+ <!-- Monaco Editor Container -->
62
+ <div id="editor"></div>
63
+ <button id="runBtn">Run Code</button>
64
+
65
+ <!-- Game Canvas -->
66
+ <canvas id="gameCanvas" width="600" height="300"></canvas>
67
  </div>
68
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
69
  <script>
70
+ // --- Initialize Monaco Editor ---
71
+ require.config({ paths: { 'vs': 'https://cdnjs.cloudflare.com/ajax/libs/monaco-editor/0.33.0/min/vs' }});
72
+ let monacoEditor;
73
+ require(['vs/editor/editor.main'], function() {
74
+ monacoEditor = monaco.editor.create(document.getElementById('editor'), {
75
+ value: `// Example Commands:
76
+ // Basic movement:
77
+ console.log("move 5");
78
+ console.log("jump 0.05");
79
+
80
+ // Advanced logic example:
81
+ // if (hero.velocity.x > 4) { console.log("Hero is speeding!"); }
82
+
83
+ // To attack using different weapons:
84
+ // console.log("attack sword");
85
+ // console.log("attack bow");
86
+ // console.log("attack bomb");`,
87
+ language: "javascript",
88
+ theme: "vs-light",
89
+ automaticLayout: true
90
+ });
91
  });
92
 
93
  // --- Matter.js Game Setup ---
 
95
  Render = Matter.Render,
96
  World = Matter.World,
97
  Bodies = Matter.Bodies,
98
+ Body = Matter.Body;
 
99
 
100
  const engine = Engine.create();
101
  const render = Render.create({
102
  canvas: document.getElementById("gameCanvas"),
103
  engine: engine,
104
+ options: { width: 600, height: 300, wireframes: false }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
105
  });
106
 
107
+ // --- Create Game Bodies ---
108
+ const ground = Bodies.rectangle(300, 290, 600, 20, { isStatic: true });
109
+ const hero = Bodies.rectangle(100, 250, 40, 40, { restitution: 0.5 });
110
+
111
+ // Weapons:
112
+ const sword = Bodies.rectangle(140, 250, 30, 10, { isStatic: true, render: { visible: false } });
113
  let arrows = [];
114
  let bombs = [];
115
+
116
+ // Enemies:
117
  const enemies = [
118
+ Bodies.rectangle(400, 250, 40, 40, { isStatic: false }),
119
+ Bodies.rectangle(550, 250, 40, 40, { isStatic: false })
 
 
 
 
 
 
 
 
 
 
 
120
  ];
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
121
 
122
+ World.add(engine.world, [ground, hero, sword, ...enemies]);
123
  Engine.run(engine);
124
  Render.run(render);
125
 
126
+ // --- Expose Key Variables for Advanced Access ---
127
  window.engine = engine;
128
+ window.render = render;
129
  window.world = engine.world;
130
  window.hero = hero;
 
 
 
 
131
  window.sword = sword;
132
+ window.enemies = enemies;
133
 
134
+ // --- Task System: 50 Tasks ---
135
  const tasks = [
136
+ { id: 1, text: "Reach x = 200", condition: () => hero.position.x >= 200 },
137
+ { id: 2, text: "Defeat at least one enemy", condition: () => enemies.some(e => e.isRemoved) },
138
+ { id: 3, text: "Defeat all enemies", condition: () => enemies.every(e => e.isRemoved) },
139
+ { id: 4, text: "Jump above y = 180", condition: () => hero.position.y <= 180 },
140
+ { id: 5, text: "Reach x = 300", condition: () => hero.position.x >= 300 },
141
+ { id: 6, text: "Reach x = 400", condition: () => hero.position.x >= 400 },
142
+ { id: 7, text: "Reach x = 500", condition: () => hero.position.x >= 500 },
143
+ { id: 8, text: "Move with speed > 3", condition: () => Math.abs(hero.velocity.x) > 3 },
144
+ { id: 9, text: "Move left with speed > 3", condition: () => hero.velocity.x < -3 },
145
+ { id: 10, text: "Land with low y-velocity (< 2)", condition: () => Math.abs(hero.velocity.y) < 2 },
146
+ { id: 11, text: "Stay on ground (y > 240)", condition: () => hero.position.y > 240 },
147
+ { id: 12, text: "Achieve x = 150", condition: () => hero.position.x >= 150 },
148
+ { id: 13, text: "Achieve x = 250", condition: () => hero.position.x >= 250 },
149
+ { id: 14, text: "Achieve x = 350", condition: () => hero.position.x >= 350 },
150
+ { id: 15, text: "Achieve x = 450", condition: () => hero.position.x >= 450 },
151
+ { id: 16, text: "Perform a moderate jump (y between 240 and 260)", condition: () => hero.position.y >= 240 && hero.position.y <= 260 },
152
+ { id: 17, text: "Move 100 units from start", condition: () => hero.position.x - 100 >= 100 },
153
+ { id: 18, text: "Hero's x velocity is nearly 0", condition: () => Math.abs(hero.velocity.x) < 0.1 },
154
+ { id: 19, text: "Hero's y velocity is nearly 0", condition: () => Math.abs(hero.velocity.y) < 0.1 },
155
+ { id: 20, text: "Attack using sword to defeat an enemy", condition: () => enemies.some(e => e.isRemoved) },
156
+ { id: 21, text: "Attack using bow to defeat an enemy", condition: () => enemies.some(e => e.isRemoved) },
157
+ { id: 22, text: "Attack using bomb to defeat an enemy", condition: () => enemies.some(e => e.isRemoved) },
158
+ { id: 23, text: "Move and jump simultaneously", condition: () => hero.position.x >= 120 && hero.position.y <= 240 },
159
+ { id: 24, text: "Achieve a sprint speed (|velocity.x| >= 7)", condition: () => Math.abs(hero.velocity.x) >= 7 },
160
+ { id: 25, text: "Perform a duck action", condition: () => hero.render && hero.render.fillStyle === "blue" },
161
+ { id: 26, text: "Perform a dash move (|velocity.x| >= 5)", condition: () => Math.abs(hero.velocity.x) >= 5 },
162
+ { id: 27, text: "Change direction (velocity.x becomes negative)", condition: () => hero.velocity.x < 0 },
163
+ { id: 28, text: "Move upward quickly (velocity.y < -2)", condition: () => hero.velocity.y < -2 },
164
+ { id: 29, text: "Stay completely still", condition: () => Math.abs(hero.velocity.x) < 0.1 && Math.abs(hero.velocity.y) < 0.1 },
165
+ { id: 30, text: "Reach far right (x >= 550)", condition: () => hero.position.x >= 550 },
166
+ { id: 31, text: "Reach far left (x <= 50)", condition: () => hero.position.x <= 50 },
167
+ { id: 32, text: "Perform a combo attack (all enemies defeated)", condition: () => enemies.every(e => e.isRemoved) },
168
+ { id: 33, text: "Attack with bow and bomb consecutively", condition: () => enemies.every(e => e.isRemoved) },
169
+ { id: 34, text: "Show high energy (dummy condition)", condition: () => hero.position.x > 0 },
170
+ { id: 35, text: "Perform a precise jump (y < 230)", condition: () => hero.position.y < 230 },
171
+ { id: 36, text: "Land softly (|velocity.y| < 1)", condition: () => Math.abs(hero.velocity.y) < 1 },
172
+ { id: 37, text: "Achieve horizontal acceleration > 4", condition: () => Math.abs(hero.velocity.x) > 4 },
173
+ { id: 38, text: "Avoid enemy collision", condition: () => enemies.every(e => !Matter.Bounds.overlaps(hero.bounds, e.bounds)) },
174
+ { id: 39, text: "Come near an enemy (within 50 units) without attacking", condition: () => enemies.some(e => Math.abs(hero.position.x - e.position.x) < 50 && !e.isRemoved) },
175
+ { id: 40, text: "Reset near starting point (x < 120)", condition: () => hero.position.x < 120 },
176
+ { id: 41, text: "Jump height exceeds 30 units", condition: () => (250 - hero.position.y) >= 30 },
177
+ { id: 42, text: "Maintain steady movement (velocity.x ~ 3)", condition: () => Math.abs(hero.velocity.x - 3) < 1 },
178
+ { id: 43, text: "Attack then move (enemy defeated and x > 150)", condition: () => enemies.some(e => e.isRemoved) && hero.position.x > 150 },
179
+ { id: 44, text: "Keep speed constant (velocity.x ~ 3)", condition: () => Math.abs(hero.velocity.x - 3) < 0.5 },
180
+ { id: 45, text: "Show agility (jump and dash with |velocity.x| > 4)", condition: () => hero.position.y < 240 && Math.abs(hero.velocity.x) > 4 },
181
+ { id: 46, text: "Combo move achieves x >= 350", condition: () => hero.position.x >= 350 },
182
+ { id: 47, text: "Recover from an attack (x >= 100)", condition: () => hero.position.x >= 100 },
183
+ { id: 48, text: "Dodge enemy attack (no overlap with any enemy)", condition: () => enemies.every(e => !Matter.Bounds.overlaps(hero.bounds, e.bounds)) },
184
+ { id: 49, text: "Exhibit erratic movement (|velocity.x| > 5)", condition: () => Math.abs(hero.velocity.x) > 5 },
185
+ { id: 50, text: "Final Task: Overcome all challenges (x >= 550 and all enemies defeated)", condition: () => hero.position.x >= 550 && enemies.every(e => e.isRemoved) }
186
  ];
187
+
188
  let currentTaskIndex = 0;
189
  function updateTaskText() {
190
  document.getElementById("taskText").innerText = tasks[currentTaskIndex].text;
191
  }
192
  updateTaskText();
193
 
194
+ // --- Enemy Movement (Simple AI) ---
195
  function moveEnemies() {
196
+ enemies.forEach((enemy) => {
197
  if (enemy.isRemoved) return;
198
  const dx = hero.position.x - enemy.position.x;
199
+ const speed = dx > 0 ? 1.5 : -1.5;
200
+ Body.setVelocity(enemy, { x: speed, y: 0 });
 
 
201
  });
202
  }
203
  setInterval(moveEnemies, 100);
204
 
205
+ // --- Command Parser (No debug console output) ---
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
206
  function handleGameAction(command) {
207
  const parts = command.split(" ");
208
  const action = parts[0];
209
  const param = parts[1];
210
  const value = parseFloat(parts[1]);
 
 
 
 
 
 
 
 
 
211
 
212
  if (action === "move" && !isNaN(value)) {
213
  Body.setVelocity(hero, { x: value, y: hero.velocity.y });
214
+ } else if (action === "jump" && !isNaN(value)) {
215
  Body.applyForce(hero, hero.position, { x: 0, y: -value });
 
 
216
  } else if (action === "attack") {
217
+ if (param === "sword") performSwordAttack();
218
+ if (param === "bow") fireArrow();
219
+ if (param === "bomb") throwBomb();
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
220
  }
221
  }
222
 
223
  function performSwordAttack() {
224
  Body.setPosition(sword, { x: hero.position.x + 30, y: hero.position.y });
225
  sword.render.visible = true;
226
+ enemies.forEach((enemy) => {
227
  if (!enemy.isRemoved && Matter.Bounds.overlaps(sword.bounds, enemy.bounds)) {
228
+ World.remove(engine.world, enemy);
229
+ enemy.isRemoved = true;
 
 
 
 
 
 
 
 
 
 
 
230
  }
231
  });
232
+ setTimeout(() => { sword.render.visible = false; }, 500);
233
  }
234
 
235
  function fireArrow() {
236
+ const arrow = Bodies.rectangle(hero.position.x + 20, hero.position.y, 20, 5, { restitution: 0.2 });
 
 
 
 
 
237
  Body.setVelocity(arrow, { x: 5, y: 0 });
238
  arrows.push(arrow);
239
  World.add(engine.world, arrow);
240
+ setTimeout(() => { World.remove(engine.world, arrow); }, 3000);
 
 
 
241
  }
242
 
243
  function throwBomb() {
244
+ const bomb = Bodies.circle(hero.position.x, hero.position.y - 10, 10, { restitution: 0.5 });
 
 
 
 
245
  Body.setVelocity(bomb, { x: 3, y: -3 });
246
  bombs.push(bomb);
247
  World.add(engine.world, bomb);
248
  setTimeout(() => {
249
  bombs = bombs.filter(b => b !== bomb);
250
  World.remove(engine.world, bomb);
251
+ enemies.forEach((enemy) => {
252
  if (!enemy.isRemoved && Matter.Bounds.overlaps(bomb.bounds, enemy.bounds)) {
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
253
  World.remove(engine.world, enemy);
254
  enemy.isRemoved = true;
 
255
  }
256
+ });
257
+ }, 2000);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
258
  }
259
 
260
+ // --- Check Task Completion ---
261
  function checkTaskCompletion() {
262
  if (tasks[currentTaskIndex].condition()) {
263
  alert(`βœ… Task Completed: ${tasks[currentTaskIndex].text}`);
264
  currentTaskIndex++;
265
  if (currentTaskIndex < tasks.length) {
266
  updateTaskText();
267
+ resetHeroPosition();
268
  } else {
269
+ alert("πŸŽ‰ All Tasks Completed!");
270
  }
271
  }
272
  }
273
 
274
+ // --- Run Code Button Event ---
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
275
  document.getElementById("runBtn").addEventListener("click", () => {
276
+ // Retrieve user code from Monaco Editor
277
+ const userCode = monacoEditor.getValue();
 
278
  try {
279
+ // Override console.log temporarily to capture commands (without on-screen debug)
280
  const oldConsoleLog = console.log;
281
+ console.log = function(msg) {
282
+ handleGameAction(msg);
283
+ };
284
+ new Function(userCode)();
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
285
  console.log = oldConsoleLog;
286
+ setTimeout(checkTaskCompletion, 1000);
287
  } catch (error) {
288
+ alert("Error: " + error.message);
289
  }
290
  });
291
 
292
+ function resetHeroPosition() {
293
+ Body.setPosition(hero, { x: 100, y: 250 });
294
+ Body.setVelocity(hero, { x: 0, y: 0 });
295
+ }
296
  </script>
297
  </body>
298
+ </html>