Sprint 2: gameplay — achievements, score popups, new enemy, polish
- AchievementsManager with 10 unlockables and toast popups (Genesis Block, Chain Reaction x3, Bug Hunter, First Flight, Liftoff, Power Trip, Survivor 500, Skyscraper 1000, Speedrun 100/60s, Gas Baron 50k) - Score popups: +N text floats above player on every landing (gold and larger for genesis platforms) - Powerup duration bar in HUD bottom, color-coded per power-up, uses scaleX for smooth depletion animation - New enemy: Failed Tx, falls from above with sine drift, unlocks at difficulty > 800, can be stomped, tinted red - Dynamic background: dark cosmic overlay alpha scales with height (max 0.5 at very high altitudes) - Achievement hooks integrated into ScoreManager, GameScene - Combo no longer resets if combo was already 0 (was triggering log spam)
This commit is contained in:
@@ -8,29 +8,46 @@ export class Enemy extends Physics.Arcade.Sprite {
|
|||||||
scene.physics.add.existing(this);
|
scene.physics.add.existing(this);
|
||||||
|
|
||||||
this.enemyType = type;
|
this.enemyType = type;
|
||||||
this.setScale(0.7);
|
|
||||||
this.body.allowGravity = false;
|
this.body.allowGravity = false;
|
||||||
this.body.setSize(90, 80);
|
|
||||||
this.body.setOffset(35, 30);
|
|
||||||
|
|
||||||
if (type === 'bug') {
|
if (type === 'bug') {
|
||||||
|
this.setScale(0.7);
|
||||||
|
this.body.setSize(this.width * 0.7, this.height * 0.7);
|
||||||
|
this.body.setOffset(this.width * 0.15, this.height * 0.15);
|
||||||
this.speed = Phaser.Math.Between(60, 140) * (Math.random() < 0.5 ? 1 : -1);
|
this.speed = Phaser.Math.Between(60, 140) * (Math.random() < 0.5 ? 1 : -1);
|
||||||
this.startX = x;
|
this.startX = x;
|
||||||
this.patrolRange = Phaser.Math.Between(80, 200);
|
this.patrolRange = Phaser.Math.Between(80, 200);
|
||||||
|
} else if (type === 'failed_tx') {
|
||||||
|
this.setScale(0.55);
|
||||||
|
this.setTint(0xef4444);
|
||||||
|
this.body.setSize(this.width * 0.6, this.height * 0.6);
|
||||||
|
this.body.setOffset(this.width * 0.2, this.height * 0.2);
|
||||||
|
this.fallSpeed = Phaser.Math.Between(80, 140);
|
||||||
|
this.driftAmplitude = Phaser.Math.Between(20, 60);
|
||||||
|
this.driftFreq = Phaser.Math.FloatBetween(0.001, 0.003);
|
||||||
|
this.spawnTime = scene.time.now;
|
||||||
|
this.spawnX = x;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
preUpdate(time, delta) {
|
preUpdate(time, delta) {
|
||||||
super.preUpdate(time, delta);
|
super.preUpdate(time, delta);
|
||||||
|
|
||||||
if (this.enemyType === 'bug') {
|
if (this.enemyType === 'bug') {
|
||||||
this.x += this.speed * (delta / 1000);
|
this.x += this.speed * (delta / 1000);
|
||||||
if (Math.abs(this.x - this.startX) > this.patrolRange) {
|
if (Math.abs(this.x - this.startX) > this.patrolRange) {
|
||||||
this.speed *= -1;
|
this.speed *= -1;
|
||||||
this.setFlipX(this.speed < 0);
|
this.setFlipX(this.speed < 0);
|
||||||
}
|
}
|
||||||
// Wrap
|
|
||||||
if (this.x < -60) this.x = GAME_WIDTH + 60;
|
if (this.x < -60) this.x = GAME_WIDTH + 60;
|
||||||
if (this.x > GAME_WIDTH + 60) this.x = -60;
|
if (this.x > GAME_WIDTH + 60) this.x = -60;
|
||||||
|
} else if (this.enemyType === 'failed_tx') {
|
||||||
|
const dt = delta / 1000;
|
||||||
|
this.y += this.fallSpeed * dt;
|
||||||
|
const t = time - this.spawnTime;
|
||||||
|
this.x = this.spawnX + Math.sin(t * this.driftFreq) * this.driftAmplitude;
|
||||||
|
this.x = Phaser.Math.Clamp(this.x, 30, GAME_WIDTH - 30);
|
||||||
|
this.setAngle(Math.sin(t * 0.005) * 15);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
149
src/managers/AchievementsManager.js
Normal file
149
src/managers/AchievementsManager.js
Normal file
@@ -0,0 +1,149 @@
|
|||||||
|
import { sound } from './SoundManager.js';
|
||||||
|
|
||||||
|
const STORAGE_KEY = 'naddie_achievements_v1';
|
||||||
|
|
||||||
|
const DEFS = [
|
||||||
|
{ id: 'genesis_block', title: 'Genesis Block', desc: 'Land on a gold platform' },
|
||||||
|
{ id: 'chain_reaction', title: 'Chain Reaction', desc: 'Reach combo x3.0' },
|
||||||
|
{ id: 'bug_hunter', title: 'Bug Hunter', desc: 'Stomp 5 enemies in one run' },
|
||||||
|
{ id: 'first_flight', title: 'First Flight', desc: 'Use the propeller' },
|
||||||
|
{ id: 'liftoff', title: 'Liftoff', desc: 'Use the rocket' },
|
||||||
|
{ id: 'power_trip', title: 'Power Trip', desc: 'Use all 3 power-ups in one run' },
|
||||||
|
{ id: 'survivor', title: 'Survivor', desc: 'Reach block height 500' },
|
||||||
|
{ id: 'skyscraper', title: 'Skyscraper', desc: 'Reach block height 1000' },
|
||||||
|
{ id: 'speedrun', title: 'Speedrun', desc: 'Reach block 100 in under 60s' },
|
||||||
|
{ id: 'gas_baron', title: 'Gas Baron', desc: 'Score 50,000 in one run' },
|
||||||
|
];
|
||||||
|
|
||||||
|
export class AchievementsManager {
|
||||||
|
constructor(scene) {
|
||||||
|
this.scene = scene;
|
||||||
|
this.unlocked = this._load();
|
||||||
|
this.popupQueue = [];
|
||||||
|
this.popupActive = false;
|
||||||
|
|
||||||
|
// Per-run counters
|
||||||
|
this.bugsStomped = 0;
|
||||||
|
this.powerupsUsed = new Set();
|
||||||
|
this.startTime = Date.now();
|
||||||
|
}
|
||||||
|
|
||||||
|
static getAll() {
|
||||||
|
return DEFS;
|
||||||
|
}
|
||||||
|
|
||||||
|
_load() {
|
||||||
|
try {
|
||||||
|
const raw = localStorage.getItem(STORAGE_KEY);
|
||||||
|
return raw ? JSON.parse(raw) : {};
|
||||||
|
} catch (_) {
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_save() {
|
||||||
|
try {
|
||||||
|
localStorage.setItem(STORAGE_KEY, JSON.stringify(this.unlocked));
|
||||||
|
} catch (_) {}
|
||||||
|
}
|
||||||
|
|
||||||
|
isUnlocked(id) {
|
||||||
|
return !!this.unlocked[id];
|
||||||
|
}
|
||||||
|
|
||||||
|
unlock(id) {
|
||||||
|
if (this.unlocked[id]) return;
|
||||||
|
const def = DEFS.find((d) => d.id === id);
|
||||||
|
if (!def) return;
|
||||||
|
this.unlocked[id] = Date.now();
|
||||||
|
this._save();
|
||||||
|
this.popupQueue.push(def);
|
||||||
|
this._processQueue();
|
||||||
|
sound.powerup();
|
||||||
|
}
|
||||||
|
|
||||||
|
_processQueue() {
|
||||||
|
if (this.popupActive || this.popupQueue.length === 0) return;
|
||||||
|
this.popupActive = true;
|
||||||
|
const def = this.popupQueue.shift();
|
||||||
|
this._showPopup(def, () => {
|
||||||
|
this.popupActive = false;
|
||||||
|
this._processQueue();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
_showPopup(def, onDone) {
|
||||||
|
const { width } = this.scene.scale;
|
||||||
|
const y = 110;
|
||||||
|
const panel = this.scene.add.rectangle(width / 2, y, 320, 70, 0x1a0533, 0.92)
|
||||||
|
.setStrokeStyle(2, 0xffd700)
|
||||||
|
.setScrollFactor(0)
|
||||||
|
.setDepth(800);
|
||||||
|
const title = this.scene.add.text(width / 2, y - 14, `ACHIEVEMENT: ${def.title}`, {
|
||||||
|
fontFamily: '"Press Start 2P", monospace',
|
||||||
|
fontSize: '10px',
|
||||||
|
color: '#ffd700',
|
||||||
|
}).setOrigin(0.5).setScrollFactor(0).setDepth(801);
|
||||||
|
const desc = this.scene.add.text(width / 2, y + 10, def.desc, {
|
||||||
|
fontFamily: '"Press Start 2P", monospace',
|
||||||
|
fontSize: '8px',
|
||||||
|
color: '#d8b4fe',
|
||||||
|
}).setOrigin(0.5).setScrollFactor(0).setDepth(801);
|
||||||
|
|
||||||
|
panel.setAlpha(0);
|
||||||
|
title.setAlpha(0);
|
||||||
|
desc.setAlpha(0);
|
||||||
|
|
||||||
|
this.scene.tweens.add({
|
||||||
|
targets: [panel, title, desc],
|
||||||
|
alpha: 1,
|
||||||
|
y: `+=10`,
|
||||||
|
duration: 250,
|
||||||
|
ease: 'Quad.easeOut',
|
||||||
|
});
|
||||||
|
|
||||||
|
this.scene.time.delayedCall(2500, () => {
|
||||||
|
this.scene.tweens.add({
|
||||||
|
targets: [panel, title, desc],
|
||||||
|
alpha: 0,
|
||||||
|
duration: 350,
|
||||||
|
onComplete: () => {
|
||||||
|
panel.destroy(); title.destroy(); desc.destroy();
|
||||||
|
onDone();
|
||||||
|
},
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- Game event hooks ---
|
||||||
|
|
||||||
|
onPlatformLand(type, combo) {
|
||||||
|
if (type === 'genesis') this.unlock('genesis_block');
|
||||||
|
if (combo >= 3) this.unlock('chain_reaction');
|
||||||
|
}
|
||||||
|
|
||||||
|
onEnemyStomp() {
|
||||||
|
this.bugsStomped += 1;
|
||||||
|
if (this.bugsStomped >= 5) this.unlock('bug_hunter');
|
||||||
|
}
|
||||||
|
|
||||||
|
onPowerup(name) {
|
||||||
|
this.powerupsUsed.add(name);
|
||||||
|
if (name === 'propeller') this.unlock('first_flight');
|
||||||
|
if (name === 'rocket') this.unlock('liftoff');
|
||||||
|
if (this.powerupsUsed.size >= 3) this.unlock('power_trip');
|
||||||
|
}
|
||||||
|
|
||||||
|
onBlockHeight(blocks) {
|
||||||
|
if (blocks >= 500) this.unlock('survivor');
|
||||||
|
if (blocks >= 1000) this.unlock('skyscraper');
|
||||||
|
if (blocks >= 100) {
|
||||||
|
const elapsed = (Date.now() - this.startTime) / 1000;
|
||||||
|
if (elapsed <= 60) this.unlock('speedrun');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
onScore(score) {
|
||||||
|
if (score >= 50000) this.unlock('gas_baron');
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -92,8 +92,12 @@ export class PlatformManager {
|
|||||||
Math.floor(difficultyLevel / 1000) * DIFFICULTY.enemyIncreasePer1000,
|
Math.floor(difficultyLevel / 1000) * DIFFICULTY.enemyIncreasePer1000,
|
||||||
DIFFICULTY.maxEnemyRate
|
DIFFICULTY.maxEnemyRate
|
||||||
);
|
);
|
||||||
const bugRate = Math.min(ENEMY_RATES.bug + enemyBonus, DIFFICULTY.maxEnemyRate);
|
const totalRate = Math.min(ENEMY_RATES.bug + enemyBonus, DIFFICULTY.maxEnemyRate);
|
||||||
if (Math.random() >= bugRate) return;
|
if (Math.random() >= totalRate) return;
|
||||||
|
|
||||||
|
// Failed-Tx type unlocks at higher difficulty
|
||||||
|
const failedTxAllowed = difficultyLevel > 800;
|
||||||
|
const type = failedTxAllowed && Math.random() < 0.30 ? 'failed_tx' : 'bug';
|
||||||
|
|
||||||
const offset = Phaser.Math.Between(-60, 60);
|
const offset = Phaser.Math.Between(-60, 60);
|
||||||
const ex = Phaser.Math.Clamp(platformX + offset, 50, GAME_WIDTH - 50);
|
const ex = Phaser.Math.Clamp(platformX + offset, 50, GAME_WIDTH - 50);
|
||||||
@@ -108,7 +112,7 @@ export class PlatformManager {
|
|||||||
});
|
});
|
||||||
if (tooClose) return;
|
if (tooClose) return;
|
||||||
|
|
||||||
this.enemies.add(new Enemy(this.scene, ex, ey, 'bug'));
|
this.enemies.add(new Enemy(this.scene, ex, ey, type));
|
||||||
}
|
}
|
||||||
|
|
||||||
getPlatforms() {
|
getPlatforms() {
|
||||||
|
|||||||
@@ -39,6 +39,18 @@ export class ScoreManager {
|
|||||||
color: '#aaa',
|
color: '#aaa',
|
||||||
}).setOrigin(1, 0).setScrollFactor(0).setDepth(200).setShadow(1, 1, '#000000', 2, false, true);
|
}).setOrigin(1, 0).setScrollFactor(0).setDepth(200).setShadow(1, 1, '#000000', 2, false, true);
|
||||||
|
|
||||||
|
// Powerup duration bar (hidden until active)
|
||||||
|
this.powerupBarBg = scene.add.rectangle(scene.scale.width / 2, scene.scale.height - 24, 200, 12, 0x000000, 0.6)
|
||||||
|
.setStrokeStyle(2, 0xa855f7)
|
||||||
|
.setScrollFactor(0).setDepth(200).setVisible(false);
|
||||||
|
this.powerupBarFill = scene.add.rectangle(scene.scale.width / 2 - 99, scene.scale.height - 24, 196, 8, 0xa855f7)
|
||||||
|
.setOrigin(0, 0.5).setScrollFactor(0).setDepth(201).setVisible(false);
|
||||||
|
this.powerupBarLabel = scene.add.text(scene.scale.width / 2, scene.scale.height - 40, '', {
|
||||||
|
fontFamily: '"Press Start 2P", monospace',
|
||||||
|
fontSize: '9px',
|
||||||
|
color: '#d8b4fe',
|
||||||
|
}).setOrigin(0.5).setScrollFactor(0).setDepth(201).setVisible(false);
|
||||||
|
|
||||||
this.updateBestDisplay();
|
this.updateBestDisplay();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -47,11 +59,51 @@ export class ScoreManager {
|
|||||||
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}`);
|
||||||
|
if (this.scene.achievements) this.scene.achievements.onBlockHeight(this.blockHeight);
|
||||||
|
|
||||||
if (this.blockHeight % 100 === 0 && this.blockHeight > 0) {
|
if (this.blockHeight % 100 === 0 && this.blockHeight > 0) {
|
||||||
this.showMilestone(this.blockHeight);
|
this.showMilestone(this.blockHeight);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this._updatePowerupBar();
|
||||||
|
}
|
||||||
|
|
||||||
|
_updatePowerupBar() {
|
||||||
|
const player = this.scene.player;
|
||||||
|
if (!player) return;
|
||||||
|
|
||||||
|
let active = null;
|
||||||
|
let remaining = 0;
|
||||||
|
let total = 0;
|
||||||
|
let color = 0xa855f7;
|
||||||
|
let label = '';
|
||||||
|
|
||||||
|
if (player.state === 'propeller') {
|
||||||
|
active = 'propeller';
|
||||||
|
remaining = Math.max(0, player.propellerTimer);
|
||||||
|
total = 3500;
|
||||||
|
color = 0x44aaff;
|
||||||
|
label = 'PROPELLER';
|
||||||
|
} else if (player.state === 'rocket') {
|
||||||
|
active = 'rocket';
|
||||||
|
remaining = Math.max(0, player.rocketTimer);
|
||||||
|
total = 3000;
|
||||||
|
color = 0xff4444;
|
||||||
|
label = 'ROCKET';
|
||||||
|
}
|
||||||
|
|
||||||
|
if (active) {
|
||||||
|
const pct = remaining / total;
|
||||||
|
this.powerupBarBg.setVisible(true);
|
||||||
|
this.powerupBarFill.setVisible(true).setFillStyle(color);
|
||||||
|
this.powerupBarFill.scaleX = pct;
|
||||||
|
this.powerupBarLabel.setVisible(true).setText(label);
|
||||||
|
} else {
|
||||||
|
this.powerupBarBg.setVisible(false);
|
||||||
|
this.powerupBarFill.setVisible(false);
|
||||||
|
this.powerupBarLabel.setVisible(false);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
onLand(platformY, platformType) {
|
onLand(platformY, platformType) {
|
||||||
@@ -72,7 +124,13 @@ export class ScoreManager {
|
|||||||
if (this.genesisJumps <= 0) this.genesisActive = false;
|
if (this.genesisJumps <= 0) this.genesisActive = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
this.addPoints(Math.floor(basePoints * multiplier));
|
const gained = Math.floor(basePoints * multiplier);
|
||||||
|
this.addPoints(gained);
|
||||||
|
this._spawnScorePopup(gained, platformType === 'genesis');
|
||||||
|
|
||||||
|
if (this.scene.achievements) {
|
||||||
|
this.scene.achievements.onPlatformLand(platformType, this.comboMultiplier);
|
||||||
|
}
|
||||||
|
|
||||||
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)}`);
|
||||||
@@ -80,16 +138,37 @@ export class ScoreManager {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_spawnScorePopup(amount, big) {
|
||||||
|
const player = this.scene.player;
|
||||||
|
if (!player) return;
|
||||||
|
const txt = this.scene.add.text(player.x, player.y - 30, `+${amount}`, {
|
||||||
|
fontFamily: '"Press Start 2P", monospace',
|
||||||
|
fontSize: big ? '14px' : '10px',
|
||||||
|
color: big ? '#ffd700' : '#22c55e',
|
||||||
|
}).setOrigin(0.5).setDepth(150);
|
||||||
|
this.scene.tweens.add({
|
||||||
|
targets: txt,
|
||||||
|
y: txt.y - 35,
|
||||||
|
alpha: 0,
|
||||||
|
duration: 700,
|
||||||
|
ease: 'Quad.easeOut',
|
||||||
|
onComplete: () => txt.destroy(),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
addPoints(amount) {
|
addPoints(amount) {
|
||||||
this.score += amount;
|
this.score += amount;
|
||||||
this.hudScore.setText(`Gas: ${this.score}`);
|
this.hudScore.setText(`Gas: ${this.score}`);
|
||||||
|
if (this.scene.achievements) this.scene.achievements.onScore(this.score);
|
||||||
}
|
}
|
||||||
|
|
||||||
onFall() {
|
onFall() {
|
||||||
|
if (this.combo > 0) {
|
||||||
this.combo = 0;
|
this.combo = 0;
|
||||||
this.comboMultiplier = 1;
|
this.comboMultiplier = 1;
|
||||||
this.hudCombo.setText('');
|
this.hudCombo.setText('');
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
showMilestone(blocks) {
|
showMilestone(blocks) {
|
||||||
const { width, height } = this.scene.scale;
|
const { width, height } = this.scene.scale;
|
||||||
@@ -133,5 +212,8 @@ export class ScoreManager {
|
|||||||
this.hudBlocks.destroy();
|
this.hudBlocks.destroy();
|
||||||
this.hudCombo.destroy();
|
this.hudCombo.destroy();
|
||||||
this.hudBest.destroy();
|
this.hudBest.destroy();
|
||||||
|
this.powerupBarBg.destroy();
|
||||||
|
this.powerupBarFill.destroy();
|
||||||
|
this.powerupBarLabel.destroy();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,6 +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 { AchievementsManager } from '../managers/AchievementsManager.js';
|
||||||
import { sound } from '../managers/SoundManager.js';
|
import { sound } from '../managers/SoundManager.js';
|
||||||
import { GAME_WIDTH, GAME_HEIGHT, SCORE, PHYSICS } from '../config/game.config.js';
|
import { GAME_WIDTH, GAME_HEIGHT, SCORE, PHYSICS } from '../config/game.config.js';
|
||||||
|
|
||||||
@@ -19,6 +20,10 @@ export class GameScene extends Scene {
|
|||||||
.setScrollFactor(0)
|
.setScrollFactor(0)
|
||||||
.setDepth(-10);
|
.setDepth(-10);
|
||||||
|
|
||||||
|
// Color overlay for height-based tint shift
|
||||||
|
this.bgTint = this.add.rectangle(GAME_WIDTH / 2, GAME_HEIGHT / 2, GAME_WIDTH, GAME_HEIGHT, 0x000033, 0)
|
||||||
|
.setScrollFactor(0).setDepth(-9);
|
||||||
|
|
||||||
this.createGweiParticles();
|
this.createGweiParticles();
|
||||||
|
|
||||||
this.cursors = this.input.keyboard.createCursorKeys();
|
this.cursors = this.input.keyboard.createCursorKeys();
|
||||||
@@ -42,6 +47,7 @@ export class GameScene extends Scene {
|
|||||||
this.trailGraphics = this.add.graphics().setDepth(-3);
|
this.trailGraphics = this.add.graphics().setDepth(-3);
|
||||||
|
|
||||||
this.platformManager = new PlatformManager(this);
|
this.platformManager = new PlatformManager(this);
|
||||||
|
this.achievements = new AchievementsManager(this);
|
||||||
this.scoreManager = new ScoreManager(this);
|
this.scoreManager = new ScoreManager(this);
|
||||||
|
|
||||||
for (let i = 0; i < 10; i++) {
|
for (let i = 0; i < 10; i++) {
|
||||||
@@ -84,6 +90,10 @@ export class GameScene extends Scene {
|
|||||||
|
|
||||||
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;
|
||||||
|
|
||||||
|
// Background darkens as player climbs higher (cosmic feel)
|
||||||
|
const tintAlpha = Math.min(0.5, height / 8000);
|
||||||
|
this.bgTint.fillAlpha = tintAlpha;
|
||||||
this.platformManager.update(this.difficultyLevel, killLine);
|
this.platformManager.update(this.difficultyLevel, killLine);
|
||||||
this.scoreManager.update(this.player.y);
|
this.scoreManager.update(this.player.y);
|
||||||
|
|
||||||
@@ -160,13 +170,18 @@ 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;
|
||||||
const isSpring = powerup.constructor.name === 'Spring';
|
const name = powerup.constructor.name.toLowerCase();
|
||||||
|
const isSpring = name === 'spring';
|
||||||
const consumed = powerup.onPlayerTouch(player);
|
const consumed = powerup.onPlayerTouch(player);
|
||||||
if (consumed) {
|
if (consumed) {
|
||||||
this.createPowerupParticles(px, py);
|
this.createPowerupParticles(px, py);
|
||||||
this.flashScreen();
|
this.flashScreen();
|
||||||
if (isSpring) sound.spring();
|
if (isSpring) sound.spring();
|
||||||
else sound.powerup();
|
else sound.powerup();
|
||||||
|
if (this.achievements) {
|
||||||
|
const map = { spring: 'spring', propellerhat: 'propeller', rocket: 'rocket' };
|
||||||
|
this.achievements.onPowerup(map[name] || name);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -192,6 +207,7 @@ export class GameScene extends Scene {
|
|||||||
player.jump();
|
player.jump();
|
||||||
sound.stomp();
|
sound.stomp();
|
||||||
this.scoreManager.addPoints(SCORE.stompBonus);
|
this.scoreManager.addPoints(SCORE.stompBonus);
|
||||||
|
if (this.achievements) this.achievements.onEnemyStomp();
|
||||||
this.createJumpParticles(player.x, player.y + player.displayHeight / 2 + 3);
|
this.createJumpParticles(player.x, player.y + player.displayHeight / 2 + 3);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user