diff --git a/data/levels.json b/data/levels.json
index 446178d..b267d13 100644
--- a/data/levels.json
+++ b/data/levels.json
@@ -1,7 +1,7 @@
[
{
"name": "Level 1",
- "robots":{
+ "robots": {
"player": {
"position": {
"x": 200,
@@ -9,6 +9,34 @@
}
}
},
+ "waypoints": [
+ {
+ "position": {
+ "x": 420,
+ "y": 200
+ },
+ "vertices": [
+ {
+ "x": -50,
+ "y": -50
+ },
+ {
+ "x": 50,
+ "y": -50
+ },
+ {
+ "x": 50,
+ "y": 50
+ },
+ {
+ "x": -50,
+ "y": 50
+ }
+ ],
+ "strokeColor": "#0000FF",
+ "fillColor": "#0000CC"
+ }
+ ],
"obstacles": [
{
"vertices": [
@@ -30,7 +58,7 @@
}
],
"position": {
- "x": 400,
+ "x": 800,
"y": 300
},
"strokeColor": "#999999",
@@ -39,25 +67,170 @@
{
"vertices": [
{
- "x": 300,
- "y": 380
+ "x": -10,
+ "y": -10
},
{
- "x": 420,
- "y": 380
+ "x": 1010,
+ "y": -10
},
{
- "x": 350,
- "y": 550
+ "x": 1010,
+ "y": 10
},
{
- "x": 280,
- "y": 420
+ "x": -10,
+ "y": 10
}
],
"position": {
- "x": 400,
- "y": -50
+ "x": 500,
+ "y": 0
+ },
+ "strokeColor": "#999999",
+ "fillColor": "#CCCCCC"
+ },
+ {
+ "vertices": [
+ {
+ "x": -10,
+ "y": -10
+ },
+ {
+ "x": 1010,
+ "y": -10
+ },
+ {
+ "x": 1010,
+ "y": 10
+ },
+ {
+ "x": -10,
+ "y": 10
+ }
+ ],
+ "position": {
+ "x": 500,
+ "y": 620
+ },
+ "strokeColor": "#999999",
+ "fillColor": "#CCCCCC"
+ },
+ {
+ "vertices": [
+ {
+ "x": -10,
+ "y": 10
+ },
+ {
+ "x": 10,
+ "y": 10
+ },
+ {
+ "x": 10,
+ "y": 610
+ },
+ {
+ "x": -10,
+ "y": 610
+ }
+ ],
+ "position": {
+ "x": 0,
+ "y": 310
+ },
+ "strokeColor": "#999999",
+ "fillColor": "#CCCCCC"
+ },
+ {
+ "vertices": [
+ {
+ "x": -10,
+ "y": 10
+ },
+ {
+ "x": 10,
+ "y": 10
+ },
+ {
+ "x": 10,
+ "y": 610
+ },
+ {
+ "x": -10,
+ "y": 610
+ }
+ ],
+ "position": {
+ "x": 1000,
+ "y": 310
+ },
+ "strokeColor": "#999999",
+ "fillColor": "#CCCCCC"
+ }
+ ]
+ },
+ {
+ "name": "Level 2",
+ "robots": {
+ "player": {
+ "position": {
+ "x": 200,
+ "y": 200
+ }
+ }
+ },
+ "waypoints": [
+ {
+ "position": {
+ "x": 220,
+ "y": 500
+ },
+ "vertices": [
+ {
+ "x": -50,
+ "y": -50
+ },
+ {
+ "x": 50,
+ "y": -50
+ },
+ {
+ "x": 50,
+ "y": 50
+ },
+ {
+ "x": -50,
+ "y": 50
+ }
+ ],
+ "strokeColor": "#000099",
+ "fillColor": "#0000CC"
+ }
+ ],
+ "obstacles": [
+ {
+ "vertices": [
+ {
+ "x": -100,
+ "y": -100
+ },
+ {
+ "x": 100,
+ "y": -100
+ },
+ {
+ "x": 100,
+ "y": 100
+ },
+ {
+ "x": -100,
+ "y": 100
+ }
+ ],
+ "position": {
+ "x": 200,
+ "y": 300
},
"strokeColor": "#999999",
"fillColor": "#CCCCCC"
diff --git a/game.js b/game.js
index b12efcf..9394bbe 100644
--- a/game.js
+++ b/game.js
@@ -61,10 +61,40 @@ function createInitialRobots() {
}
// ✅ Function to log messages to console
+const maxLines = 64;
+const logLines = [];
+
function logToConsole(text) {
- //console.log(text.replace("", "").replace("", ""));
- consoleElement.innerHTML += text.replace(/\n/g, "
") + "
";
- consoleElement.scrollTop = consoleElement.scrollHeight;
+ if (text.includes("Pyodide not initialized yet.")) return;
+
+ const newLines = text.split('\n').map(line => line.trim()).filter(line => line !== "");
+
+ // Add new lines to the array
+ logLines.push(...newLines);
+
+ // Keep only the last maxLines entries
+ if (logLines.length > maxLines) {
+ logLines.splice(0, logLines.length - maxLines);
+ }
+
+ // Clear console
+ consoleElement.innerHTML = "";
+
+ // Create and append separate divs for each line
+ logLines.forEach(lineText => {
+ const lineDiv = document.createElement('div');
+ lineDiv.textContent = lineText;
+ consoleElement.appendChild(lineDiv);
+ });
+
+ // Scroll to bottom
+ consoleElement.scrollTop = consoleElement.scrollHeight;
+}
+
+
+function clearConsole() {
+ logLines.length = 0; // Empty the logLines array
+ consoleElement.innerHTML = ""; // Clear the console DOM element
}
// ✅ Game Control Functions
@@ -90,6 +120,7 @@ function togglePause() {
function resetGame() {
// Terminate the worker
pyodideWorker.terminate();
+ clearConsole();
// Restart the worker and rebind event listener
pyodideWorker = startPyodideWorker();
@@ -105,6 +136,8 @@ function resetGame() {
// Unpause the game if it was paused
paused = false;
document.getElementById("pause-button").innerText = "Pause";
+
+ logToConsole("Welcome to the game! Type your Python code in the editor and click 'Compile' to execute it.");
}
const targetFPS = 30;
@@ -128,6 +161,11 @@ function gameLoop(timestamp) {
gameWorld.update();
gameWorld.draw(ctx);
+ if (gameWorld.checkPlayerCompletedTask()){
+ logToConsole("🏁 Task Completed! 🏁");
+ togglePause();
+ gameWorld.currentLevel++;
+ }
pyodideWorker.postMessage({
type: "game_state",
@@ -158,7 +196,7 @@ function updateSensorData() {
document.getElementById("compile-button").addEventListener("click", () => {
if (paused) return;
-
+
// Use the Monaco Editor instance to get the code
let code = monacoEditor.getValue(); // Get text from the editor
console.log(code);
diff --git a/gameworld.js b/gameworld.js
index 193ecaf..3084451 100644
--- a/gameworld.js
+++ b/gameworld.js
@@ -11,7 +11,9 @@ export class GameWorld {
let ground = Matter.Bodies.rectangle(400, 600, 800, 50, { isStatic: true });
Matter.World.add(this.world, ground);
this.levelData = null;
+ this.currentLevel = 0;
+ this.waypoints = [];
this.obstacles = [];
this.robots = [];
@@ -25,9 +27,10 @@ export class GameWorld {
reset(player = null) {
this.robots = []
this.obstacles = []
+ this.waypoints = [];
Matter.World.clear(this.world); // Clear the world without resetting the engine
- let level = this.levelData[0];
+ let level = this.levelData[this.currentLevel];
if (player) {
console.log("Resetting player position to:", level.robots["player"]["position"]);
@@ -41,7 +44,13 @@ export class GameWorld {
for (let i = 0; i < level.obstacles.length; i++) {
let obstacle = level.obstacles[i];
- this.addObstacle(obstacle.vertices, obstacle.position);
+ this.addObstacle(obstacle.vertices, obstacle.position, obstacle.strokeColor, obstacle.fillColor);
+ }
+
+ for (let i = 0; i < level.waypoints.length; i++) {
+ let obstacle = level.waypoints[i];
+ console.log("Adding waypoint:", obstacle);
+ this.addWaypoint(obstacle.vertices, obstacle.position, obstacle.strokeColor, obstacle.fillColor);
}
// this.addObstacle([
@@ -68,17 +77,48 @@ export class GameWorld {
this.robots[id].update(this);
}
+
+
+ //this.checkPlayerCompletedTask();
+
Matter.Engine.update(this.engine);
}
- addObstacle(vertices, position = { x: 0, y: 0 }) {
+ // Needs to be updated to handle different win conditions
+ checkPlayerCompletedTask() {
+ let playerPos = this.robots[0].body.position;
+ let waypointBounds = this.waypoints[0].bounds;
+ if (Matter.Bounds.contains(waypointBounds, playerPos)) {
+ console.log("Player is inside the waypoint's bounding box.");
+ return true;
+ }
+ return false;
+ }
+
+ addWaypoint(vertices, position = { x: 0, y: 0 }, strokeColor = "yellow", fillColor = "yellow") {
+ // Convert the polygon points into a Matter.js body
+ let body = Matter.Bodies.fromVertices(position.x, position.y, [vertices], {
+ isSensor: true,
+ isStatic: true,
+ label: "zone"
+ });
+ body.strokeColor = strokeColor;
+ body.fillColor = fillColor;
+ // Add body to world and store it
+ Matter.World.add(this.world, body);
+ this.waypoints.push(body);
+
+ }
+
+ addObstacle(vertices, position = { x: 0, y: 0 }, strokeColor = "black", fillColor = "gray") {
// Convert the polygon points into a Matter.js body
let body = Matter.Bodies.fromVertices(position.x, position.y, [vertices], {
isStatic: true, // Obstacles shouldn't move
});
-
+ body.strokeColor = strokeColor;
+ body.fillColor = fillColor;
// Add body to world and store it
Matter.World.add(this.world, body);
this.obstacles.push(body);
@@ -118,7 +158,29 @@ export class GameWorld {
for (let i = 1; i < vertices.length; i++) {
ctx.lineTo(vertices[i].x, vertices[i].y);
}
+ ctx.fillStyle = body.fillColor;;
+
+ ctx.strokeStyle = body.strokeColor;
ctx.closePath();
+ ctx.fill();
+ ctx.stroke();
+ });
+
+ // Draw Waypoints
+ this.waypoints.forEach(body => {
+ ctx.beginPath();
+ let vertices = body.vertices;
+ ctx.moveTo(vertices[0].x, vertices[0].y);
+ for (let i = 1; i < vertices.length; i++) {
+ ctx.lineTo(vertices[i].x, vertices[i].y);
+ }
+ ctx.fillStyle = body.fillColor;;
+
+ ctx.strokeStyle = body.strokeColor;
+ ctx.closePath();
+ ctx.globalAlpha = 0.2; // Applies to all drawing
+ ctx.fill();
+ ctx.globalAlpha = 1.0; // Reset after if needed
ctx.stroke();
});
@@ -145,6 +207,8 @@ export class GameWorld {
let startPoint = { x: startX, y: startY };
let endPoint = { x: endX, y: endY };
+ ignoreBodies.push(...this.waypoints); // Add all obstacles to the ignore list
+
// Get all bodies in the world
let allBodies = Matter.Composite.allBodies(this.engine.world);
diff --git a/index.html b/index.html
index a47d571..7043d91 100644
--- a/index.html
+++ b/index.html
@@ -2,81 +2,221 @@
Write code, simulate physics, and see instant output
+