Initial commit: Naddie Jump — Monad Edition
This commit is contained in:
36
src/entities/Enemy.js
Normal file
36
src/entities/Enemy.js
Normal file
@@ -0,0 +1,36 @@
|
||||
import { Physics } from 'phaser';
|
||||
import { GAME_WIDTH } from '../config/game.config.js';
|
||||
|
||||
export class Enemy extends Physics.Arcade.Sprite {
|
||||
constructor(scene, x, y, type = 'bug') {
|
||||
super(scene, x, y, 'enemy_bug');
|
||||
scene.add.existing(this);
|
||||
scene.physics.add.existing(this);
|
||||
|
||||
this.enemyType = type;
|
||||
this.setScale(0.7);
|
||||
this.body.allowGravity = false;
|
||||
this.body.setSize(90, 80);
|
||||
this.body.setOffset(35, 30);
|
||||
|
||||
if (type === 'bug') {
|
||||
this.speed = Phaser.Math.Between(60, 140) * (Math.random() < 0.5 ? 1 : -1);
|
||||
this.startX = x;
|
||||
this.patrolRange = Phaser.Math.Between(80, 200);
|
||||
}
|
||||
}
|
||||
|
||||
preUpdate(time, delta) {
|
||||
super.preUpdate(time, delta);
|
||||
if (this.enemyType === 'bug') {
|
||||
this.x += this.speed * (delta / 1000);
|
||||
if (Math.abs(this.x - this.startX) > this.patrolRange) {
|
||||
this.speed *= -1;
|
||||
this.setFlipX(this.speed < 0);
|
||||
}
|
||||
// Wrap
|
||||
if (this.x < -60) this.x = GAME_WIDTH + 60;
|
||||
if (this.x > GAME_WIDTH + 60) this.x = -60;
|
||||
}
|
||||
}
|
||||
}
|
||||
80
src/entities/Platform.js
Normal file
80
src/entities/Platform.js
Normal file
@@ -0,0 +1,80 @@
|
||||
import { Physics } from 'phaser';
|
||||
|
||||
export class Platform extends Physics.Arcade.Sprite {
|
||||
constructor(scene, x, y, type = 'stable') {
|
||||
super(scene, x, y, 'platform');
|
||||
scene.add.existing(this);
|
||||
scene.physics.add.existing(this, true); // static body by default
|
||||
|
||||
this.platformType = type;
|
||||
|
||||
this.breakingState = 0;
|
||||
this.moveSpeed = 0;
|
||||
this.moveRange = 0;
|
||||
this.startX = x;
|
||||
|
||||
if (type === 'moving') {
|
||||
// Convert to dynamic for movement
|
||||
this.body.destroy();
|
||||
this.body = new Phaser.Physics.Arcade.Body(scene.physics.world, this);
|
||||
this.body.allowGravity = false;
|
||||
this.body.immovable = true;
|
||||
this.moveSpeed = Phaser.Math.Between(50, 120) * (Math.random() < 0.5 ? 1 : -1);
|
||||
this.moveRange = Phaser.Math.Between(60, 160);
|
||||
} else if (type === 'breaking') {
|
||||
this.setTint(0x999999); // grey tint for broken look
|
||||
} else if (type === 'genesis') {
|
||||
this.setTint(0xffd700); // gold
|
||||
this.genesisGlow = scene.add.ellipse(x, y + 10, 100, 30, 0xffd700, 0.25)
|
||||
.setDepth(this.depth - 1);
|
||||
scene.tweens.add({
|
||||
targets: this.genesisGlow,
|
||||
scaleX: 1.4,
|
||||
scaleY: 1.4,
|
||||
alpha: 0.1,
|
||||
duration: 800,
|
||||
yoyo: true,
|
||||
repeat: -1,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
preUpdate(time, delta) {
|
||||
super.preUpdate(time, delta);
|
||||
if (this.platformType === 'moving' && this.body) {
|
||||
this.setVelocityX(this.moveSpeed);
|
||||
if (Math.abs(this.x - this.startX) > this.moveRange) {
|
||||
this.moveSpeed *= -1;
|
||||
}
|
||||
if (this.genesisGlow) this.genesisGlow.setPosition(this.x, this.y + 10);
|
||||
}
|
||||
}
|
||||
|
||||
onPlayerLand(player) {
|
||||
if (this.platformType === 'breaking') {
|
||||
if (this.breakingState > 0) return false;
|
||||
this.breakingState = 2;
|
||||
this.disableBody(true, false);
|
||||
this.scene.tweens.add({
|
||||
targets: this,
|
||||
alpha: 0,
|
||||
scaleX: 0.3,
|
||||
scaleY: 0.1,
|
||||
duration: 250,
|
||||
onComplete: () => this.destroy(),
|
||||
});
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
isBroken() {
|
||||
return this.breakingState >= 2;
|
||||
}
|
||||
|
||||
destroy(fromScene) {
|
||||
if (this.glowTween) this.glowTween.stop();
|
||||
if (this.genesisGlow) this.genesisGlow.destroy();
|
||||
super.destroy(fromScene);
|
||||
}
|
||||
}
|
||||
117
src/entities/Player.js
Normal file
117
src/entities/Player.js
Normal file
@@ -0,0 +1,117 @@
|
||||
import { Physics } from 'phaser';
|
||||
import {
|
||||
GAME_WIDTH,
|
||||
JUMP_VELOCITY,
|
||||
SUPER_JUMP_VELOCITY,
|
||||
ROCKET_VELOCITY,
|
||||
PROPELLER_VELOCITY,
|
||||
PLAYER_SPEED,
|
||||
} 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'; // normal, propeller, rocket, dead
|
||||
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);
|
||||
|
||||
// Screen wrap
|
||||
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;
|
||||
|
||||
// Tilt sprite based on movement
|
||||
if (velocityX < 0) {
|
||||
this.setFlipX(true);
|
||||
this.setAngle(-5);
|
||||
} else if (velocityX > 0) {
|
||||
this.setFlipX(false);
|
||||
this.setAngle(5);
|
||||
} else {
|
||||
this.setAngle(0);
|
||||
}
|
||||
|
||||
// Power-up timers
|
||||
if (this.state === 'propeller') {
|
||||
this.propellerTimer -= delta;
|
||||
this.setVelocityY(PROPELLER_VELOCITY);
|
||||
if (this.propellerTimer <= 0) this.endPowerUp();
|
||||
} else if (this.state === 'rocket') {
|
||||
this.rocketTimer -= delta;
|
||||
this.setVelocityY(ROCKET_VELOCITY);
|
||||
if (this.rocketTimer <= 0) this.endPowerUp();
|
||||
}
|
||||
}
|
||||
|
||||
jump(force = JUMP_VELOCITY) {
|
||||
if (this.state === 'dead' || this.state === 'rocket' || this.state === 'propeller') return;
|
||||
this.setVelocityY(force);
|
||||
// Squash animation
|
||||
this.scene.tweens.add({
|
||||
targets: this,
|
||||
scaleX: 0.55,
|
||||
scaleY: 0.4,
|
||||
duration: 80,
|
||||
yoyo: true,
|
||||
ease: 'Quad.easeOut',
|
||||
});
|
||||
}
|
||||
|
||||
startPropeller() {
|
||||
if (this.state === 'dead') return;
|
||||
this.state = 'propeller';
|
||||
this.propellerTimer = 3500;
|
||||
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 = 4000;
|
||||
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.setVelocity(0, -250);
|
||||
this.body.allowGravity = true;
|
||||
}
|
||||
}
|
||||
21
src/entities/PropellerHat.js
Normal file
21
src/entities/PropellerHat.js
Normal file
@@ -0,0 +1,21 @@
|
||||
import { Physics } from 'phaser';
|
||||
|
||||
export class PropellerHat extends Physics.Arcade.Sprite {
|
||||
constructor(scene, x, y) {
|
||||
super(scene, x, y, 'propeller_hat');
|
||||
scene.add.existing(this);
|
||||
scene.physics.add.existing(this, true);
|
||||
|
||||
this.setScale(0.45);
|
||||
this.body.setSize(50, 40);
|
||||
this.body.setOffset(48, 43);
|
||||
|
||||
// No spin — static propeller hat
|
||||
}
|
||||
|
||||
onPlayerTouch(player) {
|
||||
this.destroy();
|
||||
player.startPropeller();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
29
src/entities/Rocket.js
Normal file
29
src/entities/Rocket.js
Normal file
@@ -0,0 +1,29 @@
|
||||
import { Physics } from 'phaser';
|
||||
|
||||
export class Rocket extends Physics.Arcade.Sprite {
|
||||
constructor(scene, x, y) {
|
||||
super(scene, x, y, 'rocket');
|
||||
scene.add.existing(this);
|
||||
scene.physics.add.existing(this, true);
|
||||
|
||||
this.setScale(0.45);
|
||||
this.body.setSize(80, 100, false);
|
||||
this.body.setOffset(59, 91);
|
||||
|
||||
// Float animation
|
||||
scene.tweens.add({
|
||||
targets: this,
|
||||
y: y - 8,
|
||||
duration: 600,
|
||||
yoyo: true,
|
||||
repeat: -1,
|
||||
ease: 'Sine.easeInOut',
|
||||
});
|
||||
}
|
||||
|
||||
onPlayerTouch(player) {
|
||||
this.destroy();
|
||||
player.startRocket();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
23
src/entities/Spring.js
Normal file
23
src/entities/Spring.js
Normal file
@@ -0,0 +1,23 @@
|
||||
import { Physics } from 'phaser';
|
||||
import { SUPER_JUMP_VELOCITY } from '../config/game.config.js';
|
||||
|
||||
export class Spring extends Physics.Arcade.Sprite {
|
||||
constructor(scene, x, y) {
|
||||
super(scene, x, y, 'spring');
|
||||
scene.add.existing(this);
|
||||
scene.physics.add.existing(this, true);
|
||||
|
||||
this.setScale(1);
|
||||
this.body.setSize(20, 32);
|
||||
this.active = true;
|
||||
}
|
||||
|
||||
onPlayerTouch(player) {
|
||||
if (!this.active) return false;
|
||||
this.active = false;
|
||||
this.setVisible(false);
|
||||
this.body.enable = false;
|
||||
player.jump(SUPER_JUMP_VELOCITY);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user