diff --git a/src/game.js b/src/game.js index 8bb7124..87b13a8 100644 --- a/src/game.js +++ b/src/game.js @@ -213,6 +213,7 @@ team, fireTimer: 0, aiTimer: 0.4 + Math.random() * 1.2, + avoidTimer: 0, alive: true, }; } @@ -347,9 +348,10 @@ const livingEnemies = enemies.filter((enemy) => enemy.alive); for (const enemy of livingEnemies) { enemy.fireTimer = Math.max(0, enemy.fireTimer - dt); + enemy.avoidTimer = Math.max(0, enemy.avoidTimer - dt); enemy.aiTimer -= dt; - if (enemy.aiTimer <= 0) { + if (enemy.aiTimer <= 0 && enemy.avoidTimer <= 0) { enemy.dir = chooseEnemyDirection(enemy); enemy.aiTimer = 0.45 + Math.random() * 1.25; } @@ -357,12 +359,16 @@ const dir = DIRS[enemy.dir]; const moved = moveTank(enemy, dir.x * ENEMY_SPEED * dt, dir.y * ENEMY_SPEED * dt, livingEnemies); if (!moved) { - enemy.dir = randomDirection(); - enemy.aiTimer = 0.25; + const escapeDir = chooseOpenEnemyDirection(enemy, livingEnemies); + enemy.dir = escapeDir || randomDirection(); + enemy.avoidTimer = 0.55; + enemy.aiTimer = 0.2; + const next = DIRS[enemy.dir]; + moveTank(enemy, next.x * ENEMY_SPEED * dt, next.y * ENEMY_SPEED * dt, livingEnemies); } if (canSeePlayer(enemy) || Math.random() < dt * 0.18) { - facePlayer(enemy); + if (enemy.avoidTimer <= 0) facePlayer(enemy); fire(enemy); } } @@ -383,6 +389,35 @@ return names[Math.floor(Math.random() * names.length)]; } + function chooseOpenEnemyDirection(enemy, livingEnemies) { + const preferred = shuffledDirections().sort((a, b) => distanceFromPlayer(enemy, b) - distanceFromPlayer(enemy, a)); + return preferred.find((name) => canMove(enemy, name, livingEnemies)); + } + + function shuffledDirections() { + const names = Object.keys(DIRS); + for (let i = names.length - 1; i > 0; i -= 1) { + const j = Math.floor(Math.random() * (i + 1)); + [names[i], names[j]] = [names[j], names[i]]; + } + return names; + } + + function distanceFromPlayer(enemy, dirName) { + const dir = DIRS[dirName]; + const nextX = enemy.x + dir.x * TILE; + const nextY = enemy.y + dir.y * TILE; + const dx = nextX - player.x; + const dy = nextY - player.y; + return dx * dx + dy * dy; + } + + function canMove(entity, dirName, peerEnemies) { + const dir = DIRS[dirName]; + const probe = { ...entity, x: entity.x + dir.x * 4, y: entity.y + dir.y * 4 }; + return !isBlocked(probe, peerEnemies.filter((peer) => peer !== entity)); + } + function facePlayer(enemy) { const dx = player.x - enemy.x; const dy = player.y - enemy.y;