Spaces:
Sleeping
Sleeping
| const { MAP_WIDTH, MAP_HEIGHT, PLAYER_RADIUS, PLAYER_SPEED, ORBIT_RADIUS_BASE, ORBIT_RADIUS_STEP, SPAWN_PROTECTION_TICKS } = require('../../../shared/constants'); | |
| class Player { | |
| constructor(id, name = 'Player', skinId = 0) { | |
| this.id = id; | |
| this.name = name; | |
| this.skinId = skinId; | |
| this.x = Math.random() * MAP_WIDTH; | |
| this.y = Math.random() * MAP_HEIGHT; | |
| this.radius = PLAYER_RADIUS; | |
| this.score = 0; | |
| this.kills = 0; | |
| this.stamina = 100; | |
| this.isBoosting = false; | |
| this.input = { x: this.x, y: this.y }; | |
| this.angle = 0; | |
| this.orbits = []; | |
| this.protectionTicks = SPAWN_PROTECTION_TICKS; | |
| } | |
| setInput(x, y) { | |
| if (typeof x !== 'number' || typeof y !== 'number') return; | |
| this.input = { | |
| x: Math.min(Math.max(x, 0), MAP_WIDTH), | |
| y: Math.min(Math.max(y, 0), MAP_HEIGHT), | |
| }; | |
| } | |
| setBoost(boost) { | |
| this.isBoosting = boost; | |
| } | |
| update() { | |
| // 1. Calculate speed based on boosting state and stamina | |
| let currentSpeed = PLAYER_SPEED; | |
| if (this.isBoosting && this.stamina > 0) { | |
| currentSpeed = PLAYER_SPEED * 1.8; // 1.8x speed boost | |
| this.stamina = Math.max(0, this.stamina - 1.5); // Consume 1.5 stamina per tick (~45/s) | |
| } else { | |
| this.stamina = Math.min(100, this.stamina + 0.6); // Recover 0.6 stamina per tick (~18/s) | |
| } | |
| if (this.protectionTicks > 0) { | |
| this.protectionTicks = Math.max(0, this.protectionTicks - 1); | |
| } | |
| const dx = this.input.x - this.x; | |
| const dy = this.input.y - this.y; | |
| const dist = Math.sqrt(dx * dx + dy * dy); | |
| if (dist > 0) { | |
| this.angle = Math.atan2(dy, dx); | |
| } | |
| if (dist > currentSpeed) { | |
| this.x += (dx / dist) * currentSpeed; | |
| this.y += (dy / dist) * currentSpeed; | |
| } else { | |
| this.x = this.input.x; | |
| this.y = this.input.y; | |
| } | |
| } | |
| addOrbit(type = 'normal') { | |
| this.orbits.push({ | |
| angle: Math.random() * Math.PI * 2, | |
| ring: this.orbits.length % 2, | |
| type, | |
| hp: type === 'strong' ? 2 : 1, | |
| }); | |
| } | |
| removeOrbit(orbit) { | |
| const index = this.orbits.indexOf(orbit); | |
| if (index !== -1) { | |
| this.orbits.splice(index, 1); | |
| } | |
| } | |
| toJSON() { | |
| return { | |
| id: this.id, | |
| name: this.name, | |
| x: this.x, | |
| y: this.y, | |
| radius: this.radius, | |
| score: this.score, | |
| kills: this.kills, | |
| angle: this.angle, | |
| orbits: this.orbits, | |
| skinId: this.skinId, | |
| stamina: this.stamina, | |
| isBoosting: this.isBoosting, | |
| isProtected: this.protectionTicks > 0, | |
| }; | |
| } | |
| } | |
| module.exports = Player; | |