Spaces:
Sleeping
Sleeping
| const Player = require('./Player'); | |
| const { BOT_LIFETIME_MIN_MS, BOT_LIFETIME_MAX_MS } = require('../../../shared/constants'); | |
| class Bot extends Player { | |
| constructor(id, name, skinId) { | |
| super(id, name, skinId); | |
| this.isBot = true; | |
| this.decisionTimer = 0; | |
| // Track when the bot was created and assign a random lifetime between min/max | |
| this.spawnTime = Date.now(); | |
| this.lifetime = BOT_LIFETIME_MIN_MS + Math.floor(Math.random() * (BOT_LIFETIME_MAX_MS - BOT_LIFETIME_MIN_MS)); | |
| } | |
| updateAI(room) { | |
| this.decisionTimer--; | |
| if (this.decisionTimer <= 0) { | |
| // Make a new decision every 15-30 frames (~500ms - 1s) to simulate human reaction delay | |
| this.decisionTimer = 15 + Math.floor(Math.random() * 15); | |
| let targetX = this.x; | |
| let targetY = this.y; | |
| // 1. Scan for the nearest player/other bot | |
| let closestPlayer = null; | |
| let minPlayerDist = Infinity; | |
| for (const other of room.players.values()) { | |
| if (other.id === this.id) continue; | |
| const dx = other.x - this.x; | |
| const dy = other.y - this.y; | |
| const dist = Math.sqrt(dx * dx + dy * dy); | |
| if (dist < minPlayerDist) { | |
| minPlayerDist = dist; | |
| closestPlayer = other; | |
| } | |
| } | |
| // If a target is close enough (within 450 pixels) | |
| if (closestPlayer && minPlayerDist < 450) { | |
| // If we have more orbits, chase them! | |
| if (this.orbits.length > closestPlayer.orbits.length) { | |
| targetX = closestPlayer.x; | |
| targetY = closestPlayer.y; | |
| this.isBoosting = this.stamina > 30; // Boost when chasing if we have enough stamina | |
| } else { | |
| // Otherwise, run away from them in the opposite direction! | |
| const dx = closestPlayer.x - this.x; | |
| const dy = closestPlayer.y - this.y; | |
| targetX = this.x - dx * 2; | |
| targetY = this.y - dy * 2; | |
| this.isBoosting = this.stamina > 20; // Boost to escape! | |
| } | |
| } else { | |
| this.isBoosting = false; // Rest and recover stamina when collecting/wandering | |
| // 2. Otherwise, look for the nearest pickup | |
| let closestPickup = null; | |
| let minPickupDist = Infinity; | |
| for (const pickup of room.pickups.list()) { | |
| const dx = pickup.x - this.x; | |
| const dy = pickup.y - this.y; | |
| const dist = Math.sqrt(dx * dx + dy * dy); | |
| if (dist < minPickupDist) { | |
| minPickupDist = dist; | |
| closestPickup = pickup; | |
| } | |
| } | |
| if (closestPickup) { | |
| targetX = closestPickup.x; | |
| targetY = closestPickup.y; | |
| } else { | |
| // Random wander if no pickups or players | |
| targetX = this.x + (Math.random() - 0.5) * 400; | |
| targetY = this.y + (Math.random() - 0.5) * 400; | |
| } | |
| } | |
| this.setInput(targetX, targetY); | |
| } | |
| } | |
| } | |
| module.exports = Bot; | |