279 lines
7.7 KiB
JavaScript
279 lines
7.7 KiB
JavaScript
import { Robot } from "./robot.js";
|
|
import { GameWorld } from "./gameworld.js";
|
|
|
|
const consoleElement = document.getElementById("console");
|
|
const gameCanvas = document.getElementById("gameCanvas");
|
|
const ctx = gameCanvas.getContext("2d");
|
|
|
|
const gameWorld = new GameWorld();
|
|
let monacoEditor;
|
|
|
|
let pyodideWorker = startPyodideWorker();
|
|
let robots = null;//createInitialRobots();
|
|
//gameWorld.addRobot(robots["player"]);
|
|
let paused = false;
|
|
|
|
let scale = 1; // Zoom level
|
|
let offsetX = 0; // Pan X
|
|
let offsetY = 0; // Pan Y
|
|
let isPanning = false;
|
|
let startX, startY; // Mouse start positions
|
|
|
|
|
|
// ✅ Function to create the Pyodide Worker
|
|
function startPyodideWorker() {
|
|
const worker = new Worker("pyodide-worker.js?v=" + Date.now());
|
|
|
|
// ✅ Reattach the event listener when a new worker is created
|
|
worker.onmessage = (event) => {
|
|
if (paused) return;
|
|
|
|
switch (event.data.type) {
|
|
case "console":
|
|
logToConsole(event.data.data);
|
|
break;
|
|
case "error":
|
|
logToConsole(`<span style="color:red;">${event.data.message}</span>`);
|
|
break;
|
|
case "fire":
|
|
fire();
|
|
break;
|
|
case "turn":
|
|
turn(event.data.data);
|
|
break;
|
|
case "move":
|
|
move(event.data.data);
|
|
break;
|
|
}
|
|
};
|
|
|
|
return worker;
|
|
}
|
|
|
|
// ✅ Function to create initial robots
|
|
function createInitialRobots() {
|
|
return {
|
|
"player": new Robot("player", 50, 50, "blue")
|
|
//"enemy1": new Robot("enemy1", 200, 150, "red"),
|
|
//"enemy2": new Robot("enemy2", 400, 250, "red")
|
|
};
|
|
}
|
|
|
|
// ✅ Function to log messages to console
|
|
function logToConsole(text) {
|
|
//console.log(text.replace("<b>", "").replace("</b>", ""));
|
|
consoleElement.innerHTML += text.replace(/\n/g, "<br>") + "<br>";
|
|
consoleElement.scrollTop = consoleElement.scrollHeight;
|
|
}
|
|
|
|
// ✅ Game Control Functions
|
|
function fire() {
|
|
logToConsole("<b>🔥 Gun Fired! 🔥</b>");
|
|
}
|
|
|
|
function turn(deg) {
|
|
robots["player"].turn(deg);
|
|
}
|
|
|
|
function move(distance) {
|
|
robots["player"].move(distance);
|
|
}
|
|
|
|
// ✅ Pause/Resume Function
|
|
function togglePause() {
|
|
paused = !paused;
|
|
document.getElementById("pause-button").innerText = paused ? "Resume" : "Pause";
|
|
}
|
|
|
|
// ✅ Reset Function (Fixed)
|
|
function resetGame() {
|
|
// Terminate the worker
|
|
pyodideWorker.terminate();
|
|
|
|
// Restart the worker and rebind event listener
|
|
pyodideWorker = startPyodideWorker();
|
|
|
|
// Reset the robots to their initial state
|
|
robots = createInitialRobots();
|
|
gameWorld.reset();
|
|
gameWorld.addRobot(robots["player"]);
|
|
|
|
// Clear the console
|
|
consoleElement.innerHTML = "";
|
|
|
|
// Unpause the game if it was paused
|
|
paused = false;
|
|
document.getElementById("pause-button").innerText = "Pause";
|
|
}
|
|
|
|
const targetFPS = 30;
|
|
const targetInterval = 1000 / targetFPS; // Time in milliseconds per frame
|
|
let lastFrameTime = 0;
|
|
|
|
// ✅ Game Loop
|
|
function gameLoop(timestamp) {
|
|
const deltaTime = timestamp - lastFrameTime;
|
|
// If enough time has passed since the last frame, update and draw
|
|
if (deltaTime >= targetInterval) {
|
|
lastFrameTime = timestamp;
|
|
if (!paused) {
|
|
ctx.resetTransform();
|
|
// Fill the entire visible canvas to remove artifacts
|
|
ctx.fillStyle = "#DDD";
|
|
ctx.fillRect(0, 0, gameCanvas.width, gameCanvas.height);
|
|
|
|
ctx.translate(offsetX, offsetY); // Apply panning
|
|
ctx.scale(scale, scale); // Apply zooming
|
|
|
|
gameWorld.update();
|
|
gameWorld.draw(ctx);
|
|
|
|
pyodideWorker.postMessage({
|
|
type: "game_state",
|
|
state: gameWorld
|
|
});
|
|
|
|
|
|
}
|
|
}
|
|
requestAnimationFrame(gameLoop);
|
|
}
|
|
|
|
resetGame(); // Initialize the game and robots
|
|
// Start game loop
|
|
gameLoop();
|
|
|
|
// ✅ Update "distance" and "speed" every 2 seconds with random values
|
|
function updateSensorData() {
|
|
const distance = Math.random() * 100; // Random distance (0-100)
|
|
const speed = Math.random() * 10; // Random speed (0-10)
|
|
console.log(`Distance: ${distance.toFixed(2)}, Speed: ${speed.toFixed(2)}`);
|
|
pyodideWorker.postMessage({
|
|
type: "sensor_update",
|
|
data: { distance, speed }
|
|
});
|
|
|
|
//logToConsole(`📡 Sensor Update - Distance: ${distance.toFixed(2)}, Speed: ${speed.toFixed(2)}`);
|
|
}
|
|
|
|
//setInterval(updateSensorData, 2000); // Call every 2 seconds
|
|
|
|
|
|
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);
|
|
|
|
code = code.replace(/time\.sleep\(/g, "await time.sleep(");
|
|
console.log(code);
|
|
consoleElement.innerHTML = "";
|
|
pyodideWorker.postMessage({
|
|
type: "execute",
|
|
code: code
|
|
});
|
|
});
|
|
|
|
|
|
document.getElementById("pause-button").addEventListener("click", togglePause);
|
|
document.getElementById("reset-button").addEventListener("click", resetGame);
|
|
|
|
gameCanvas.addEventListener("wheel", (event) => {
|
|
event.preventDefault();
|
|
|
|
const scaleFactor = 1.1;
|
|
const mouseX = event.offsetX;
|
|
const mouseY = event.offsetY;
|
|
|
|
// Convert mouse coordinates to world coordinates (before zoom)
|
|
const worldX = (mouseX - offsetX) / scale;
|
|
const worldY = (mouseY - offsetY) / scale;
|
|
|
|
// Apply zoom
|
|
if (event.deltaY < 0) {
|
|
scale *= scaleFactor; // Zoom in
|
|
} else {
|
|
scale /= scaleFactor; // Zoom out
|
|
}
|
|
|
|
// Keep zoom within limits
|
|
scale = Math.max(0.5, Math.min(3, scale));
|
|
|
|
// Adjust offset so zooming is centered at mouse position
|
|
offsetX = mouseX - worldX * scale;
|
|
offsetY = mouseY - worldY * scale;
|
|
});
|
|
|
|
|
|
gameCanvas.addEventListener("mousedown", (event) => {
|
|
isPanning = true;
|
|
startX = event.clientX - offsetX;
|
|
startY = event.clientY - offsetY;
|
|
});
|
|
|
|
gameCanvas.addEventListener("mousemove", (event) => {
|
|
if (!isPanning) return;
|
|
offsetX = event.clientX - startX;
|
|
offsetY = event.clientY - startY;
|
|
});
|
|
|
|
gameCanvas.addEventListener("mouseup", () => {
|
|
isPanning = false;
|
|
});
|
|
|
|
const textarea = document.getElementById('python-code');
|
|
|
|
// textarea.addEventListener('keydown', function (event) {
|
|
// if (event.key === 'Tab') {
|
|
// event.preventDefault();
|
|
// const start = this.selectionStart;
|
|
// const end = this.selectionEnd;
|
|
// this.value = this.value.substring(0, start) + ' ' + this.value.substring(end);
|
|
// this.selectionStart = this.selectionEnd = start + 1;
|
|
// }
|
|
// });
|
|
|
|
// game.js
|
|
|
|
export function initializeMonaco() {
|
|
require.config({ paths: { vs: "https://cdn.jsdelivr.net/npm/monaco-editor@0.39.0/min/vs" } });
|
|
|
|
require(["vs/editor/editor.main"], function () {
|
|
// Store the editor instance in the global variable
|
|
monacoEditor = monaco.editor.create(document.getElementById("monaco-editor"), {
|
|
value: "# Type your Python code here\n",
|
|
language: "python",
|
|
theme: "vs-dark",
|
|
automaticLayout: true
|
|
});
|
|
});
|
|
}
|
|
|
|
function setupCanvas() {
|
|
const canvas = document.getElementById("gameCanvas");
|
|
const context = canvas.getContext("2d");
|
|
|
|
// Get the device pixel ratio
|
|
const dpr = window.devicePixelRatio || 1;
|
|
|
|
// Adjust the canvas size for high-DPI displays
|
|
const rect = canvas.getBoundingClientRect();
|
|
canvas.width = rect.width * dpr;
|
|
canvas.height = rect.height * dpr;
|
|
|
|
// Scale the drawing context
|
|
context.scale(dpr, dpr);
|
|
|
|
// Optional: Clear the canvas for better visuals
|
|
context.clearRect(0, 0, canvas.width, canvas.height);
|
|
}
|
|
|
|
// Call this function when the page loads
|
|
setupCanvas();
|
|
|
|
|
|
|
|
|
|
|