File size: 14,538 Bytes
5faa8a4
 
 
 
 
3154d99
 
 
5faa8a4
 
 
3154d99
 
 
 
 
 
 
 
5faa8a4
 
 
3154d99
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
5faa8a4
 
 
3154d99
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
5faa8a4
 
 
 
 
 
 
3154d99
5faa8a4
 
 
 
 
3154d99
5faa8a4
 
3154d99
 
 
 
 
 
5faa8a4
 
3154d99
 
5faa8a4
3154d99
 
5faa8a4
 
3154d99
5faa8a4
 
 
3154d99
5faa8a4
3154d99
5faa8a4
 
 
3154d99
5faa8a4
3154d99
5faa8a4
3154d99
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
5faa8a4
3154d99
5faa8a4
 
 
 
 
 
3154d99
5faa8a4
3154d99
5faa8a4
 
3154d99
 
5faa8a4
 
 
 
3154d99
5faa8a4
 
 
 
 
 
 
 
3154d99
5faa8a4
 
3154d99
 
 
5faa8a4
 
 
 
 
 
3154d99
5faa8a4
3154d99
 
5faa8a4
 
3154d99
5faa8a4
 
 
3154d99
5faa8a4
 
 
3154d99
5faa8a4
 
 
3154d99
5faa8a4
 
 
 
 
 
3154d99
5faa8a4
 
 
 
3154d99
 
5faa8a4
 
3154d99
5faa8a4
 
 
 
 
 
3154d99
5faa8a4
3154d99
5faa8a4
 
 
 
3154d99
5faa8a4
3154d99
 
5faa8a4
3154d99
5faa8a4
3154d99
 
 
 
5faa8a4
3154d99
5faa8a4
3154d99
5faa8a4
 
 
3154d99
 
 
 
5faa8a4
 
