Ramesh-vani commited on
Commit
34d9b9c
·
verified ·
1 Parent(s): 3154d99

Update index.html

Browse files
Files changed (1) hide show
  1. index.html +443 -181
index.html CHANGED
@@ -3,91 +3,117 @@
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,204 +121,440 @@ console.log("jump 0.05");
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>
 
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
  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>