import { Physics } from 'phaser'; import { GAME_WIDTH, JUMP_VELOCITY, ROCKET_VELOCITY, PROPELLER_VELOCITY, PLAYER_SPEED, POWERUP_DURATION, } from '../config/game.config.js'; export class Player extends Physics.Arcade.Sprite { constructor(scene, x, y) { super(scene, x, y, 'player_idle'); scene.add.existing(this); scene.physics.add.existing(this); this.setCollideWorldBounds(false); this.setScale(0.45); this.body.setSize(70, 90); this.state = 'normal'; this.propellerTimer = 0; this.rocketTimer = 0; } update(cursors, wasd, touchLeft, touchRight, time, delta) { if (this.state === 'dead') return; let velocityX = 0; if (cursors.left.isDown || wasd.left.isDown || touchLeft) velocityX = -PLAYER_SPEED; if (cursors.right.isDown || wasd.right.isDown || touchRight) velocityX = PLAYER_SPEED; this.setVelocityX(velocityX); if (this.x < -this.width / 2) this.x = GAME_WIDTH + this.width / 2; if (this.x > GAME_WIDTH + this.width / 2) this.x = -this.width / 2; if (this.state === 'rocket') { this.rocketTimer -= delta; this.setVelocityY(ROCKET_VELOCITY); if (velocityX < 0) this.setFlipX(true); else if (velocityX > 0) this.setFlipX(false); // Rocket: rigid upright with tiny lean toward movement this.setAngle(velocityX === 0 ? 0 : (velocityX < 0 ? -3 : 3)); if (this.rocketTimer <= 0) this.endPowerUp(); } else if (this.state === 'propeller') { this.propellerTimer -= delta; this.setVelocityY(PROPELLER_VELOCITY); if (velocityX < 0) this.setFlipX(true); else if (velocityX > 0) this.setFlipX(false); // Propeller: gentle sinusoidal wobble const wobble = Math.sin(time / 80) * 4; this.setAngle(wobble + (velocityX < 0 ? -2 : velocityX > 0 ? 2 : 0)); if (this.propellerTimer <= 0) this.endPowerUp(); } else { if (velocityX < 0) { this.setFlipX(true); this.setAngle(-5); } else if (velocityX > 0) { this.setFlipX(false); this.setAngle(5); } else { this.setAngle(0); } } } jump(force = JUMP_VELOCITY) { if (this.state === 'dead' || this.state === 'rocket' || this.state === 'propeller') return false; this.setVelocityY(force); this.scene.tweens.add({ targets: this, scaleX: 0.55, scaleY: 0.4, duration: 80, yoyo: true, ease: 'Quad.easeOut', }); return true; } cutJump(factor) { if (this.state !== 'normal') return; if (this.body.velocity.y < 0) { this.setVelocityY(this.body.velocity.y * factor); } } startPropeller() { if (this.state === 'dead') return; this.state = 'propeller'; this.propellerTimer = POWERUP_DURATION.propeller; const oldHeight = this.displayHeight; this.setTexture('player_propeller'); this.setScale(0.45); this.body.setSize(70, 105); this.y -= (this.displayHeight - oldHeight) / 2; } startRocket() { if (this.state === 'dead') return; this.state = 'rocket'; this.rocketTimer = POWERUP_DURATION.rocket; const oldHeight = this.displayHeight; this.setTexture('player_rocket'); this.setScale(0.52); this.body.setSize(55, 180); this.y -= (this.displayHeight - oldHeight) / 2; } endPowerUp() { const oldHeight = this.displayHeight; this.state = 'normal'; this.setTexture('player_idle'); this.setScale(0.45); this.body.setSize(70, 90); this.setAngle(0); this.y -= (this.displayHeight - oldHeight) / 2; } die() { if (this.state === 'dead') return; this.state = 'dead'; this.setTexture('player_dead'); this.setScale(0.4); this.setAngle(0); this.body.setSize(70, 90); this.body.checkCollision.none = true; this.setVelocity(0, -250); this.body.allowGravity = true; } }