Sprint 0: bug fixes and cleanup
- Fix Rocket/PropellerHat hitboxes (relative to sprite size, not magic offsets) - Spring now destroys itself on use with squash animation instead of staying invisible - Guard all powerups against double-trigger via consumed flag - Save genesis glow tween reference for clean destroy - Player.die() disables collisions (no more post-death platform bounces) - Replace hardcoded enemy spawn cap 0.5 with DIFFICULTY.maxEnemyRate - Enemy spawn position now anchored to platform X (-60/+60), not random - Powerup spawn clamped to screen bounds - Skip powerup spawn on breaking platforms (was unfair) - ScoreManager.addPoints() public method; remove direct score mutation - HUD elements use setScrollFactor(0) instead of per-frame repositioning - All magic numbers extracted to SCORE / PHYSICS / POWERUP_DURATION config - Remove dead code: showDeathVignette, drawDebug, createParticles, deathOverlay - Remove dead constants: PLATFORM_WIDTH/HEIGHT, PLAYER_MAX_SPEED, tileBias - Remove SUBMIT TO CHAIN fake button, replace with Coming Soon text - Safer cleanup loop: collect-then-destroy instead of mutating during iterate - gwei particles now have setScrollFactor(0.3) for parallax depth
This commit is contained in:
@@ -3,17 +3,14 @@ export const GAME_HEIGHT = 854;
|
|||||||
|
|
||||||
export const GRAVITY = 1200;
|
export const GRAVITY = 1200;
|
||||||
export const JUMP_VELOCITY = -650;
|
export const JUMP_VELOCITY = -650;
|
||||||
export const SUPER_JUMP_VELOCITY = -950;
|
export const SUPER_JUMP_VELOCITY = -1050;
|
||||||
export const ROCKET_VELOCITY = -1400;
|
export const ROCKET_VELOCITY = -1400;
|
||||||
export const PROPELLER_VELOCITY = -950;
|
export const PROPELLER_VELOCITY = -950;
|
||||||
|
|
||||||
export const PLAYER_SPEED = 300;
|
export const PLAYER_SPEED = 300;
|
||||||
export const PLAYER_MAX_SPEED = 400;
|
|
||||||
|
|
||||||
export const PLATFORM_GAP_MIN = 60;
|
export const PLATFORM_GAP_MIN = 60;
|
||||||
export const PLATFORM_GAP_MAX = 120;
|
export const PLATFORM_GAP_MAX = 120;
|
||||||
export const PLATFORM_WIDTH = 80;
|
|
||||||
export const PLATFORM_HEIGHT = 24;
|
|
||||||
|
|
||||||
export const SPAWN_RATES = {
|
export const SPAWN_RATES = {
|
||||||
stable: 0.60,
|
stable: 0.60,
|
||||||
@@ -23,14 +20,12 @@ export const SPAWN_RATES = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export const POWERUP_RATES = {
|
export const POWERUP_RATES = {
|
||||||
none: 0.91,
|
|
||||||
spring: 0.04,
|
spring: 0.04,
|
||||||
propeller: 0.025,
|
propeller: 0.025,
|
||||||
rocket: 0.015,
|
rocket: 0.012,
|
||||||
};
|
};
|
||||||
|
|
||||||
export const ENEMY_RATES = {
|
export const ENEMY_RATES = {
|
||||||
none: 0.90,
|
|
||||||
bug: 0.10,
|
bug: 0.10,
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -39,5 +34,28 @@ export const DIFFICULTY = {
|
|||||||
gapIncreasePer1000: 12,
|
gapIncreasePer1000: 12,
|
||||||
maxGap: 160,
|
maxGap: 160,
|
||||||
enemyIncreasePer1000: 0.015,
|
enemyIncreasePer1000: 0.015,
|
||||||
maxEnemyRate: 0.25,
|
maxEnemyRate: 0.30,
|
||||||
|
};
|
||||||
|
|
||||||
|
export const SCORE = {
|
||||||
|
basePoints: 10,
|
||||||
|
genesisBonus: 50,
|
||||||
|
genesisJumps: 5,
|
||||||
|
genesisMultiplier: 2,
|
||||||
|
stompBonus: 25,
|
||||||
|
comboStep: 0.1,
|
||||||
|
comboMax: 3,
|
||||||
|
fallResetDistance: 300,
|
||||||
|
};
|
||||||
|
|
||||||
|
export const PHYSICS = {
|
||||||
|
stompTolerance: 12,
|
||||||
|
coyoteTime: 110,
|
||||||
|
jumpBufferTime: 130,
|
||||||
|
variableJumpCutoff: 0.45,
|
||||||
|
};
|
||||||
|
|
||||||
|
export const POWERUP_DURATION = {
|
||||||
|
propeller: 3500,
|
||||||
|
rocket: 3000,
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -4,17 +4,16 @@ export class Platform extends Physics.Arcade.Sprite {
|
|||||||
constructor(scene, x, y, type = 'stable') {
|
constructor(scene, x, y, type = 'stable') {
|
||||||
super(scene, x, y, 'platform');
|
super(scene, x, y, 'platform');
|
||||||
scene.add.existing(this);
|
scene.add.existing(this);
|
||||||
scene.physics.add.existing(this, true); // static body by default
|
scene.physics.add.existing(this, true);
|
||||||
|
|
||||||
this.platformType = type;
|
this.platformType = type;
|
||||||
|
|
||||||
this.breakingState = 0;
|
this.breakingState = 0;
|
||||||
this.moveSpeed = 0;
|
this.moveSpeed = 0;
|
||||||
this.moveRange = 0;
|
this.moveRange = 0;
|
||||||
this.startX = x;
|
this.startX = x;
|
||||||
|
this.glowTween = null;
|
||||||
|
|
||||||
if (type === 'moving') {
|
if (type === 'moving') {
|
||||||
// Convert to dynamic for movement
|
|
||||||
this.body.destroy();
|
this.body.destroy();
|
||||||
this.body = new Phaser.Physics.Arcade.Body(scene.physics.world, this);
|
this.body = new Phaser.Physics.Arcade.Body(scene.physics.world, this);
|
||||||
this.body.allowGravity = false;
|
this.body.allowGravity = false;
|
||||||
@@ -22,12 +21,12 @@ export class Platform extends Physics.Arcade.Sprite {
|
|||||||
this.moveSpeed = Phaser.Math.Between(50, 120) * (Math.random() < 0.5 ? 1 : -1);
|
this.moveSpeed = Phaser.Math.Between(50, 120) * (Math.random() < 0.5 ? 1 : -1);
|
||||||
this.moveRange = Phaser.Math.Between(60, 160);
|
this.moveRange = Phaser.Math.Between(60, 160);
|
||||||
} else if (type === 'breaking') {
|
} else if (type === 'breaking') {
|
||||||
this.setTint(0x999999); // grey tint for broken look
|
this.setTint(0x999999);
|
||||||
} else if (type === 'genesis') {
|
} else if (type === 'genesis') {
|
||||||
this.setTint(0xffd700); // gold
|
this.setTint(0xffd700);
|
||||||
this.genesisGlow = scene.add.ellipse(x, y + 10, 100, 30, 0xffd700, 0.25)
|
this.genesisGlow = scene.add.ellipse(x, y + 10, 100, 30, 0xffd700, 0.25)
|
||||||
.setDepth(this.depth - 1);
|
.setDepth(this.depth - 1);
|
||||||
scene.tweens.add({
|
this.glowTween = scene.tweens.add({
|
||||||
targets: this.genesisGlow,
|
targets: this.genesisGlow,
|
||||||
scaleX: 1.4,
|
scaleX: 1.4,
|
||||||
scaleY: 1.4,
|
scaleY: 1.4,
|
||||||
@@ -63,7 +62,7 @@ export class Platform extends Physics.Arcade.Sprite {
|
|||||||
duration: 250,
|
duration: 250,
|
||||||
onComplete: () => this.destroy(),
|
onComplete: () => this.destroy(),
|
||||||
});
|
});
|
||||||
return false;
|
return true;
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@@ -73,8 +72,14 @@ export class Platform extends Physics.Arcade.Sprite {
|
|||||||
}
|
}
|
||||||
|
|
||||||
destroy(fromScene) {
|
destroy(fromScene) {
|
||||||
if (this.glowTween) this.glowTween.stop();
|
if (this.glowTween) {
|
||||||
if (this.genesisGlow) this.genesisGlow.destroy();
|
this.glowTween.stop();
|
||||||
|
this.glowTween = null;
|
||||||
|
}
|
||||||
|
if (this.genesisGlow) {
|
||||||
|
this.genesisGlow.destroy();
|
||||||
|
this.genesisGlow = null;
|
||||||
|
}
|
||||||
super.destroy(fromScene);
|
super.destroy(fromScene);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,10 +2,10 @@ import { Physics } from 'phaser';
|
|||||||
import {
|
import {
|
||||||
GAME_WIDTH,
|
GAME_WIDTH,
|
||||||
JUMP_VELOCITY,
|
JUMP_VELOCITY,
|
||||||
SUPER_JUMP_VELOCITY,
|
|
||||||
ROCKET_VELOCITY,
|
ROCKET_VELOCITY,
|
||||||
PROPELLER_VELOCITY,
|
PROPELLER_VELOCITY,
|
||||||
PLAYER_SPEED,
|
PLAYER_SPEED,
|
||||||
|
POWERUP_DURATION,
|
||||||
} from '../config/game.config.js';
|
} from '../config/game.config.js';
|
||||||
|
|
||||||
export class Player extends Physics.Arcade.Sprite {
|
export class Player extends Physics.Arcade.Sprite {
|
||||||
@@ -18,7 +18,7 @@ export class Player extends Physics.Arcade.Sprite {
|
|||||||
this.setScale(0.45);
|
this.setScale(0.45);
|
||||||
this.body.setSize(70, 90);
|
this.body.setSize(70, 90);
|
||||||
|
|
||||||
this.state = 'normal'; // normal, propeller, rocket, dead
|
this.state = 'normal';
|
||||||
this.propellerTimer = 0;
|
this.propellerTimer = 0;
|
||||||
this.rocketTimer = 0;
|
this.rocketTimer = 0;
|
||||||
}
|
}
|
||||||
@@ -31,11 +31,9 @@ export class Player extends Physics.Arcade.Sprite {
|
|||||||
if (cursors.right.isDown || wasd.right.isDown || touchRight) velocityX = PLAYER_SPEED;
|
if (cursors.right.isDown || wasd.right.isDown || touchRight) velocityX = PLAYER_SPEED;
|
||||||
this.setVelocityX(velocityX);
|
this.setVelocityX(velocityX);
|
||||||
|
|
||||||
// Screen wrap
|
|
||||||
if (this.x < -this.width / 2) this.x = GAME_WIDTH + this.width / 2;
|
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.x > GAME_WIDTH + this.width / 2) this.x = -this.width / 2;
|
||||||
|
|
||||||
// Tilt sprite based on movement
|
|
||||||
if (velocityX < 0) {
|
if (velocityX < 0) {
|
||||||
this.setFlipX(true);
|
this.setFlipX(true);
|
||||||
this.setAngle(-5);
|
this.setAngle(-5);
|
||||||
@@ -46,7 +44,6 @@ export class Player extends Physics.Arcade.Sprite {
|
|||||||
this.setAngle(0);
|
this.setAngle(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Power-up timers
|
|
||||||
if (this.state === 'propeller') {
|
if (this.state === 'propeller') {
|
||||||
this.propellerTimer -= delta;
|
this.propellerTimer -= delta;
|
||||||
this.setVelocityY(PROPELLER_VELOCITY);
|
this.setVelocityY(PROPELLER_VELOCITY);
|
||||||
@@ -59,9 +56,8 @@ export class Player extends Physics.Arcade.Sprite {
|
|||||||
}
|
}
|
||||||
|
|
||||||
jump(force = JUMP_VELOCITY) {
|
jump(force = JUMP_VELOCITY) {
|
||||||
if (this.state === 'dead' || this.state === 'rocket' || this.state === 'propeller') return;
|
if (this.state === 'dead' || this.state === 'rocket' || this.state === 'propeller') return false;
|
||||||
this.setVelocityY(force);
|
this.setVelocityY(force);
|
||||||
// Squash animation
|
|
||||||
this.scene.tweens.add({
|
this.scene.tweens.add({
|
||||||
targets: this,
|
targets: this,
|
||||||
scaleX: 0.55,
|
scaleX: 0.55,
|
||||||
@@ -70,12 +66,20 @@ export class Player extends Physics.Arcade.Sprite {
|
|||||||
yoyo: true,
|
yoyo: true,
|
||||||
ease: 'Quad.easeOut',
|
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() {
|
startPropeller() {
|
||||||
if (this.state === 'dead') return;
|
if (this.state === 'dead') return;
|
||||||
this.state = 'propeller';
|
this.state = 'propeller';
|
||||||
this.propellerTimer = 3500;
|
this.propellerTimer = POWERUP_DURATION.propeller;
|
||||||
const oldHeight = this.displayHeight;
|
const oldHeight = this.displayHeight;
|
||||||
this.setTexture('player_propeller');
|
this.setTexture('player_propeller');
|
||||||
this.setScale(0.45);
|
this.setScale(0.45);
|
||||||
@@ -86,7 +90,7 @@ export class Player extends Physics.Arcade.Sprite {
|
|||||||
startRocket() {
|
startRocket() {
|
||||||
if (this.state === 'dead') return;
|
if (this.state === 'dead') return;
|
||||||
this.state = 'rocket';
|
this.state = 'rocket';
|
||||||
this.rocketTimer = 4000;
|
this.rocketTimer = POWERUP_DURATION.rocket;
|
||||||
const oldHeight = this.displayHeight;
|
const oldHeight = this.displayHeight;
|
||||||
this.setTexture('player_rocket');
|
this.setTexture('player_rocket');
|
||||||
this.setScale(0.52);
|
this.setScale(0.52);
|
||||||
@@ -111,6 +115,7 @@ export class Player extends Physics.Arcade.Sprite {
|
|||||||
this.setScale(0.4);
|
this.setScale(0.4);
|
||||||
this.setAngle(0);
|
this.setAngle(0);
|
||||||
this.body.setSize(70, 90);
|
this.body.setSize(70, 90);
|
||||||
|
this.body.checkCollision.none = true;
|
||||||
this.setVelocity(0, -250);
|
this.setVelocity(0, -250);
|
||||||
this.body.allowGravity = true;
|
this.body.allowGravity = true;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,15 +7,18 @@ export class PropellerHat extends Physics.Arcade.Sprite {
|
|||||||
scene.physics.add.existing(this, true);
|
scene.physics.add.existing(this, true);
|
||||||
|
|
||||||
this.setScale(0.45);
|
this.setScale(0.45);
|
||||||
this.body.setSize(50, 40);
|
this.body.setSize(this.width * 0.7, this.height * 0.5);
|
||||||
this.body.setOffset(48, 43);
|
this.body.setOffset(this.width * 0.15, this.height * 0.25);
|
||||||
|
this.consumed = false;
|
||||||
// No spin — static propeller hat
|
|
||||||
}
|
}
|
||||||
|
|
||||||
onPlayerTouch(player) {
|
onPlayerTouch(player) {
|
||||||
this.destroy();
|
if (this.consumed) return false;
|
||||||
|
this.consumed = true;
|
||||||
|
this.body.enable = false;
|
||||||
|
this.setVisible(false);
|
||||||
player.startPropeller();
|
player.startPropeller();
|
||||||
|
this.destroy();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,11 +7,11 @@ export class Rocket extends Physics.Arcade.Sprite {
|
|||||||
scene.physics.add.existing(this, true);
|
scene.physics.add.existing(this, true);
|
||||||
|
|
||||||
this.setScale(0.45);
|
this.setScale(0.45);
|
||||||
this.body.setSize(80, 100, false);
|
this.body.setSize(this.width * 0.5, this.height * 0.7);
|
||||||
this.body.setOffset(59, 91);
|
this.body.setOffset(this.width * 0.25, this.height * 0.15);
|
||||||
|
this.consumed = false;
|
||||||
|
|
||||||
// Float animation
|
this.floatTween = scene.tweens.add({
|
||||||
scene.tweens.add({
|
|
||||||
targets: this,
|
targets: this,
|
||||||
y: y - 8,
|
y: y - 8,
|
||||||
duration: 600,
|
duration: 600,
|
||||||
@@ -22,8 +22,20 @@ export class Rocket extends Physics.Arcade.Sprite {
|
|||||||
}
|
}
|
||||||
|
|
||||||
onPlayerTouch(player) {
|
onPlayerTouch(player) {
|
||||||
this.destroy();
|
if (this.consumed) return false;
|
||||||
|
this.consumed = true;
|
||||||
|
this.body.enable = false;
|
||||||
|
this.setVisible(false);
|
||||||
player.startRocket();
|
player.startRocket();
|
||||||
|
this.destroy();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
destroy(fromScene) {
|
||||||
|
if (this.floatTween) {
|
||||||
|
this.floatTween.stop();
|
||||||
|
this.floatTween = null;
|
||||||
|
}
|
||||||
|
super.destroy(fromScene);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,15 +9,21 @@ export class Spring extends Physics.Arcade.Sprite {
|
|||||||
|
|
||||||
this.setScale(1);
|
this.setScale(1);
|
||||||
this.body.setSize(20, 32);
|
this.body.setSize(20, 32);
|
||||||
this.active = true;
|
this.consumed = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
onPlayerTouch(player) {
|
onPlayerTouch(player) {
|
||||||
if (!this.active) return false;
|
if (this.consumed) return false;
|
||||||
this.active = false;
|
this.consumed = true;
|
||||||
this.setVisible(false);
|
player.jump(SUPER_JUMP_VELOCITY, true);
|
||||||
this.body.enable = false;
|
this.scene.tweens.add({
|
||||||
player.jump(SUPER_JUMP_VELOCITY);
|
targets: this,
|
||||||
|
scaleY: 1.6,
|
||||||
|
duration: 80,
|
||||||
|
yoyo: true,
|
||||||
|
ease: 'Quad.easeOut',
|
||||||
|
onComplete: () => this.destroy(),
|
||||||
|
});
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
11
src/main.js
11
src/main.js
@@ -1,9 +1,9 @@
|
|||||||
import { Game, AUTO } from 'phaser';
|
import { Game, AUTO, Scale } from 'phaser';
|
||||||
import { BootScene } from './scenes/BootScene.js';
|
import { BootScene } from './scenes/BootScene.js';
|
||||||
import { MenuScene } from './scenes/MenuScene.js';
|
import { MenuScene } from './scenes/MenuScene.js';
|
||||||
import { GameScene } from './scenes/GameScene.js';
|
import { GameScene } from './scenes/GameScene.js';
|
||||||
import { GameOverScene } from './scenes/GameOverScene.js';
|
import { GameOverScene } from './scenes/GameOverScene.js';
|
||||||
import { GAME_WIDTH, GAME_HEIGHT } from './config/game.config.js';
|
import { GAME_WIDTH, GAME_HEIGHT, GRAVITY } from './config/game.config.js';
|
||||||
|
|
||||||
const config = {
|
const config = {
|
||||||
type: AUTO,
|
type: AUTO,
|
||||||
@@ -12,15 +12,14 @@ const config = {
|
|||||||
height: GAME_HEIGHT,
|
height: GAME_HEIGHT,
|
||||||
backgroundColor: '#0d001a',
|
backgroundColor: '#0d001a',
|
||||||
scale: {
|
scale: {
|
||||||
mode: Phaser.Scale.FIT,
|
mode: Scale.FIT,
|
||||||
autoCenter: Phaser.Scale.CENTER_BOTH,
|
autoCenter: Scale.CENTER_BOTH,
|
||||||
},
|
},
|
||||||
physics: {
|
physics: {
|
||||||
default: 'arcade',
|
default: 'arcade',
|
||||||
arcade: {
|
arcade: {
|
||||||
gravity: { y: 1200 },
|
gravity: { y: GRAVITY },
|
||||||
debug: false,
|
debug: false,
|
||||||
tileBias: 64,
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
scene: [BootScene, MenuScene, GameScene, GameOverScene],
|
scene: [BootScene, MenuScene, GameScene, GameOverScene],
|
||||||
|
|||||||
@@ -25,22 +25,19 @@ export class PlatformManager {
|
|||||||
this.spawnPlatform(difficultyLevel);
|
this.spawnPlatform(difficultyLevel);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Cleanup below kill line — destroy when object's bottom crosses it
|
this.cleanupGroup(this.platforms, killLine);
|
||||||
this.platforms.children.iterate((p) => {
|
this.cleanupGroup(this.enemies, killLine);
|
||||||
if (p && p.y + p.displayHeight / 2 > killLine) {
|
this.cleanupGroup(this.powerups, killLine);
|
||||||
p.destroy();
|
}
|
||||||
}
|
|
||||||
});
|
cleanupGroup(group, killLine) {
|
||||||
this.enemies.children.iterate((e) => {
|
const toDestroy = [];
|
||||||
if (e && e.y + e.displayHeight / 2 > killLine) {
|
group.children.iterate((obj) => {
|
||||||
e.destroy();
|
if (obj && obj.y + obj.displayHeight / 2 > killLine) {
|
||||||
}
|
toDestroy.push(obj);
|
||||||
});
|
|
||||||
this.powerups.children.iterate((pu) => {
|
|
||||||
if (pu && pu.y + pu.displayHeight / 2 > killLine) {
|
|
||||||
pu.destroy();
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
toDestroy.forEach((obj) => obj.destroy());
|
||||||
}
|
}
|
||||||
|
|
||||||
spawnPlatform(difficultyLevel) {
|
spawnPlatform(difficultyLevel) {
|
||||||
@@ -56,7 +53,7 @@ export class PlatformManager {
|
|||||||
|
|
||||||
const x = Phaser.Math.Between(60, GAME_WIDTH - 60);
|
const x = Phaser.Math.Between(60, GAME_WIDTH - 60);
|
||||||
const rand = Math.random();
|
const rand = Math.random();
|
||||||
let type = 'stable';
|
let type;
|
||||||
if (rand < SPAWN_RATES.stable) type = 'stable';
|
if (rand < SPAWN_RATES.stable) type = 'stable';
|
||||||
else if (rand < SPAWN_RATES.stable + SPAWN_RATES.moving) type = 'moving';
|
else if (rand < SPAWN_RATES.stable + SPAWN_RATES.moving) type = 'moving';
|
||||||
else if (rand < SPAWN_RATES.stable + SPAWN_RATES.moving + SPAWN_RATES.breaking) type = 'breaking';
|
else if (rand < SPAWN_RATES.stable + SPAWN_RATES.moving + SPAWN_RATES.breaking) type = 'breaking';
|
||||||
@@ -65,47 +62,44 @@ export class PlatformManager {
|
|||||||
const platform = new Platform(this.scene, x, this.highestY, type);
|
const platform = new Platform(this.scene, x, this.highestY, type);
|
||||||
this.platforms.add(platform);
|
this.platforms.add(platform);
|
||||||
|
|
||||||
|
if (type !== 'breaking') {
|
||||||
this.maybeSpawnPowerUp(x, this.highestY - 25);
|
this.maybeSpawnPowerUp(x, this.highestY - 25);
|
||||||
|
}
|
||||||
this.maybeSpawnEnemy(x, this.highestY, difficultyLevel);
|
this.maybeSpawnEnemy(x, this.highestY, difficultyLevel);
|
||||||
}
|
}
|
||||||
|
|
||||||
maybeSpawnPowerUp(x, y) {
|
maybeSpawnPowerUp(platformX, y) {
|
||||||
const rand = Math.random();
|
const rand = Math.random();
|
||||||
let type = 'none';
|
let type = null;
|
||||||
if (rand < POWERUP_RATES.spring) type = 'spring';
|
if (rand < POWERUP_RATES.spring) type = 'spring';
|
||||||
else if (rand < POWERUP_RATES.spring + POWERUP_RATES.propeller) type = 'propeller';
|
else if (rand < POWERUP_RATES.spring + POWERUP_RATES.propeller) type = 'propeller';
|
||||||
else if (rand < POWERUP_RATES.spring + POWERUP_RATES.propeller + POWERUP_RATES.rocket) type = 'rocket';
|
else if (rand < POWERUP_RATES.spring + POWERUP_RATES.propeller + POWERUP_RATES.rocket) type = 'rocket';
|
||||||
|
|
||||||
if (type === 'none') return;
|
if (!type) return;
|
||||||
|
|
||||||
const offsetX = Phaser.Math.Between(-25, 25);
|
const x = Phaser.Math.Clamp(platformX + Phaser.Math.Between(-15, 15), 30, GAME_WIDTH - 30);
|
||||||
if (type === 'spring') {
|
if (type === 'spring') {
|
||||||
this.powerups.add(new Spring(this.scene, x + offsetX, y));
|
this.powerups.add(new Spring(this.scene, x, y));
|
||||||
} else if (type === 'propeller') {
|
} else if (type === 'propeller') {
|
||||||
this.powerups.add(new PropellerHat(this.scene, x + offsetX, y - 35));
|
this.powerups.add(new PropellerHat(this.scene, x, y - 35));
|
||||||
} else if (type === 'rocket') {
|
} else if (type === 'rocket') {
|
||||||
this.powerups.add(new Rocket(this.scene, x + offsetX, y - 45));
|
this.powerups.add(new Rocket(this.scene, x, y - 45));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
maybeSpawnEnemy(x, y, difficultyLevel) {
|
maybeSpawnEnemy(platformX, platformY, difficultyLevel) {
|
||||||
const enemyBonus = Math.min(
|
const enemyBonus = Math.min(
|
||||||
Math.floor(difficultyLevel / 1000) * DIFFICULTY.enemyIncreasePer1000,
|
Math.floor(difficultyLevel / 1000) * DIFFICULTY.enemyIncreasePer1000,
|
||||||
DIFFICULTY.maxEnemyRate
|
DIFFICULTY.maxEnemyRate
|
||||||
);
|
);
|
||||||
const bugRate = Math.min(ENEMY_RATES.bug + enemyBonus, 0.5);
|
const bugRate = Math.min(ENEMY_RATES.bug + enemyBonus, DIFFICULTY.maxEnemyRate);
|
||||||
const rand = Math.random();
|
if (Math.random() >= bugRate) return;
|
||||||
|
|
||||||
let type = 'none';
|
const offset = Phaser.Math.Between(-60, 60);
|
||||||
if (rand < bugRate) type = 'bug';
|
const ex = Phaser.Math.Clamp(platformX + offset, 50, GAME_WIDTH - 50);
|
||||||
|
const ey = platformY - Phaser.Math.Between(60, 130);
|
||||||
|
|
||||||
if (type === 'none') return;
|
const minDist = 150;
|
||||||
|
|
||||||
const ex = Phaser.Math.Between(50, GAME_WIDTH - 50);
|
|
||||||
const ey = y - Phaser.Math.Between(50, 140);
|
|
||||||
|
|
||||||
// Ensure minimum distance from existing enemies
|
|
||||||
const minDist = 120;
|
|
||||||
let tooClose = false;
|
let tooClose = false;
|
||||||
this.enemies.children.iterate((e) => {
|
this.enemies.children.iterate((e) => {
|
||||||
if (e && Phaser.Math.Distance.Between(ex, ey, e.x, e.y) < minDist) {
|
if (e && Phaser.Math.Distance.Between(ex, ey, e.x, e.y) < minDist) {
|
||||||
@@ -114,7 +108,7 @@ export class PlatformManager {
|
|||||||
});
|
});
|
||||||
if (tooClose) return;
|
if (tooClose) return;
|
||||||
|
|
||||||
this.enemies.add(new Enemy(this.scene, ex, ey, type));
|
this.enemies.add(new Enemy(this.scene, ex, ey, 'bug'));
|
||||||
}
|
}
|
||||||
|
|
||||||
getPlatforms() {
|
getPlatforms() {
|
||||||
|
|||||||
@@ -1,3 +1,5 @@
|
|||||||
|
import { SCORE } from '../config/game.config.js';
|
||||||
|
|
||||||
export class ScoreManager {
|
export class ScoreManager {
|
||||||
constructor(scene) {
|
constructor(scene) {
|
||||||
this.scene = scene;
|
this.scene = scene;
|
||||||
@@ -5,56 +7,47 @@ export class ScoreManager {
|
|||||||
this.blockHeight = 0;
|
this.blockHeight = 0;
|
||||||
this.combo = 0;
|
this.combo = 0;
|
||||||
this.comboMultiplier = 1;
|
this.comboMultiplier = 1;
|
||||||
this.lastPlatformY = null;
|
|
||||||
this.genesisActive = false;
|
this.genesisActive = false;
|
||||||
this.genesisJumps = 0;
|
this.genesisJumps = 0;
|
||||||
|
|
||||||
this.hudBg = scene.add.rectangle(0, 0, 200, 90, 0x000000, 0.5)
|
this.hudBg = scene.add.rectangle(10, 10, 200, 90, 0x000000, 0.5)
|
||||||
.setOrigin(0)
|
.setOrigin(0)
|
||||||
|
.setScrollFactor(0)
|
||||||
.setDepth(200);
|
.setDepth(200);
|
||||||
|
|
||||||
this.hudScore = scene.add.text(16, 16, 'Gas: 0', {
|
this.hudScore = scene.add.text(16, 16, 'Gas: 0', {
|
||||||
fontFamily: '"Press Start 2P", monospace',
|
fontFamily: '"Press Start 2P", monospace',
|
||||||
fontSize: '14px',
|
fontSize: '14px',
|
||||||
color: '#d8b4fe',
|
color: '#d8b4fe',
|
||||||
}).setOrigin(0).setDepth(200).setShadow(1, 1, '#000000', 2, false, true);
|
}).setOrigin(0).setScrollFactor(0).setDepth(200).setShadow(1, 1, '#000000', 2, false, true);
|
||||||
|
|
||||||
this.hudBlocks = scene.add.text(16, 42, 'Block: 0', {
|
this.hudBlocks = scene.add.text(16, 42, 'Block: 0', {
|
||||||
fontFamily: '"Press Start 2P", monospace',
|
fontFamily: '"Press Start 2P", monospace',
|
||||||
fontSize: '12px',
|
fontSize: '12px',
|
||||||
color: '#a855f7',
|
color: '#a855f7',
|
||||||
}).setOrigin(0).setDepth(200).setShadow(1, 1, '#000000', 2, false, true);
|
}).setOrigin(0).setScrollFactor(0).setDepth(200).setShadow(1, 1, '#000000', 2, false, true);
|
||||||
|
|
||||||
this.hudCombo = scene.add.text(16, 66, '', {
|
this.hudCombo = scene.add.text(16, 66, '', {
|
||||||
fontFamily: '"Press Start 2P", monospace',
|
fontFamily: '"Press Start 2P", monospace',
|
||||||
fontSize: '10px',
|
fontSize: '10px',
|
||||||
color: '#22c55e',
|
color: '#22c55e',
|
||||||
}).setOrigin(0).setDepth(200).setShadow(1, 1, '#000000', 2, false, true);
|
}).setOrigin(0).setScrollFactor(0).setDepth(200).setShadow(1, 1, '#000000', 2, false, true);
|
||||||
|
|
||||||
this.hudBest = scene.add.text(scene.scale.width - 80, 16, 'Best: 0', {
|
this.hudBest = scene.add.text(scene.scale.width - 16, 16, 'Best: 0', {
|
||||||
fontFamily: '"Press Start 2P", monospace',
|
fontFamily: '"Press Start 2P", monospace',
|
||||||
fontSize: '9px',
|
fontSize: '9px',
|
||||||
color: '#666',
|
color: '#aaa',
|
||||||
}).setOrigin(1, 0).setDepth(200).setShadow(1, 1, '#000000', 2, false, true);
|
}).setOrigin(1, 0).setScrollFactor(0).setDepth(200).setShadow(1, 1, '#000000', 2, false, true);
|
||||||
|
|
||||||
this.updateBestDisplay();
|
this.updateBestDisplay();
|
||||||
}
|
}
|
||||||
|
|
||||||
update(playerY) {
|
update(playerY) {
|
||||||
const cam = this.scene.cameras.main;
|
|
||||||
const width = this.scene.scale.width;
|
|
||||||
this.hudBg.setPosition(cam.scrollX + 10, cam.scrollY + 10);
|
|
||||||
this.hudScore.setPosition(cam.scrollX + 16, cam.scrollY + 16);
|
|
||||||
this.hudBlocks.setPosition(cam.scrollX + 16, cam.scrollY + 42);
|
|
||||||
this.hudCombo.setPosition(cam.scrollX + 16, cam.scrollY + 66);
|
|
||||||
this.hudBest.setPosition(cam.scrollX + width - 80, cam.scrollY + 16);
|
|
||||||
|
|
||||||
const blocks = Math.max(0, Math.floor((this.scene.scale.height - playerY) / 100));
|
const blocks = Math.max(0, Math.floor((this.scene.scale.height - playerY) / 100));
|
||||||
if (blocks > this.blockHeight) {
|
if (blocks > this.blockHeight) {
|
||||||
this.blockHeight = blocks;
|
this.blockHeight = blocks;
|
||||||
this.hudBlocks.setText(`Block: ${this.blockHeight}`);
|
this.hudBlocks.setText(`Block: ${this.blockHeight}`);
|
||||||
|
|
||||||
// Milestone effect every 100 blocks
|
|
||||||
if (this.blockHeight % 100 === 0 && this.blockHeight > 0) {
|
if (this.blockHeight % 100 === 0 && this.blockHeight > 0) {
|
||||||
this.showMilestone(this.blockHeight);
|
this.showMilestone(this.blockHeight);
|
||||||
}
|
}
|
||||||
@@ -63,24 +56,23 @@ export class ScoreManager {
|
|||||||
|
|
||||||
onLand(platformY, platformType) {
|
onLand(platformY, platformType) {
|
||||||
this.combo += 1;
|
this.combo += 1;
|
||||||
this.comboMultiplier = Math.min(1 + (this.combo - 1) * 0.1, 3);
|
this.comboMultiplier = Math.min(1 + (this.combo - 1) * SCORE.comboStep, SCORE.comboMax);
|
||||||
|
|
||||||
let basePoints = 10;
|
let basePoints = SCORE.basePoints;
|
||||||
if (platformType === 'genesis') {
|
if (platformType === 'genesis') {
|
||||||
this.genesisActive = true;
|
this.genesisActive = true;
|
||||||
this.genesisJumps = 5;
|
this.genesisJumps = SCORE.genesisJumps;
|
||||||
basePoints = 50;
|
basePoints = SCORE.genesisBonus;
|
||||||
}
|
}
|
||||||
|
|
||||||
let multiplier = this.comboMultiplier;
|
let multiplier = this.comboMultiplier;
|
||||||
if (this.genesisActive) {
|
if (this.genesisActive) {
|
||||||
multiplier *= 2;
|
multiplier *= SCORE.genesisMultiplier;
|
||||||
this.genesisJumps--;
|
this.genesisJumps--;
|
||||||
if (this.genesisJumps <= 0) this.genesisActive = false;
|
if (this.genesisJumps <= 0) this.genesisActive = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
this.score += Math.floor(basePoints * multiplier);
|
this.addPoints(Math.floor(basePoints * multiplier));
|
||||||
this.hudScore.setText(`Gas: ${this.score}`);
|
|
||||||
|
|
||||||
if (this.combo > 1) {
|
if (this.combo > 1) {
|
||||||
this.hudCombo.setText(`Combo x${this.comboMultiplier.toFixed(1)}`);
|
this.hudCombo.setText(`Combo x${this.comboMultiplier.toFixed(1)}`);
|
||||||
@@ -88,6 +80,11 @@ export class ScoreManager {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
addPoints(amount) {
|
||||||
|
this.score += amount;
|
||||||
|
this.hudScore.setText(`Gas: ${this.score}`);
|
||||||
|
}
|
||||||
|
|
||||||
onFall() {
|
onFall() {
|
||||||
this.combo = 0;
|
this.combo = 0;
|
||||||
this.comboMultiplier = 1;
|
this.comboMultiplier = 1;
|
||||||
@@ -98,17 +95,20 @@ export class ScoreManager {
|
|||||||
const { width, height } = this.scene.scale;
|
const { width, height } = this.scene.scale;
|
||||||
const txt = this.scene.add.text(width / 2, height * 0.35, `EPOCH ${blocks} REACHED!`, {
|
const txt = this.scene.add.text(width / 2, height * 0.35, `EPOCH ${blocks} REACHED!`, {
|
||||||
fontFamily: '"Press Start 2P", monospace',
|
fontFamily: '"Press Start 2P", monospace',
|
||||||
fontSize: '18px',
|
fontSize: '20px',
|
||||||
color: '#ffd700',
|
color: '#ffd700',
|
||||||
}).setOrigin(0.5).setScrollFactor(0).setDepth(200);
|
}).setOrigin(0.5).setScrollFactor(0).setDepth(200).setShadow(2, 2, '#7c2d12', 0, false, true);
|
||||||
|
|
||||||
this.scene.tweens.add({
|
this.scene.tweens.add({
|
||||||
targets: txt,
|
targets: txt,
|
||||||
scale: 1.4,
|
scale: 1.5,
|
||||||
alpha: 0,
|
alpha: 0,
|
||||||
duration: 1500,
|
duration: 1800,
|
||||||
|
ease: 'Quad.easeOut',
|
||||||
onComplete: () => txt.destroy(),
|
onComplete: () => txt.destroy(),
|
||||||
});
|
});
|
||||||
|
|
||||||
|
if (this.scene.onMilestone) this.scene.onMilestone(blocks);
|
||||||
}
|
}
|
||||||
|
|
||||||
updateBestDisplay() {
|
updateBestDisplay() {
|
||||||
|
|||||||
@@ -17,10 +17,7 @@ export class BootScene extends Scene {
|
|||||||
}
|
}
|
||||||
|
|
||||||
create() {
|
create() {
|
||||||
// Generate procedural textures
|
|
||||||
this.createBackgrounds();
|
this.createBackgrounds();
|
||||||
this.createParticles();
|
|
||||||
|
|
||||||
this.scene.start('MenuScene');
|
this.scene.start('MenuScene');
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -115,8 +112,4 @@ export class BootScene extends Scene {
|
|||||||
springGfx.strokePath();
|
springGfx.strokePath();
|
||||||
springGfx.generateTexture('spring', 20, 32);
|
springGfx.generateTexture('spring', 20, 32);
|
||||||
}
|
}
|
||||||
|
|
||||||
createParticles() {
|
|
||||||
// Pre-create particle emitters config if needed
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -64,9 +64,12 @@ export class GameOverScene extends Scene {
|
|||||||
this.scene.start('MenuScene');
|
this.scene.start('MenuScene');
|
||||||
});
|
});
|
||||||
|
|
||||||
const submitBtn = this.createButton(width / 2, height * 0.92, 'SUBMIT TO CHAIN', () => {});
|
this.add.text(width / 2, height * 0.92, 'ON-CHAIN SUBMIT — COMING SOON', {
|
||||||
submitBtn.bg.setAlpha(0.4);
|
fontFamily: '"Press Start 2P", monospace',
|
||||||
submitBtn.label.setAlpha(0.4);
|
fontSize: '9px',
|
||||||
|
color: '#666',
|
||||||
|
align: 'center',
|
||||||
|
}).setOrigin(0.5);
|
||||||
}
|
}
|
||||||
|
|
||||||
createButton(x, y, text, callback) {
|
createButton(x, y, text, callback) {
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ import { Player } from '../entities/Player.js';
|
|||||||
import { Platform } from '../entities/Platform.js';
|
import { Platform } from '../entities/Platform.js';
|
||||||
import { PlatformManager } from '../managers/PlatformManager.js';
|
import { PlatformManager } from '../managers/PlatformManager.js';
|
||||||
import { ScoreManager } from '../managers/ScoreManager.js';
|
import { ScoreManager } from '../managers/ScoreManager.js';
|
||||||
import { GAME_WIDTH, GAME_HEIGHT, GRAVITY } from '../config/game.config.js';
|
import { GAME_WIDTH, GAME_HEIGHT, SCORE, PHYSICS } from '../config/game.config.js';
|
||||||
|
|
||||||
export class GameScene extends Scene {
|
export class GameScene extends Scene {
|
||||||
constructor() {
|
constructor() {
|
||||||
@@ -11,70 +11,46 @@ export class GameScene extends Scene {
|
|||||||
}
|
}
|
||||||
|
|
||||||
create() {
|
create() {
|
||||||
this.physics.world.gravity.y = GRAVITY;
|
|
||||||
|
|
||||||
// Background
|
|
||||||
this.bg = this.add.tileSprite(GAME_WIDTH / 2, GAME_HEIGHT / 2, GAME_WIDTH, GAME_HEIGHT, 'gridBg')
|
this.bg = this.add.tileSprite(GAME_WIDTH / 2, GAME_HEIGHT / 2, GAME_WIDTH, GAME_HEIGHT, 'gridBg')
|
||||||
.setScrollFactor(0)
|
.setScrollFactor(0)
|
||||||
.setDepth(-10);
|
.setDepth(-10);
|
||||||
|
|
||||||
// Gwei particles
|
|
||||||
this.createGweiParticles();
|
this.createGweiParticles();
|
||||||
|
|
||||||
// Input
|
|
||||||
this.cursors = this.input.keyboard.createCursorKeys();
|
this.cursors = this.input.keyboard.createCursorKeys();
|
||||||
this.wasd = this.input.keyboard.addKeys({
|
this.wasd = this.input.keyboard.addKeys({ up: 'W', down: 'S', left: 'A', right: 'D' });
|
||||||
up: 'W', down: 'S', left: 'A', right: 'D',
|
|
||||||
});
|
|
||||||
this.touchLeft = false;
|
this.touchLeft = false;
|
||||||
this.touchRight = false;
|
this.touchRight = false;
|
||||||
this.setupTouchControls();
|
this.setupTouchControls();
|
||||||
|
|
||||||
// Start platform
|
|
||||||
const startY = GAME_HEIGHT - 80;
|
const startY = GAME_HEIGHT - 80;
|
||||||
this.startPlatform = new Platform(this, GAME_WIDTH / 2, startY, 'stable');
|
this.startPlatform = new Platform(this, GAME_WIDTH / 2, startY, 'stable');
|
||||||
this.startPlatform.setTint(0xa78bfa);
|
this.startPlatform.setTint(0xa78bfa);
|
||||||
this.startPlatform.setAlpha(1);
|
|
||||||
|
|
||||||
// Player
|
|
||||||
this.player = new Player(this, GAME_WIDTH / 2, startY - 140);
|
this.player = new Player(this, GAME_WIDTH / 2, startY - 140);
|
||||||
this.lastJumpY = this.player.y;
|
this.lastJumpY = this.player.y;
|
||||||
|
|
||||||
// Player shadow/glow
|
|
||||||
this.playerShadow = this.add.graphics();
|
this.playerShadow = this.add.graphics();
|
||||||
this.playerShadow.setDepth(-4);
|
this.playerShadow.setDepth(-4);
|
||||||
|
|
||||||
// Trail container for fast movement
|
|
||||||
this.trailPoints = [];
|
this.trailPoints = [];
|
||||||
|
this.trailGraphics = this.add.graphics().setDepth(-3);
|
||||||
|
|
||||||
// Managers
|
|
||||||
this.platformManager = new PlatformManager(this);
|
this.platformManager = new PlatformManager(this);
|
||||||
this.scoreManager = new ScoreManager(this);
|
this.scoreManager = new ScoreManager(this);
|
||||||
|
|
||||||
// Pre-spawn platforms
|
|
||||||
for (let i = 0; i < 10; i++) {
|
for (let i = 0; i < 10; i++) {
|
||||||
this.platformManager.spawnPlatform(0);
|
this.platformManager.spawnPlatform(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Collisions
|
|
||||||
this.physics.add.collider(this.player, this.startPlatform, this.handlePlatformCollision, this.platformCollisionFilter, this);
|
this.physics.add.collider(this.player, this.startPlatform, this.handlePlatformCollision, this.platformCollisionFilter, this);
|
||||||
this.physics.add.collider(this.player, this.platformManager.getPlatforms(), this.handlePlatformCollision, this.platformCollisionFilter, this);
|
this.physics.add.collider(this.player, this.platformManager.getPlatforms(), this.handlePlatformCollision, this.platformCollisionFilter, this);
|
||||||
this.physics.add.overlap(this.player, this.platformManager.getPowerups(), this.handlePowerup, null, this);
|
this.physics.add.overlap(this.player, this.platformManager.getPowerups(), this.handlePowerup, null, this);
|
||||||
this.physics.add.overlap(this.player, this.platformManager.getEnemies(), this.handleEnemy, null, this);
|
this.physics.add.overlap(this.player, this.platformManager.getEnemies(), this.handleEnemy, null, this);
|
||||||
|
|
||||||
// Camera
|
|
||||||
this.cameras.main.setBounds(0, -999999, GAME_WIDTH, 999999 + GAME_HEIGHT);
|
this.cameras.main.setBounds(0, -999999, GAME_WIDTH, 999999 + GAME_HEIGHT);
|
||||||
this.cameras.main.startFollow(this.player, true, 0, 0.05, 0, 180);
|
this.cameras.main.startFollow(this.player, true, 0, 0.05, 0, 180);
|
||||||
|
|
||||||
// Debug hitbox renderer
|
|
||||||
this.debugGraphics = this.add.graphics().setDepth(1000);
|
|
||||||
|
|
||||||
// Death vignette overlay
|
|
||||||
this.deathOverlay = this.add.graphics();
|
|
||||||
this.deathOverlay.setScrollFactor(0);
|
|
||||||
this.deathOverlay.setDepth(100);
|
|
||||||
this.deathOverlay.setAlpha(0);
|
|
||||||
|
|
||||||
this.isGameOver = false;
|
this.isGameOver = false;
|
||||||
this.difficultyLevel = 0;
|
this.difficultyLevel = 0;
|
||||||
this.minScrollY = this.cameras.main.scrollY;
|
this.minScrollY = this.cameras.main.scrollY;
|
||||||
@@ -85,44 +61,33 @@ export class GameScene extends Scene {
|
|||||||
|
|
||||||
this.player.update(this.cursors, this.wasd, this.touchLeft, this.touchRight, time, delta);
|
this.player.update(this.cursors, this.wasd, this.touchLeft, this.touchRight, time, delta);
|
||||||
|
|
||||||
// Parallax background
|
|
||||||
this.bg.tilePositionY = this.cameras.main.scrollY * 0.3;
|
this.bg.tilePositionY = this.cameras.main.scrollY * 0.3;
|
||||||
|
|
||||||
// Kill line rises with camera but never falls back down
|
|
||||||
this.minScrollY = Math.min(this.minScrollY, this.cameras.main.scrollY);
|
this.minScrollY = Math.min(this.minScrollY, this.cameras.main.scrollY);
|
||||||
const killLine = this.minScrollY + GAME_HEIGHT;
|
const killLine = this.minScrollY + GAME_HEIGHT;
|
||||||
|
|
||||||
// Update difficulty
|
|
||||||
const height = Math.max(0, GAME_HEIGHT - this.player.y);
|
const height = Math.max(0, GAME_HEIGHT - this.player.y);
|
||||||
this.difficultyLevel = height;
|
this.difficultyLevel = height;
|
||||||
this.platformManager.update(this.difficultyLevel, killLine);
|
this.platformManager.update(this.difficultyLevel, killLine);
|
||||||
this.scoreManager.update(this.player.y);
|
this.scoreManager.update(this.player.y);
|
||||||
|
|
||||||
// Check death — same fixed kill line as platform cleanup
|
|
||||||
if (this.player.body.bottom > killLine) {
|
if (this.player.body.bottom > killLine) {
|
||||||
this.gameOver();
|
this.gameOver();
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Reset combo if falling too far without landing
|
if (this.player.body.velocity.y > 0 && this.player.y > this.lastJumpY + SCORE.fallResetDistance) {
|
||||||
if (this.player.body.velocity.y > 0 && this.player.y > this.lastJumpY + 300) {
|
|
||||||
this.scoreManager.onFall();
|
this.scoreManager.onFall();
|
||||||
this.lastJumpY = this.player.y;
|
this.lastJumpY = this.player.y;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update player shadow
|
|
||||||
this.updatePlayerShadow();
|
this.updatePlayerShadow();
|
||||||
|
|
||||||
// Update trail for fast movement
|
|
||||||
this.updateTrail();
|
this.updateTrail();
|
||||||
|
|
||||||
// Debug disabled
|
|
||||||
// this.drawDebug(killLine);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
updatePlayerShadow() {
|
updatePlayerShadow() {
|
||||||
this.playerShadow.clear();
|
this.playerShadow.clear();
|
||||||
if (!this.player.active) return;
|
if (!this.player.active) return;
|
||||||
|
|
||||||
const alpha = Math.max(0.05, 0.25 - (Math.abs(this.player.body.velocity.y) / 1200));
|
const alpha = Math.max(0.05, 0.25 - (Math.abs(this.player.body.velocity.y) / 1200));
|
||||||
this.playerShadow.fillStyle(0x000000, alpha);
|
this.playerShadow.fillStyle(0x000000, alpha);
|
||||||
this.playerShadow.fillCircle(this.player.x, this.player.y + 42, 16);
|
this.playerShadow.fillCircle(this.player.x, this.player.y + 42, 16);
|
||||||
@@ -146,11 +111,7 @@ export class GameScene extends Scene {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Redraw trail
|
|
||||||
this.trailGraphics = this.trailGraphics || this.add.graphics();
|
|
||||||
this.trailGraphics.clear();
|
this.trailGraphics.clear();
|
||||||
this.trailGraphics.setDepth(-3);
|
|
||||||
|
|
||||||
for (const point of this.trailPoints) {
|
for (const point of this.trailPoints) {
|
||||||
const color = this.player.state === 'rocket' ? 0xff4444 : 0x44aaff;
|
const color = this.player.state === 'rocket' ? 0xff4444 : 0x44aaff;
|
||||||
this.trailGraphics.fillStyle(color, point.alpha);
|
this.trailGraphics.fillStyle(color, point.alpha);
|
||||||
@@ -161,7 +122,6 @@ export class GameScene extends Scene {
|
|||||||
platformCollisionFilter(player, platform) {
|
platformCollisionFilter(player, platform) {
|
||||||
if (!platform || !platform.body) return false;
|
if (!platform || !platform.body) return false;
|
||||||
if (typeof platform.isBroken === 'function' && platform.isBroken()) return false;
|
if (typeof platform.isBroken === 'function' && platform.isBroken()) return false;
|
||||||
// Only bounce when falling and player is above platform center
|
|
||||||
return player.body.velocity.y > 0 && player.y < platform.y + 8;
|
return player.body.velocity.y > 0 && player.y < platform.y + 8;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -181,11 +141,13 @@ export class GameScene extends Scene {
|
|||||||
if (powerup && typeof powerup.onPlayerTouch === 'function') {
|
if (powerup && typeof powerup.onPlayerTouch === 'function') {
|
||||||
const px = powerup.x;
|
const px = powerup.x;
|
||||||
const py = powerup.y;
|
const py = powerup.y;
|
||||||
powerup.onPlayerTouch(player);
|
const consumed = powerup.onPlayerTouch(player);
|
||||||
|
if (consumed) {
|
||||||
this.createPowerupParticles(px, py);
|
this.createPowerupParticles(px, py);
|
||||||
this.flashScreen();
|
this.flashScreen();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
handleEnemy(player, enemy) {
|
handleEnemy(player, enemy) {
|
||||||
if (this.isGameOver) return;
|
if (this.isGameOver) return;
|
||||||
@@ -200,14 +162,11 @@ export class GameScene extends Scene {
|
|||||||
enemy.destroy();
|
enemy.destroy();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
// Mario-style stomp: falling onto enemy kills it
|
if (player.body.velocity.y > 0 && player.body.bottom <= enemy.body.top + PHYSICS.stompTolerance) {
|
||||||
const stompTolerance = 12;
|
|
||||||
if (player.body.velocity.y > 0 && player.body.bottom <= enemy.body.top + stompTolerance) {
|
|
||||||
this.createExplosion(enemy.x, enemy.y);
|
this.createExplosion(enemy.x, enemy.y);
|
||||||
enemy.destroy();
|
enemy.destroy();
|
||||||
player.jump();
|
player.jump();
|
||||||
this.scoreManager.score += 25;
|
this.scoreManager.addPoints(SCORE.stompBonus);
|
||||||
this.scoreManager.hudScore.setText(`Gas: ${this.scoreManager.score}`);
|
|
||||||
this.createJumpParticles(player.x, player.y + player.displayHeight / 2 + 3);
|
this.createJumpParticles(player.x, player.y + player.displayHeight / 2 + 3);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -219,7 +178,6 @@ export class GameScene extends Scene {
|
|||||||
this.isGameOver = true;
|
this.isGameOver = true;
|
||||||
this.player.die();
|
this.player.die();
|
||||||
this.cameras.main.shake(300, 0.012);
|
this.cameras.main.shake(300, 0.012);
|
||||||
// Small explosion at impact point instead of full-screen vignette
|
|
||||||
const bx = ex ?? this.player.x;
|
const bx = ex ?? this.player.x;
|
||||||
const by = ey ?? this.player.y;
|
const by = ey ?? this.player.y;
|
||||||
this.createExplosion(bx, by);
|
this.createExplosion(bx, by);
|
||||||
@@ -233,76 +191,6 @@ export class GameScene extends Scene {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
drawDebug(camBottom) {
|
|
||||||
this.debugGraphics.clear();
|
|
||||||
|
|
||||||
// Kill line — magenta
|
|
||||||
this.debugGraphics.lineStyle(2, 0xff00ff, 0.7);
|
|
||||||
this.debugGraphics.lineBetween(0, camBottom, GAME_WIDTH, camBottom);
|
|
||||||
|
|
||||||
// Player — red
|
|
||||||
if (this.player.active && this.player.body) {
|
|
||||||
this.debugGraphics.lineStyle(2, 0xff0000, 1);
|
|
||||||
this.debugGraphics.strokeRectShape(this.player.body);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Start platform — green
|
|
||||||
if (this.startPlatform && this.startPlatform.body) {
|
|
||||||
this.debugGraphics.lineStyle(2, 0x00ff00, 1);
|
|
||||||
this.debugGraphics.strokeRectShape(this.startPlatform.body);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Platforms — green
|
|
||||||
this.platformManager.getPlatforms().children.iterate((p) => {
|
|
||||||
if (p && p.body) {
|
|
||||||
this.debugGraphics.lineStyle(2, 0x00ff00, 1);
|
|
||||||
this.debugGraphics.strokeRectShape(p.body);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// Enemies — blue
|
|
||||||
this.platformManager.getEnemies().children.iterate((e) => {
|
|
||||||
if (e && e.body) {
|
|
||||||
this.debugGraphics.lineStyle(2, 0x0000ff, 1);
|
|
||||||
this.debugGraphics.strokeRectShape(e.body);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// Powerups — yellow
|
|
||||||
this.platformManager.getPowerups().children.iterate((pu) => {
|
|
||||||
if (pu && pu.body) {
|
|
||||||
this.debugGraphics.lineStyle(2, 0xffff00, 1);
|
|
||||||
this.debugGraphics.strokeRectShape(pu.body);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
showDeathVignette() {
|
|
||||||
const cx = GAME_WIDTH / 2;
|
|
||||||
const cy = GAME_HEIGHT / 2;
|
|
||||||
const maxR = Math.max(GAME_WIDTH, GAME_HEIGHT);
|
|
||||||
|
|
||||||
this.deathOverlay.clear();
|
|
||||||
// Draw a radial gradient-like vignette using fewer circles
|
|
||||||
const steps = 5;
|
|
||||||
const band = (maxR * 0.8) / steps;
|
|
||||||
for (let i = steps; i >= 0; i--) {
|
|
||||||
const t = i / steps;
|
|
||||||
const r = 50 + t * (maxR * 0.8);
|
|
||||||
const alpha = 0.05 + (1 - t) * 0.55;
|
|
||||||
this.deathOverlay.lineStyle(band, 0x7f0000, alpha);
|
|
||||||
this.deathOverlay.strokeCircle(cx, cy, r);
|
|
||||||
}
|
|
||||||
this.deathOverlay.setAlpha(0);
|
|
||||||
|
|
||||||
this.tweens.add({
|
|
||||||
targets: this.deathOverlay,
|
|
||||||
alpha: 1,
|
|
||||||
duration: 400,
|
|
||||||
ease: 'Quad.easeOut',
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
flashScreen() {
|
flashScreen() {
|
||||||
const flash = this.add.rectangle(GAME_WIDTH / 2, GAME_HEIGHT / 2, GAME_WIDTH, GAME_HEIGHT, 0xffffff, 0.3);
|
const flash = this.add.rectangle(GAME_WIDTH / 2, GAME_HEIGHT / 2, GAME_WIDTH, GAME_HEIGHT, 0xffffff, 0.3);
|
||||||
flash.setScrollFactor(0);
|
flash.setScrollFactor(0);
|
||||||
@@ -316,7 +204,7 @@ export class GameScene extends Scene {
|
|||||||
}
|
}
|
||||||
|
|
||||||
setupTouchControls() {
|
setupTouchControls() {
|
||||||
const { width, height } = this.scale;
|
const { width } = this.scale;
|
||||||
this.input.on('pointerdown', (pointer) => {
|
this.input.on('pointerdown', (pointer) => {
|
||||||
if (pointer.x < width / 2) this.touchLeft = true;
|
if (pointer.x < width / 2) this.touchLeft = true;
|
||||||
else this.touchRight = true;
|
else this.touchRight = true;
|
||||||
@@ -339,7 +227,7 @@ export class GameScene extends Scene {
|
|||||||
Phaser.Math.Between(0, GAME_WIDTH),
|
Phaser.Math.Between(0, GAME_WIDTH),
|
||||||
Phaser.Math.Between(0, GAME_HEIGHT),
|
Phaser.Math.Between(0, GAME_HEIGHT),
|
||||||
'gwei'
|
'gwei'
|
||||||
).setAlpha(0.5);
|
).setAlpha(0.5).setScrollFactor(0.3).setDepth(-8);
|
||||||
const scene = this;
|
const scene = this;
|
||||||
this.tweens.add({
|
this.tweens.add({
|
||||||
targets: p,
|
targets: p,
|
||||||
@@ -349,7 +237,7 @@ export class GameScene extends Scene {
|
|||||||
repeat: -1,
|
repeat: -1,
|
||||||
delay: Phaser.Math.Between(0, 4000),
|
delay: Phaser.Math.Between(0, 4000),
|
||||||
onRepeat: function() {
|
onRepeat: function() {
|
||||||
p.y = scene.cameras.main.scrollY + GAME_HEIGHT + 20;
|
p.y = Phaser.Math.Between(0, GAME_HEIGHT);
|
||||||
p.x = Phaser.Math.Between(0, GAME_WIDTH);
|
p.x = Phaser.Math.Between(0, GAME_WIDTH);
|
||||||
p.setAlpha(0.5);
|
p.setAlpha(0.5);
|
||||||
},
|
},
|
||||||
|
|||||||
Reference in New Issue
Block a user