do properre canvas: const canvas = document.getElementById("game-canvas");
const context = canvas.getContext("2d");

const GRAVITY = 80;
const ENEMY_RADIUS = 25;
const PLAYER_RADIUS = 25;
const TRACTION = 0.95;

let playerX = 50;
let playerY = 550;
let enemyX = 750;
let enemyY = 150;
let enemySpeedX = 0;
let enemySpeedY = 0;
let playerSpeedX = 0;
let playerSpeedY = 0;
let playerIsOnGround = false;

const paths = [];
const nodes = [];

class Node {
constructor(x, y) {
this.x = x;
this.y = y;
this.neighbors = [];
}

distanceTo(otherNode) {
return Math.sqrt((otherNode.x - this.x) ** 2 + (otherNode.y - this.y) ** 2);
}
}

class Path {
constructor(startNode, endNode, nodes) {
this.startNode = startNode;
this.endNode = endNode;
this.nodes = nodes;
this.cost = nodes.reduce((acc, node, i) => {
if (i > 0) {
acc += node.distanceTo(nodes[i - 1]);
}
return acc;
}, 0);
}
}

function generatePaths() {
const nodes = [];
for (let i = 0; i < canvas.width; i += 50) {
for (let j = 0; j < canvas.height; j += 50) {
const node = new Node(i + 25, j + 25);
nodes.push(node);
}
}
nodes.forEach((node, index) => {
const neighbors = nodes.filter((otherNode, otherIndex) => {
if (otherIndex === index) return false;
return node.distanceTo(otherNode) < 75;
});
node.neighbors = neighbors;
});
}

function findClosestNode(x, y) {
let closestNode = null;
let closestDistance = Infinity;
nodes.forEach((node) => {
const distance = node.distanceTo({ x: x, y: y });
if (distance < closestDistance) {
closestNode = node;
closestDistance = distance;
}
});
if (closestDistance < 50) {
return closestNode;
}
return null;
}

function findPath(startNode, endNode) {
const queue = [];
const visited = new Set();
queue.push({ node: startNode, path: [startNode] });

while (queue.length > 0) {
const { node, path } = queue.shift();

if (visited.has(node)) {
continue;
}
visited.add(node);

if (node === endNode) {
return path;
}

node.neighbors.forEach((neighbor) => {
queue.push({ node: neighbor, path: [path, neighbor] });
});
}

return null;
}

function applyGravity() {
if (!playerIsOnGround) {
playerSpeedY += GRAVITY / 60;
}
}

function applyTraction() {
playerSpeedX *= TRACTION;
}

function movePlayer() {
playerX += playerSpeedX;
playerY += playerSpeedY;

if (playerX < 0) {
playerX = 0;
}
if (playerX + 50 > canvas.width) {
playerX = canvas.width - 50;
}

context.fillStyle = "red";
context.fillRect(playerX, playerY, 50, 50);
}

function moveEnemy() {
let path;

if (paths.length > 0) {
const path = paths[0];
const startNode = path.startNode;
const endNode = path.endNode;
const shortestPath = findPath(startNode, endNode);
console.log("startNode:", startNode);
console.log("endNode:", endNode);
console.log("shortestPath:", shortestPath);
}

for (let i = 0; i < paths.length; i++) {
const { nodes, startNode, endNode, cost } = paths[i];
if (startNode.x === enemyX && startNode.y === enemyY) {
path = { nodes: nodes, cost: cost };
break;
}
}

if (path) {
const followNode = path.nodes.shift();
const theta = Math.atan2(followNode.y - enemyY, followNode.x - enemyX);
enemySpeedX = Math.cos(theta) * 5;
enemySpeedY = Math.sin(theta) * 5;
enemyX += enemySpeedX;
enemyY += enemySpeedY;
console.log(enemyX, enemyY);

if (
enemyX - ENEMY_RADIUS < 0 ||
enemyX + ENEMY_RADIUS > canvas.width ||
enemyY - ENEMY_RADIUS < 0 ||
enemyY + ENEMY_RADIUS > canvas.height
) {
paths = paths.filter(
(p) => p.startNode.x !== enemyX || p.startNode.y !== enemyY
);
return;
}

context.fillStyle = "blue";
context.fillRect(
enemyX - ENEMY_RADIUS,
enemyY - ENEMY_RADIUS,
ENEMY_RADIUS * 2,
ENEMY_RADIUS * 2
);

if (path.nodes.length === 0) {
paths = paths.filter(
(p) => p.startNode.x !== enemyX || p.startNode.y !== enemyY
);
}
} else {
paths = paths.filter(
(p) => p.startNode.x !== enemyX || p.startNode.y !== enemyY
);
}
}

function checkCollision() {
let isOnGround = false;

if (playerY + PLAYER_RADIUS >= canvas.height) {
playerSpeedY = 0;
playerY = canvas.height - PLAYER_RADIUS;
playerIsOnGround = true;
isOnGround = true;
}
if (
playerX + 50 >= enemyX - ENEMY_RADIUS &&
playerX <= enemyX + ENEMY_RADIUS &&
playerY <= enemyY + ENEMY_RADIUS &&
playerY + 50 >= enemyY - ENEMY_RADIUS
) {
playerSpeedY = 0;
playerY = enemyY - ENEMY_RADIUS - 50;
playerIsOnGround = true;
isOnGround = true;
}
if (playerY <= 0) {
playerSpeedY = 0;
playerY = 0;
}
if (!isOnGround) {
playerIsOnGround = false;
}
}

function draw() {
context.clearRect(0, 0, canvas.width, canvas.height);
context.fillStyle = "gray";
context.fillRect(0, 0, canvas.width, canvas.height);

applyGravity();
applyTraction();
movePlayer();
moveEnemy();
checkCollision();

requestAnimationFrame(draw);
}

function reset() {
playerX = 50;
playerY = 550;
enemyX = 750;
enemyY = 150;
enemySpeedX = 0;
enemySpeedY = 0;
playerSpeedX = 0;
playerSpeedY = 0;
playerIsOnGround = true;

context.strokeStyle = "black";
context.lineWidth = 2;
context.strokeRect(0, 0, canvas.width, canvas.height);
context.clearRect(0, 0, canvas.width, canvas.height);
context.fillStyle = "gray";
context.fillRect(0, 0, canvas.width, canvas.height);

paths.length = 0;

generatePaths();
draw();
}

generatePaths();

canvas.addEventListener("click", (event) => {
const rect = canvas.getBoundingClientRect();
const x = event.clientX - rect.left;
const y = event.clientY - rect.top;

const closestNode = findClosestNode(x, y);
console.log("x:", x, "y:", y);
console.log("closestNode:", closestNode);

if (closestNode) {
const playerNode = nodes.find(
(node) =>
playerX + PLAYER_RADIUS >= node.x - 25 &&
playerX + PLAYER_RADIUS <= node.x + 25 &&
playerY + PLAYER_RADIUS >= node.y - 25 &&
playerY + PLAYER_RADIUS <= node.y + 25
);
const path = findPath(playerNode, closestNode);
paths.push(new Path(playerNode, closestNode, path));
}
});

document.addEventListener("keydown", (event) => {
if (event.code === "KeyA") {
playerSpeedX = -5;
}
if (event.code === "KeyD") {
playerSpeedX = 5;
}
if (event.code === "KeyW" && playerIsOnGround) {
playerSpeedY = -20;
playerIsOnGround = false;
}
});

document.addEventListener("keyup", (event) => {
if (event.code === "KeyA" || event.code === "KeyD") {
playerSpeedX *= TRACTION;
}
});

window.addEventListener("load", () => {
reset();
});