export class Sensor { constructor(robot, offsetAngle, offsetDistance) { this.robot = robot this.value = null; // Last recorded value this.offsetAngle = offsetAngle; this.offsetDistance = offsetDistance; this.hitX = null; this.hitY = null; this.hitObject = null; } updatePosition() { const robotAngle = this.robot.angle * (Math.PI / 180); const offsetAngleRad = this.offsetAngle * (Math.PI / 180); this.startX = this.robot.x + this.offsetDistance * Math.cos(robotAngle + offsetAngleRad); this.startY = this.robot.y + this.offsetDistance * Math.sin(robotAngle + offsetAngleRad); } read(robot, gameWorld) { throw new Error("read() must be implemented in subclasses"); } draw(ctx) { // Default empty draw, can be overridden } } export class RaycastSensor extends Sensor { constructor(robot, offsetAngle, offsetDistance, angle, distance) { super(robot, offsetAngle, offsetDistance); // Call the parent class constructor this.angle = angle; this.distance = distance; this.range = 100; } updatePosition() { const robotAngle = this.robot.angle * (Math.PI / 180); const offsetAngleRad = this.offsetAngle * (Math.PI / 180); this.startX = this.robot.x + this.offsetDistance * Math.cos(robotAngle + offsetAngleRad); this.startY = this.robot.y + this.offsetDistance * Math.sin(robotAngle + offsetAngleRad); const sensorAngle = robotAngle + (this.angle * (Math.PI / 180)); this.endX = this.startX + Math.cos(sensorAngle) * this.range; this.endY = this.startY + Math.sin(sensorAngle) * this.range; } read(robot, gameWorld) { //console.log(robot); //console.log(gameWorld); this.updatePosition(); const angleInRadians = (this.robot.direction + this.angle) * Math.PI / 180; const x = this.robot.x + Math.cos(angleInRadians) * this.distance; const y = this.robot.y + Math.sin(angleInRadians) * this.distance; // Ensure gameWorld is available and properly passed to the sensor let hitPos = gameWorld.rayCast(this.startX, this.startY, this.endX, this.endY); if (hitPos != null) { this.hitX = hitPos.x; this.hitY = hitPos.y; this.endX = this.hitX; this.endY = this.hitY; } else { this.hitX = null; this.hitY = null; } // console.log("Obstacle detected!"); // } else { // console.log("Clear path!"); // } } draw(ctx) { ctx.strokeStyle = "lime"; ctx.fillStyle = "green" ctx.beginPath(); ctx.arc(this.startX, this.startY, 2, 0, 2 * Math.PI); ctx.fillStyle = "red"; ctx.fill(); ctx.stroke(); if (this.hitX != null) { ctx.strokeStyle = "red"; ctx.fillStyle = "red" ctx.beginPath(); ctx.arc(this.hitX, this.hitY, 2, 0, 2 * Math.PI); ctx.fillStyle = "red"; ctx.fill(); ctx.stroke(); } ctx.strokeStyle = "yellow"; ctx.beginPath(); ctx.lineWidth = 2; ctx.moveTo(this.startX, this.startY); ctx.lineTo(this.endX, this.endY); ctx.stroke(); } } export class FloorColorSensor extends Sensor { constructor(robot, offsetAngle, offsetDistance) { super(robot, offsetAngle, offsetDistance); // No angle offset, directly below robot } read(robot, gameWorld) { this.value = gameWorld.getFloorColor(robot.x, robot.y); return this.value; } draw(ctx) { this.updatePosition(); ctx.strokeStyle = "purple"; ctx.fillStyle = "green" ctx.beginPath(); ctx.arc(this.startX, this.startY, 2, 0, 2 * Math.PI); ctx.fillStyle = "red"; ctx.fill(); ctx.stroke(); } }