167 lines
5.8 KiB
JavaScript
167 lines
5.8 KiB
JavaScript
import TilePropertyHelper from '../systems/TilePropertyHelper.js';
|
|
|
|
export default class Player {
|
|
/**
|
|
* @param {Phaser.Scene} scene
|
|
* @param {number} x World pixel x (center)
|
|
* @param {number} y World pixel y (center)
|
|
* @param {Phaser.Tilemaps.TilemapLayer} laddersLayer
|
|
*/
|
|
constructor(scene, x, y, laddersLayer) {
|
|
this._scene = scene;
|
|
this._laddersLayer = laddersLayer;
|
|
|
|
this.lives = 3;
|
|
this.onLadder = false;
|
|
this.atLadderTop = false;
|
|
this._enteredFromTop = false;
|
|
|
|
// Create physics sprite
|
|
this._sprite = scene.physics.add.sprite(x, y, 'player', 0);
|
|
this._sprite.setCollideWorldBounds(false);
|
|
// Body is narrower/shorter than the 128x256 frame to fit the character artwork
|
|
this._sprite.body.setSize(80, 240).setOffset(24, 8);
|
|
|
|
// Animations
|
|
scene.anims.create({
|
|
key: 'walk',
|
|
frames: scene.anims.generateFrameNumbers('player', { start: 0, end: 1 }),
|
|
frameRate: 8,
|
|
repeat: -1,
|
|
});
|
|
scene.anims.create({
|
|
key: 'stand',
|
|
frames: [{ key: 'player', frame: 0 }],
|
|
frameRate: 1,
|
|
repeat: 0,
|
|
});
|
|
|
|
this._cursors = scene.input.keyboard.createCursorKeys();
|
|
this._jumpKey = this._cursors.up;
|
|
}
|
|
|
|
get sprite() { return this._sprite; }
|
|
get body() { return this._sprite.body; }
|
|
|
|
update(time, delta) {
|
|
this._handleLadder();
|
|
this._handleMovement();
|
|
this._updateAnimation();
|
|
}
|
|
|
|
_handleLadder() {
|
|
const sprite = this._sprite;
|
|
const cursors = this._cursors;
|
|
const body = sprite.body;
|
|
|
|
// Tile at player center (for body-of-ladder detection)
|
|
const centerTile = this._laddersLayer.getTileAtWorldXY(sprite.x, sprite.y);
|
|
// Tile just below body bottom (body bottom = sprite.y + 120; +2 puts us inside
|
|
// the tile when standing on it, since physics places body bottom at tile top)
|
|
const feetTile = this._laddersLayer.getTileAtWorldXY(sprite.x, sprite.y + 122);
|
|
|
|
const centerIsLadder = centerTile && TilePropertyHelper.isLadder(centerTile.index);
|
|
const feetIsLadderTop = feetTile && TilePropertyHelper.isLadderTop(feetTile.index);
|
|
|
|
// Update atLadderTop: feet are resting on a top rung and not actively climbing
|
|
this.atLadderTop = !!(feetIsLadderTop && !this.onLadder);
|
|
|
|
if (this.onLadder) {
|
|
// Move up or down
|
|
if (cursors.up.isDown) {
|
|
sprite.body.setVelocityY(-200);
|
|
} else if (cursors.down.isDown) {
|
|
sprite.body.setVelocityY(200);
|
|
} else {
|
|
sprite.body.setVelocityY(0);
|
|
}
|
|
|
|
// Exit ladder when pressing left or right
|
|
if (cursors.left.isDown || cursors.right.isDown) {
|
|
this._exitLadder();
|
|
return;
|
|
}
|
|
|
|
// Once the center reaches a ladder tile we are fully on the ladder
|
|
if (centerIsLadder) {
|
|
this._enteredFromTop = false;
|
|
}
|
|
|
|
// Exit if center is no longer over a ladder tile.
|
|
// Exception: when we entered from the top the center starts above the ladder;
|
|
// suppress the exit until the player descends into the rungs, or until
|
|
// they press up (changing their mind).
|
|
if (!centerIsLadder && (!this._enteredFromTop || cursors.up.isDown)) {
|
|
this._exitLadder();
|
|
}
|
|
} else {
|
|
// Enter ladder when player center overlaps a ladder tile and pressing up/down
|
|
if (centerIsLadder && (cursors.up.isDown || cursors.down.isDown)) {
|
|
this.onLadder = true;
|
|
sprite.body.setGravityY(-800); // Cancels world gravity (800 + -800 = 0)
|
|
sprite.body.setVelocityY(0);
|
|
sprite.body.setVelocityX(0);
|
|
// Snap horizontally to ladder center
|
|
sprite.x = centerTile.pixelX + 64;
|
|
// Enter from the top: standing on the top rung tile, pressing down
|
|
} else if (feetIsLadderTop && cursors.down.isDown && body.blocked.down) {
|
|
this._enteredFromTop = true;
|
|
this.onLadder = true;
|
|
sprite.body.setGravityY(-800);
|
|
sprite.body.setVelocityY(200);
|
|
sprite.body.setVelocityX(0);
|
|
sprite.x = feetTile.pixelX + 64;
|
|
}
|
|
}
|
|
}
|
|
|
|
_exitLadder() {
|
|
this.onLadder = false;
|
|
this._enteredFromTop = false;
|
|
this._sprite.body.setGravityY(0); // Remove local gravity override; world gravity resumes
|
|
}
|
|
|
|
_handleMovement() {
|
|
if (this.onLadder) return;
|
|
|
|
const sprite = this._sprite;
|
|
const cursors = this._cursors;
|
|
const body = sprite.body;
|
|
|
|
// Horizontal movement
|
|
if (cursors.left.isDown) {
|
|
body.setVelocityX(-250);
|
|
sprite.setFlipX(true);
|
|
} else if (cursors.right.isDown) {
|
|
body.setVelocityX(250);
|
|
sprite.setFlipX(false);
|
|
} else {
|
|
body.setVelocityX(0);
|
|
}
|
|
|
|
// Jump: only on key press (JustDown prevents hold-to-jump repeat)
|
|
// Allowed when: grounded AND (not on ladder OR standing at ladder top)
|
|
if (Phaser.Input.Keyboard.JustDown(this._jumpKey)) {
|
|
if (body.blocked.down) {
|
|
body.setVelocityY(-550);
|
|
}
|
|
}
|
|
}
|
|
|
|
_updateAnimation() {
|
|
const sprite = this._sprite;
|
|
|
|
if (this.onLadder) {
|
|
sprite.anims.stop();
|
|
sprite.setFrame(0);
|
|
return;
|
|
}
|
|
|
|
if (sprite.body.velocity.x !== 0) {
|
|
sprite.anims.play('walk', true);
|
|
} else {
|
|
sprite.anims.play('stand', true);
|
|
}
|
|
}
|
|
}
|