3154d99
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8" />
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>JS Coding Game - Professional Template with Monaco Editor</title>
  <!-- Monaco Editor Loader -->
  <script src="https://cdnjs.cloudflare.com/ajax/libs/monaco-editor/0.33.0/min/vs/loader.js"></script>
  <!-- Matter.js Library -->
  <script src="https://cdnjs.cloudflare.com/ajax/libs/matter-js/0.18.0/matter.min.js"></script>
  <style>
    body { font-family: Arial, sans-serif; margin: 0; padding: 20px; background: #f9f9f9; }
    #container { max-width: 1000px; margin: auto; }
    #guide { background: #ffffff; border: 1px solid #ccc; padding: 15px; margin-bottom: 20px; }
    #guide h3, #guide h4 { margin-top: 0; }
    #taskPanel { background: #ffffff; border: 1px solid #ccc; padding: 10px; margin-bottom: 10px; }
    #editor { width: 100%; height: 250px; border: 1px solid #ccc; }
    #runBtn { padding: 10px 20px; margin-top: 10px; font-size: 16px; cursor: pointer; }
    #gameCanvas { display: block; margin-top: 20px; border: 1px solid #333; background: #e0f7fa; }
  </style>
</head>
<body>
  <div id="container">
    <!-- How To Play Guide -->
    <div id="guide">
      <h3>How to Play</h3>
      <p>
        Control the hero by writing JavaScript commands in the editor below.
        When you click "Run Code," your commands will be executed and affect the game in real time.
      </p>
      <h4>Basic Commands</h4>
      <ul>
        <li><code>console.log("move 5");</code> – Moves the hero to the right (sets hero’s x velocity to 5).</li>
        <li><code>console.log("jump 0.05");</code> – Makes the hero jump by applying an upward force.</li>
        <li><code>console.log("attack sword");</code> – Attacks with the sword (removes an enemy if in range).</li>
        <li><code>console.log("attack bow");</code> – Fires an arrow from the hero.</li>
        <li><code>console.log("attack bomb");</code> – Throws a bomb that explodes after a short delay.</li>
      </ul>
      <h4>Advanced Logic</h4>
      <p>
        You can access global game variables directly to build advanced logic. For example:
      </p>
      <pre style="background:#eee; padding:10px;">if (hero.velocity.x &gt; 4) {
  console.log("Hero is speeding!");
}</pre>
      <p>The following game variables are available:</p>
      <ul>
        <li><code>hero</code> – The hero’s Matter.js body (position, velocity, etc.).</li>
        <li><code>engine</code> – The Matter.js engine instance.</li>
        <li><code>world</code> – The Matter.js world containing all game bodies.</li>
        <li><code>enemies</code> – An array of enemy bodies.</li>
        <li><code>sword</code> – The sword body used for melee attacks.</li>
      </ul>
    </div>

    <!-- Task Display Panel -->
    <div id="taskPanel">
      <strong>Current Task:</strong> <span id="taskText"></span>
    </div>

    <!-- Monaco Editor Container -->
    <div id="editor"></div>
    <button id="runBtn">Run Code</button>

    <!-- Game Canvas -->
    <canvas id="gameCanvas" width="600" height="300"></canvas>
  </div>

  <script>
    // --- Initialize Monaco Editor ---
    require.config({ paths: { 'vs': 'https://cdnjs.cloudflare.com/ajax/libs/monaco-editor/0.33.0/min/vs' }});
    let monacoEditor;
    require(['vs/editor/editor.main'], function() {
      monacoEditor = monaco.editor.create(document.getElementById('editor'), {
        value: `// Example Commands:
// Basic movement:
console.log("move 5");
console.log("jump 0.05");

// Advanced logic example:
// if (hero.velocity.x > 4) { console.log("Hero is speeding!"); }

// To attack using different weapons:
// console.log("attack sword");
// console.log("attack bow");
// console.log("attack bomb");`,
        language: "javascript",
        theme: "vs-light",
        automaticLayout: true
      });
    });

    // --- Matter.js Game Setup ---
    const Engine = Matter.Engine,
          Render = Matter.Render,
          World = Matter.World,
          Bodies = Matter.Bodies,
          Body = Matter.Body;

    const engine = Engine.create();
    const render = Render.create({
      canvas: document.getElementById("gameCanvas"),
      engine: engine,
      options: { width: 600, height: 300, wireframes: false }
    });

    // --- Create Game Bodies ---
    const ground = Bodies.rectangle(300, 290, 600, 20, { isStatic: true });
    const hero = Bodies.rectangle(100, 250, 40, 40, { restitution: 0.5 });
    
    // Weapons:
    const sword = Bodies.rectangle(140, 250, 30, 10, { isStatic: true, render: { visible: false } });
    let arrows = [];
    let bombs = [];
    
    // Enemies:
    const enemies = [
      Bodies.rectangle(400, 250, 40, 40, { isStatic: false }),
      Bodies.rectangle(550, 250, 40, 40, { isStatic: false })
    ];

    World.add(engine.world, [ground, hero, sword, ...enemies]);
    Engine.run(engine);
    Render.run(render);

    // --- Expose Key Variables for Advanced Access ---
    window.engine = engine;
    window.render = render;
    window.world = engine.world;
    window.hero = hero;
    window.sword = sword;
    window.enemies = enemies;

    // --- Task System: 50 Tasks ---
    const tasks = [
      { id: 1, text: "Reach x = 200", condition: () => hero.position.x >= 200 },
      { id: 2, text: "Defeat at least one enemy", condition: () => enemies.some(e => e.isRemoved) },
      { id: 3, text: "Defeat all enemies", condition: () => enemies.every(e => e.isRemoved) },
      { id: 4, text: "Jump above y = 180", condition: () => hero.position.y <= 180 },
      { id: 5, text: "Reach x = 300", condition: () => hero.position.x >= 300 },
      { id: 6, text: "Reach x = 400", condition: () => hero.position.x >= 400 },
      { id: 7, text: "Reach x = 500", condition: () => hero.position.x >= 500 },
      { id: 8, text: "Move with speed > 3", condition: () => Math.abs(hero.velocity.x) > 3 },
      { id: 9, text: "Move left with speed > 3", condition: () => hero.velocity.x < -3 },
      { id: 10, text: "Land with low y-velocity (< 2)", condition: () => Math.abs(hero.velocity.y) < 2 },
      { id: 11, text: "Stay on ground (y > 240)", condition: () => hero.position.y > 240 },
      { id: 12, text: "Achieve x = 150", condition: () => hero.position.x >= 150 },
      { id: 13, text: "Achieve x = 250", condition: () => hero.position.x >= 250 },
      { id: 14, text: "Achieve x = 350", condition: () => hero.position.x >= 350 },
      { id: 15, text: "Achieve x = 450", condition: () => hero.position.x >= 450 },
      { id: 16, text: "Perform a moderate jump (y between 240 and 260)", condition: () => hero.position.y >= 240 && hero.position.y <= 260 },
      { id: 17, text: "Move 100 units from start", condition: () => hero.position.x - 100 >= 100 },
      { id: 18, text: "Hero's x velocity is nearly 0", condition: () => Math.abs(hero.velocity.x) < 0.1 },
      { id: 19, text: "Hero's y velocity is nearly 0", condition: () => Math.abs(hero.velocity.y) < 0.1 },
      { id: 20, text: "Attack using sword to defeat an enemy", condition: () => enemies.some(e => e.isRemoved) },
      { id: 21, text: "Attack using bow to defeat an enemy", condition: () => enemies.some(e => e.isRemoved) },
      { id: 22, text: "Attack using bomb to defeat an enemy", condition: () => enemies.some(e => e.isRemoved) },
      { id: 23, text: "Move and jump simultaneously", condition: () => hero.position.x >= 120 && hero.position.y <= 240 },
      { id: 24, text: "Achieve a sprint speed (|velocity.x| >= 7)", condition: () => Math.abs(hero.velocity.x) >= 7 },
      { id: 25, text: "Perform a duck action", condition: () => hero.render && hero.render.fillStyle === "blue" },
      { id: 26, text: "Perform a dash move (|velocity.x| >= 5)", condition: () => Math.abs(hero.velocity.x) >= 5 },
      { id: 27, text: "Change direction (velocity.x becomes negative)", condition: () => hero.velocity.x < 0 },
      { id: 28, text: "Move upward quickly (velocity.y < -2)", condition: () => hero.velocity.y < -2 },
      { id: 29, text: "Stay completely still", condition: () => Math.abs(hero.velocity.x) < 0.1 && Math.abs(hero.velocity.y) < 0.1 },
      { id: 30, text: "Reach far right (x >= 550)", condition: () => hero.position.x >= 550 },
      { id: 31, text: "Reach far left (x <= 50)", condition: () => hero.position.x <= 50 },
      { id: 32, text: "Perform a combo attack (all enemies defeated)", condition: () => enemies.every(e => e.isRemoved) },
      { id: 33, text: "Attack with bow and bomb consecutively", condition: () => enemies.every(e => e.isRemoved) },
      { id: 34, text: "Show high energy (dummy condition)", condition: () => hero.position.x > 0 },
      { id: 35, text: "Perform a precise jump (y < 230)", condition: () => hero.position.y < 230 },
      { id: 36, text: "Land softly (|velocity.y| < 1)", condition: () => Math.abs(hero.velocity.y) < 1 },
      { id: 37, text: "Achieve horizontal acceleration > 4", condition: () => Math.abs(hero.velocity.x) > 4 },
      { id: 38, text: "Avoid enemy collision", condition: () => enemies.every(e => !Matter.Bounds.overlaps(hero.bounds, e.bounds)) },
      { 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) },
      { id: 40, text: "Reset near starting point (x < 120)", condition: () => hero.position.x < 120 },
      { id: 41, text: "Jump height exceeds 30 units", condition: () => (250 - hero.position.y) >= 30 },
      { id: 42, text: "Maintain steady movement (velocity.x ~ 3)", condition: () => Math.abs(hero.velocity.x - 3) < 1 },
      { id: 43, text: "Attack then move (enemy defeated and x > 150)", condition: () => enemies.some(e => e.isRemoved) && hero.position.x > 150 },
      { id: 44, text: "Keep speed constant (velocity.x ~ 3)", condition: () => Math.abs(hero.velocity.x - 3) < 0.5 },
      { id: 45, text: "Show agility (jump and dash with |velocity.x| > 4)", condition: () => hero.position.y < 240 && Math.abs(hero.velocity.x) > 4 },
      { id: 46, text: "Combo move achieves x >= 350", condition: () => hero.position.x >= 350 },
      { id: 47, text: "Recover from an attack (x >= 100)", condition: () => hero.position.x >= 100 },
      { id: 48, text: "Dodge enemy attack (no overlap with any enemy)", condition: () => enemies.every(e => !Matter.Bounds.overlaps(hero.bounds, e.bounds)) },
      { id: 49, text: "Exhibit erratic movement (|velocity.x| > 5)", condition: () => Math.abs(hero.velocity.x) > 5 },
      { id: 50, text: "Final Task: Overcome all challenges (x >= 550 and all enemies defeated)", condition: () => hero.position.x >= 550 && enemies.every(e => e.isRemoved) }
    ];
    
    let currentTaskIndex = 0;
    function updateTaskText() {
      document.getElementById("taskText").innerText = tasks[currentTaskIndex].text;
    }
    updateTaskText();

    // --- Enemy Movement (Simple AI) ---
    function moveEnemies() {
      enemies.forEach((enemy) => {
        if (enemy.isRemoved) return;
        const dx = hero.position.x - enemy.position.x;
        const speed = dx > 0 ? 1.5 : -1.5;
        Body.setVelocity(enemy, { x: speed, y: 0 });
      });
    }
    setInterval(moveEnemies, 100);

    // --- Command Parser (No debug console output) ---
    function handleGameAction(command) {
      const parts = command.split(" ");
      const action = parts[0];
      const param = parts[1];
      const value = parseFloat(parts[1]);

      if (action === "move" && !isNaN(value)) {
        Body.setVelocity(hero, { x: value, y: hero.velocity.y });
      } else if (action === "jump" && !isNaN(value)) {
        Body.applyForce(hero, hero.position, { x: 0, y: -value });
      } else if (action === "attack") {
        if (param === "sword") performSwordAttack();
        if (param === "bow") fireArrow();
        if (param === "bomb") throwBomb();
      }
    }
    
    function performSwordAttack() {
      Body.setPosition(sword, { x: hero.position.x + 30, y: hero.position.y });
      sword.render.visible = true;
      enemies.forEach((enemy) => {
        if (!enemy.isRemoved && Matter.Bounds.overlaps(sword.bounds, enemy.bounds)) {
          World.remove(engine.world, enemy);
          enemy.isRemoved = true;
        }
      });
      setTimeout(() => { sword.render.visible = false; }, 500);
    }
    
    function fireArrow() {
      const arrow = Bodies.rectangle(hero.position.x + 20, hero.position.y, 20, 5, { restitution: 0.2 });
      Body.setVelocity(arrow, { x: 5, y: 0 });
      arrows.push(arrow);
      World.add(engine.world, arrow);
      setTimeout(() => { World.remove(engine.world, arrow); }, 3000);
    }
    
    function throwBomb() {
      const bomb = Bodies.circle(hero.position.x, hero.position.y - 10, 10, { restitution: 0.5 });
      Body.setVelocity(bomb, { x: 3, y: -3 });
      bombs.push(bomb);
      World.add(engine.world, bomb);
      setTimeout(() => {
        bombs = bombs.filter(b => b !== bomb);
        World.remove(engine.world, bomb);
        enemies.forEach((enemy) => {
          if (!enemy.isRemoved && Matter.Bounds.overlaps(bomb.bounds, enemy.bounds)) {
            World.remove(engine.world, enemy);
            enemy.isRemoved = true;
          }
        });
      }, 2000);
    }

    // --- Check Task Completion ---
    function checkTaskCompletion() {
      if (tasks[currentTaskIndex].condition()) {
        alert(`βœ… Task Completed: ${tasks[currentTaskIndex].text}`);
        currentTaskIndex++;
        if (currentTaskIndex < tasks.length) {
          updateTaskText();
          resetHeroPosition();
        } else {
          alert("πŸŽ‰ All Tasks Completed!");
        }
      }
    }
    
    // --- Run Code Button Event ---
    document.getElementById("runBtn").addEventListener("click", () => {
      // Retrieve user code from Monaco Editor
      const userCode = monacoEditor.getValue();
      try {
        // Override console.log temporarily to capture commands (without on-screen debug)
        const oldConsoleLog = console.log;
        console.log = function(msg) { 
          handleGameAction(msg); 
        };
        new Function(userCode)();
        console.log = oldConsoleLog;
        setTimeout(checkTaskCompletion, 1000);
      } catch (error) {
        alert("Error: " + error.message);
      }
    });

    function resetHeroPosition() {
      Body.setPosition(hero, { x: 100, y: 250 });
      Body.setVelocity(hero, { x: 0, y: 0 });
    }
  </script>
</body>
</html>