Selectable backgrounds with live preview in settings
- 6 procedural, vertically-tiling backgrounds generated in BootScene: Grid, Hex Nodes, Starfield, Synthwave, Circuit, Void - config/backgrounds.js registry + utils/background.js helper; selection persisted via storage (KEYS.background) - Menu / Game / GameOver use the selected background texture - Settings overlay gains a BACKGROUND selector: in-panel live preview tile + < name > cycling, updates the menu background live and persists choice - Fix: stepper/arrow buttons now add both bg and label to the modal so the -, +, <, > glyphs render above the panel
This commit is contained in:
19
src/config/backgrounds.js
Normal file
19
src/config/backgrounds.js
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
/**
|
||||||
|
* Selectable backgrounds. Each `texture` is generated procedurally in
|
||||||
|
* BootScene.createBackgroundTextures and used as a tiling tileSprite.
|
||||||
|
* All patterns are designed to tile vertically so the parallax scroll is seamless.
|
||||||
|
*/
|
||||||
|
export const BACKGROUNDS = [
|
||||||
|
{ id: 'grid', name: 'Grid', texture: 'bg_grid' },
|
||||||
|
{ id: 'hex', name: 'Hex Nodes', texture: 'bg_hex' },
|
||||||
|
{ id: 'starfield', name: 'Starfield', texture: 'bg_starfield' },
|
||||||
|
{ id: 'synthwave', name: 'Synthwave', texture: 'bg_synthwave' },
|
||||||
|
{ id: 'circuit', name: 'Circuit', texture: 'bg_circuit' },
|
||||||
|
{ id: 'void', name: 'Void', texture: 'bg_void' },
|
||||||
|
];
|
||||||
|
|
||||||
|
export const DEFAULT_BACKGROUND = 'grid';
|
||||||
|
|
||||||
|
export function getBackground(id) {
|
||||||
|
return BACKGROUNDS.find((b) => b.id === id) || BACKGROUNDS[0];
|
||||||
|
}
|
||||||
@@ -18,10 +18,150 @@ export class BootScene extends Scene {
|
|||||||
|
|
||||||
create() {
|
create() {
|
||||||
this.createBackgrounds();
|
this.createBackgrounds();
|
||||||
|
this.createBackgroundTextures();
|
||||||
this.createParticleTextures();
|
this.createParticleTextures();
|
||||||
this.scene.start('MenuScene');
|
this.scene.start('MenuScene');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Generates a texture via an off-screen graphics buffer.
|
||||||
|
_tex(key, w, h, draw) {
|
||||||
|
const g = this.make.graphics({ x: 0, y: 0, add: false });
|
||||||
|
draw(g);
|
||||||
|
g.generateTexture(key, w, h);
|
||||||
|
g.destroy();
|
||||||
|
}
|
||||||
|
|
||||||
|
createBackgroundTextures() {
|
||||||
|
const S = 256;
|
||||||
|
|
||||||
|
// 1) Grid — Monad blockchain grid (default look, larger so kept as bg_grid)
|
||||||
|
this._tex('bg_grid', S, S, (g) => {
|
||||||
|
g.fillStyle(0x0f001f, 1);
|
||||||
|
g.fillRect(0, 0, S, S);
|
||||||
|
g.lineStyle(1, 0x2e0059, 0.4);
|
||||||
|
for (let i = 0; i <= S; i += 32) {
|
||||||
|
g.moveTo(i, 0); g.lineTo(i, S);
|
||||||
|
g.moveTo(0, i); g.lineTo(S, i);
|
||||||
|
}
|
||||||
|
g.strokePath();
|
||||||
|
// node dots at some intersections (deterministic -> tiles)
|
||||||
|
g.fillStyle(0x7c3aed, 0.35);
|
||||||
|
for (let x = 0; x <= S; x += 64) {
|
||||||
|
for (let y = 0; y <= S; y += 64) {
|
||||||
|
if (((x + y) / 64) % 2 === 0) g.fillCircle(x, y, 2);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// 2) Hex Nodes — honeycomb
|
||||||
|
this._tex('bg_hex', S, S, (g) => {
|
||||||
|
g.fillStyle(0x0d0420, 1);
|
||||||
|
g.fillRect(0, 0, S, S);
|
||||||
|
const r = 22;
|
||||||
|
const w = Math.sqrt(3) * r;
|
||||||
|
const vSpace = 1.5 * r;
|
||||||
|
g.lineStyle(1.5, 0x6d28d9, 0.30);
|
||||||
|
for (let row = -1, ry = 0; ry < S + r; row++, ry = row * vSpace) {
|
||||||
|
const offset = (row % 2 === 0) ? 0 : w / 2;
|
||||||
|
for (let cx = -w; cx < S + w; cx += w) {
|
||||||
|
this._hex(g, cx + offset, ry, r);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// accent glow nodes
|
||||||
|
g.fillStyle(0xa855f7, 0.25);
|
||||||
|
for (let row = 0, ry = 0; ry < S; row += 2, ry = row * vSpace) {
|
||||||
|
for (let cx = 0; cx < S; cx += w * 2) g.fillCircle(cx, ry, 2.5);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// 3) Starfield — deep space
|
||||||
|
this._tex('bg_starfield', S, S, (g) => {
|
||||||
|
g.fillStyle(0x070016, 1);
|
||||||
|
g.fillRect(0, 0, S, S);
|
||||||
|
// faint distant dust
|
||||||
|
const dust = [0x1a0b3a, 0x12082b];
|
||||||
|
for (let i = 0; i < 26; i++) {
|
||||||
|
g.fillStyle(Phaser.Utils.Array.GetRandom(dust), 0.5);
|
||||||
|
g.fillCircle(Phaser.Math.Between(0, S), Phaser.Math.Between(0, S), Phaser.Math.Between(20, 55));
|
||||||
|
}
|
||||||
|
// stars
|
||||||
|
const cols = [0xffffff, 0xd8b4fe, 0x93c5fd, 0xa855f7];
|
||||||
|
for (let i = 0; i < 150; i++) {
|
||||||
|
g.fillStyle(Phaser.Utils.Array.GetRandom(cols), Phaser.Math.FloatBetween(0.35, 1));
|
||||||
|
g.fillCircle(Phaser.Math.Between(0, S), Phaser.Math.Between(0, S), Phaser.Math.FloatBetween(0.6, 1.8));
|
||||||
|
}
|
||||||
|
// a few bright glows
|
||||||
|
for (let i = 0; i < 5; i++) {
|
||||||
|
const x = Phaser.Math.Between(0, S); const y = Phaser.Math.Between(0, S);
|
||||||
|
g.fillStyle(0xffffff, 0.18); g.fillCircle(x, y, 6);
|
||||||
|
g.fillStyle(0xffffff, 0.9); g.fillCircle(x, y, 1.6);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// 4) Synthwave — neon horizontal grid
|
||||||
|
this._tex('bg_synthwave', S, S, (g) => {
|
||||||
|
g.fillStyle(0x0a0118, 1);
|
||||||
|
g.fillRect(0, 0, S, S);
|
||||||
|
// vertical faint lines
|
||||||
|
g.lineStyle(1, 0x3b0764, 0.5);
|
||||||
|
for (let x = 0; x <= S; x += 32) { g.moveTo(x, 0); g.lineTo(x, S); }
|
||||||
|
g.strokePath();
|
||||||
|
// neon horizontal lines, alternating magenta/cyan
|
||||||
|
for (let y = 0; y <= S; y += 28) {
|
||||||
|
const cyan = (y / 28) % 2 === 0;
|
||||||
|
const col = cyan ? 0x00e5ff : 0xff2d95;
|
||||||
|
g.lineStyle(3, col, 0.10); g.beginPath(); g.moveTo(0, y); g.lineTo(S, y); g.strokePath();
|
||||||
|
g.lineStyle(1, col, 0.55); g.beginPath(); g.moveTo(0, y); g.lineTo(S, y); g.strokePath();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// 5) Circuit — PCB traces
|
||||||
|
this._tex('bg_circuit', S, S, (g) => {
|
||||||
|
g.fillStyle(0x04120e, 1);
|
||||||
|
g.fillRect(0, 0, S, S);
|
||||||
|
g.lineStyle(1, 0x0f766e, 0.45);
|
||||||
|
for (let i = 0; i <= S; i += 32) { g.moveTo(i, 0); g.lineTo(i, S); g.moveTo(0, i); g.lineTo(S, i); }
|
||||||
|
g.strokePath();
|
||||||
|
// brighter trace accents (deterministic checker -> tiles)
|
||||||
|
g.lineStyle(2, 0x10b981, 0.5);
|
||||||
|
for (let x = 0; x < S; x += 32) {
|
||||||
|
for (let y = 0; y < S; y += 32) {
|
||||||
|
if (((x / 32) + (y / 32)) % 3 === 0) { g.beginPath(); g.moveTo(x, y); g.lineTo(x + 32, y); g.strokePath(); }
|
||||||
|
if (((x / 32) + (y / 32)) % 4 === 0) { g.beginPath(); g.moveTo(x, y); g.lineTo(x, y + 32); g.strokePath(); }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// solder nodes
|
||||||
|
g.fillStyle(0x34d399, 0.8);
|
||||||
|
for (let x = 0; x <= S; x += 32) {
|
||||||
|
for (let y = 0; y <= S; y += 32) {
|
||||||
|
if (((x + y) / 32) % 2 === 0) g.fillCircle(x, y, 2);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// 6) Void — minimal dark
|
||||||
|
this._tex('bg_void', S, S, (g) => {
|
||||||
|
g.fillStyle(0x060010, 1);
|
||||||
|
g.fillRect(0, 0, S, S);
|
||||||
|
g.fillStyle(0x1b1036, 0.6);
|
||||||
|
for (let x = 24; x < S; x += 48) {
|
||||||
|
for (let y = 24; y < S; y += 48) g.fillCircle(x, y, 1);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
_hex(g, cx, cy, r) {
|
||||||
|
g.beginPath();
|
||||||
|
for (let i = 0; i < 6; i++) {
|
||||||
|
const a = Math.PI / 180 * (60 * i - 90);
|
||||||
|
const px = cx + r * Math.cos(a);
|
||||||
|
const py = cy + r * Math.sin(a);
|
||||||
|
if (i === 0) g.moveTo(px, py); else g.lineTo(px, py);
|
||||||
|
}
|
||||||
|
g.closePath();
|
||||||
|
g.strokePath();
|
||||||
|
}
|
||||||
|
|
||||||
createParticleTextures() {
|
createParticleTextures() {
|
||||||
// All white so emitters can tint per-use. Keep textures tiny.
|
// All white so emitters can tint per-use. Keep textures tiny.
|
||||||
|
|
||||||
@@ -59,37 +199,6 @@ export class BootScene extends Scene {
|
|||||||
}
|
}
|
||||||
|
|
||||||
createBackgrounds() {
|
createBackgrounds() {
|
||||||
const gridW = 512;
|
|
||||||
const gridH = 512;
|
|
||||||
const gridGraphics = this.make.graphics({ x: 0, y: 0, add: false });
|
|
||||||
|
|
||||||
// Deep purple background
|
|
||||||
gridGraphics.fillStyle(0x0f001f, 1);
|
|
||||||
gridGraphics.fillRect(0, 0, gridW, gridH);
|
|
||||||
|
|
||||||
// Grid lines
|
|
||||||
gridGraphics.lineStyle(1, 0x2e0059, 0.35);
|
|
||||||
for (let i = 0; i <= gridW; i += 32) {
|
|
||||||
gridGraphics.moveTo(i, 0);
|
|
||||||
gridGraphics.lineTo(i, gridH);
|
|
||||||
}
|
|
||||||
for (let i = 0; i <= gridH; i += 32) {
|
|
||||||
gridGraphics.moveTo(0, i);
|
|
||||||
gridGraphics.lineTo(gridW, i);
|
|
||||||
}
|
|
||||||
gridGraphics.strokePath();
|
|
||||||
|
|
||||||
// Some accent hexagons / blockchain nodes
|
|
||||||
gridGraphics.lineStyle(1, 0x581c87, 0.2);
|
|
||||||
for (let i = 0; i < 8; i++) {
|
|
||||||
const hx = Phaser.Math.Between(20, gridW - 20);
|
|
||||||
const hy = Phaser.Math.Between(20, gridH - 20);
|
|
||||||
const size = Phaser.Math.Between(6, 14);
|
|
||||||
gridGraphics.strokeCircle(hx, hy, size);
|
|
||||||
}
|
|
||||||
|
|
||||||
gridGraphics.generateTexture('gridBg', gridW, gridH);
|
|
||||||
|
|
||||||
// Star / particle texture
|
// Star / particle texture
|
||||||
const sGfx = this.make.graphics({ x: 0, y: 0, add: false });
|
const sGfx = this.make.graphics({ x: 0, y: 0, add: false });
|
||||||
sGfx.fillStyle(0xffffff, 0.9);
|
sGfx.fillStyle(0xffffff, 0.9);
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ import { Scene } from 'phaser';
|
|||||||
import { sound } from '../managers/SoundManager.js';
|
import { sound } from '../managers/SoundManager.js';
|
||||||
import { createButton } from '../utils/ui.js';
|
import { createButton } from '../utils/ui.js';
|
||||||
import { todaySeed } from '../utils/random.js';
|
import { todaySeed } from '../utils/random.js';
|
||||||
|
import { currentBgTexture } from '../utils/background.js';
|
||||||
|
|
||||||
export class GameOverScene extends Scene {
|
export class GameOverScene extends Scene {
|
||||||
constructor() {
|
constructor() {
|
||||||
@@ -28,7 +29,7 @@ export class GameOverScene extends Scene {
|
|||||||
create() {
|
create() {
|
||||||
const { width, height } = this.scale;
|
const { width, height } = this.scale;
|
||||||
|
|
||||||
this.add.tileSprite(width / 2, height / 2, width, height, 'gridBg');
|
this.add.tileSprite(width / 2, height / 2, width, height, currentBgTexture());
|
||||||
|
|
||||||
const naddie = this.add.image(width / 2, height * 0.22, 'player_dead').setScale(0.32);
|
const naddie = this.add.image(width / 2, height * 0.22, 'player_dead').setScale(0.32);
|
||||||
this.tweens.add({ targets: naddie, angle: -10, duration: 2000, yoyo: true, repeat: -1, ease: 'Sine.easeInOut' });
|
this.tweens.add({ targets: naddie, angle: -10, duration: 2000, yoyo: true, repeat: -1, ease: 'Sine.easeInOut' });
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ import { StatsManager } from '../managers/StatsManager.js';
|
|||||||
import { sound } from '../managers/SoundManager.js';
|
import { sound } from '../managers/SoundManager.js';
|
||||||
import { storage, KEYS } from '../utils/storage.js';
|
import { storage, KEYS } from '../utils/storage.js';
|
||||||
import { rng } from '../utils/random.js';
|
import { rng } from '../utils/random.js';
|
||||||
|
import { currentBgTexture } from '../utils/background.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';
|
||||||
|
|
||||||
export class GameScene extends Scene {
|
export class GameScene extends Scene {
|
||||||
@@ -29,7 +30,7 @@ export class GameScene extends Scene {
|
|||||||
// Seed gameplay RNG so a given seed produces a reproducible run (Daily).
|
// Seed gameplay RNG so a given seed produces a reproducible run (Daily).
|
||||||
rng.seed(this.seed);
|
rng.seed(this.seed);
|
||||||
|
|
||||||
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, currentBgTexture())
|
||||||
.setScrollFactor(0)
|
.setScrollFactor(0)
|
||||||
.setDepth(-10);
|
.setDepth(-10);
|
||||||
|
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import { ParticleManager } from '../managers/ParticleManager.js';
|
|||||||
import { createButton } from '../utils/ui.js';
|
import { createButton } from '../utils/ui.js';
|
||||||
import { storage, KEYS } from '../utils/storage.js';
|
import { storage, KEYS } from '../utils/storage.js';
|
||||||
import { todaySeed } from '../utils/random.js';
|
import { todaySeed } from '../utils/random.js';
|
||||||
|
import { currentBgTexture, currentBackground, setBackground, BACKGROUNDS } from '../utils/background.js';
|
||||||
|
|
||||||
export class MenuScene extends Scene {
|
export class MenuScene extends Scene {
|
||||||
constructor() {
|
constructor() {
|
||||||
@@ -15,7 +16,7 @@ export class MenuScene extends Scene {
|
|||||||
create() {
|
create() {
|
||||||
const { width, height } = this.scale;
|
const { width, height } = this.scale;
|
||||||
|
|
||||||
this.add.tileSprite(width / 2, height / 2, width, height, 'gridBg');
|
this.menuBg = this.add.tileSprite(width / 2, height / 2, width, height, currentBgTexture());
|
||||||
|
|
||||||
this.add.text(width / 2, height * 0.16, 'NADDIE JUMP', {
|
this.add.text(width / 2, height * 0.16, 'NADDIE JUMP', {
|
||||||
fontFamily: '"Press Start 2P", monospace',
|
fontFamily: '"Press Start 2P", monospace',
|
||||||
@@ -251,15 +252,15 @@ export class MenuScene extends Scene {
|
|||||||
|
|
||||||
showSettings() {
|
showSettings() {
|
||||||
const { width, height } = this.scale;
|
const { width, height } = this.scale;
|
||||||
const m = this._openModal('SETTINGS', 460);
|
const m = this._openModal('SETTINGS', 620);
|
||||||
const cx = width / 2;
|
const cx = width / 2;
|
||||||
let baseY = height / 2 - 130;
|
const cy = height / 2;
|
||||||
|
|
||||||
// Volume stepper
|
// Volume stepper
|
||||||
m.add(this.add.text(cx, baseY, 'VOLUME', {
|
m.add(this.add.text(cx, cy - 230, 'VOLUME', {
|
||||||
fontFamily: '"Press Start 2P", monospace', fontSize: '11px', color: '#d8b4fe',
|
fontFamily: '"Press Start 2P", monospace', fontSize: '11px', color: '#d8b4fe',
|
||||||
}).setOrigin(0.5));
|
}).setOrigin(0.5));
|
||||||
const volText = this.add.text(cx, baseY + 34, `${Math.round(sound.getVolume() * 100)}%`, {
|
const volText = this.add.text(cx, cy - 200, `${Math.round(sound.getVolume() * 100)}%`, {
|
||||||
fontFamily: '"Press Start 2P", monospace', fontSize: '13px', color: '#ffd700',
|
fontFamily: '"Press Start 2P", monospace', fontSize: '13px', color: '#ffd700',
|
||||||
}).setOrigin(0.5);
|
}).setOrigin(0.5);
|
||||||
m.add(volText);
|
m.add(volText);
|
||||||
@@ -270,27 +271,56 @@ export class MenuScene extends Scene {
|
|||||||
volText.setText(`${Math.round(sound.getVolume() * 100)}%`);
|
volText.setText(`${Math.round(sound.getVolume() * 100)}%`);
|
||||||
sound.click();
|
sound.click();
|
||||||
};
|
};
|
||||||
m.add(createButton(this, cx - 90, baseY + 34, '-', () => stepVol(-0.1), { width: 44, height: 36, fontSize: '14px' }).bg);
|
const volMinus = createButton(this, cx - 90, cy - 200, '-', () => stepVol(-0.1), { width: 44, height: 36, fontSize: '16px' });
|
||||||
m.add(createButton(this, cx + 90, baseY + 34, '+', () => stepVol(0.1), { width: 44, height: 36, fontSize: '14px' }).bg);
|
const volPlus = createButton(this, cx + 90, cy - 200, '+', () => stepVol(0.1), { width: 44, height: 36, fontSize: '16px' });
|
||||||
|
m.add(volMinus.bg); m.add(volMinus.label);
|
||||||
|
m.add(volPlus.bg); m.add(volPlus.label);
|
||||||
|
|
||||||
// Particle quality toggle
|
// Particle quality toggle
|
||||||
baseY += 96;
|
m.add(this.add.text(cx, cy - 150, 'PARTICLE QUALITY', {
|
||||||
m.add(this.add.text(cx, baseY, 'PARTICLE QUALITY', {
|
|
||||||
fontFamily: '"Press Start 2P", monospace', fontSize: '11px', color: '#d8b4fe',
|
fontFamily: '"Press Start 2P", monospace', fontSize: '11px', color: '#d8b4fe',
|
||||||
}).setOrigin(0.5));
|
}).setOrigin(0.5));
|
||||||
const currentQ = () => storage.getItem(KEYS.particleQuality, ParticleManager.resolveQuality(this));
|
const currentQ = () => storage.getItem(KEYS.particleQuality, ParticleManager.resolveQuality(this));
|
||||||
const qBtn = createButton(this, cx, baseY + 34, currentQ().toUpperCase(), () => {
|
const qBtn = createButton(this, cx, cy - 120, currentQ().toUpperCase(), () => {
|
||||||
const next = currentQ() === 'low' ? 'high' : 'low';
|
const next = currentQ() === 'low' ? 'high' : 'low';
|
||||||
storage.setItem(KEYS.particleQuality, next);
|
storage.setItem(KEYS.particleQuality, next);
|
||||||
qBtn.label.setText(next.toUpperCase());
|
qBtn.label.setText(next.toUpperCase());
|
||||||
sound.click();
|
sound.click();
|
||||||
}, { width: 160, height: 38, fontSize: '12px' });
|
}, { width: 160, height: 36, fontSize: '12px' });
|
||||||
m.add(qBtn.bg); m.add(qBtn.label);
|
m.add(qBtn.bg); m.add(qBtn.label);
|
||||||
|
|
||||||
|
// Background selector with live preview
|
||||||
|
m.add(this.add.text(cx, cy - 72, 'BACKGROUND', {
|
||||||
|
fontFamily: '"Press Start 2P", monospace', fontSize: '11px', color: '#d8b4fe',
|
||||||
|
}).setOrigin(0.5));
|
||||||
|
|
||||||
|
let bgIndex = Math.max(0, BACKGROUNDS.findIndex((b) => b.id === currentBackground().id));
|
||||||
|
const preview = this.add.tileSprite(cx, cy - 8, 280, 90, BACKGROUNDS[bgIndex].texture).setDepth(102);
|
||||||
|
m.elements.push(preview);
|
||||||
|
const frame = this.add.rectangle(cx, cy - 8, 284, 94).setStrokeStyle(2, 0xa855f7);
|
||||||
|
m.add(frame);
|
||||||
|
const bgName = this.add.text(cx, cy + 58, BACKGROUNDS[bgIndex].name.toUpperCase(), {
|
||||||
|
fontFamily: '"Press Start 2P", monospace', fontSize: '11px', color: '#ffd700',
|
||||||
|
}).setOrigin(0.5);
|
||||||
|
m.add(bgName);
|
||||||
|
|
||||||
|
const applyBg = (dir) => {
|
||||||
|
bgIndex = (bgIndex + dir + BACKGROUNDS.length) % BACKGROUNDS.length;
|
||||||
|
const bg = BACKGROUNDS[bgIndex];
|
||||||
|
setBackground(bg.id);
|
||||||
|
preview.setTexture(bg.texture);
|
||||||
|
bgName.setText(bg.name.toUpperCase());
|
||||||
|
if (this.menuBg) this.menuBg.setTexture(bg.texture);
|
||||||
|
sound.click();
|
||||||
|
};
|
||||||
|
const bgPrev = createButton(this, cx - 125, cy + 58, '<', () => applyBg(-1), { width: 40, height: 36, fontSize: '16px' });
|
||||||
|
const bgNext = createButton(this, cx + 125, cy + 58, '>', () => applyBg(1), { width: 40, height: 36, fontSize: '16px' });
|
||||||
|
m.add(bgPrev.bg); m.add(bgPrev.label);
|
||||||
|
m.add(bgNext.bg); m.add(bgNext.label);
|
||||||
|
|
||||||
// Reset progress (two-step confirm)
|
// Reset progress (two-step confirm)
|
||||||
baseY += 96;
|
|
||||||
let armed = false;
|
let armed = false;
|
||||||
const resetBtn = createButton(this, cx, baseY + 10, 'RESET PROGRESS', () => {
|
const resetBtn = createButton(this, cx, cy + 130, 'RESET PROGRESS', () => {
|
||||||
if (!armed) {
|
if (!armed) {
|
||||||
armed = true;
|
armed = true;
|
||||||
resetBtn.label.setText('TAP AGAIN!');
|
resetBtn.label.setText('TAP AGAIN!');
|
||||||
@@ -302,7 +332,7 @@ export class MenuScene extends Scene {
|
|||||||
storage.removeItem(KEYS.achievements);
|
storage.removeItem(KEYS.achievements);
|
||||||
resetBtn.label.setText('DONE');
|
resetBtn.label.setText('DONE');
|
||||||
sound.click();
|
sound.click();
|
||||||
}, { width: 240, height: 42, fontSize: '12px', bgColor: 0x581c87, hoverColor: 0x7e22ce });
|
}, { width: 240, height: 40, fontSize: '12px', bgColor: 0x581c87, hoverColor: 0x7e22ce });
|
||||||
m.add(resetBtn.bg); m.add(resetBtn.label);
|
m.add(resetBtn.bg); m.add(resetBtn.label);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
17
src/utils/background.js
Normal file
17
src/utils/background.js
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
import { storage, KEYS } from './storage.js';
|
||||||
|
import { BACKGROUNDS, DEFAULT_BACKGROUND, getBackground } from '../config/backgrounds.js';
|
||||||
|
|
||||||
|
export function currentBackground() {
|
||||||
|
const id = storage.getItem(KEYS.background, DEFAULT_BACKGROUND);
|
||||||
|
return getBackground(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function currentBgTexture() {
|
||||||
|
return currentBackground().texture;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function setBackground(id) {
|
||||||
|
storage.setItem(KEYS.background, id);
|
||||||
|
}
|
||||||
|
|
||||||
|
export { BACKGROUNDS, getBackground };
|
||||||
@@ -11,6 +11,7 @@ export const KEYS = {
|
|||||||
tutorialSeen: 'naddie_tutorial_seen',
|
tutorialSeen: 'naddie_tutorial_seen',
|
||||||
achievements: 'naddie_achievements_v1',
|
achievements: 'naddie_achievements_v1',
|
||||||
particleQuality: 'naddie_particle_quality',
|
particleQuality: 'naddie_particle_quality',
|
||||||
|
background: 'naddie_background',
|
||||||
stats: 'naddie_stats_v1',
|
stats: 'naddie_stats_v1',
|
||||||
dailyPrefix: 'naddie_daily_',
|
dailyPrefix: 'naddie_daily_',
|
||||||
};
|
};
|
||||||
|
|||||||
Reference in New Issue
Block a user