Spaces:
Running
Running
<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 > 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> | |