B1 Seeded RNG + Daily Challenge - utils/random.js wraps Phaser seedable RND (Between/frac are NOT seedable) - All gameplay spawning (PlatformManager, Platform, Enemy) uses seeded rng - GameScene reads mode/seed in init and seeds the run; daily shows a HUD badge and keeps a per-day best (daily_<YYYYMMDD>); MenuScene DAILY button; GameOver RETRY preserves mode and shows today's best - Verified: same seed -> identical layout, different seed -> different B2 New content - Enemy mev_bot: homing chaser that eases toward the player (unlock >1500) - Platform reorg: phantom, semi-transparent, vanishes shortly after landing (unlock >600); no power-ups on breaking/reorg; SPAWN_RATES + UNLOCK config - Verified spawn distribution at high difficulty includes all new types B3 Settings - SoundManager gains volume (persisted); MenuScene SETTINGS overlay with volume stepper, particle-quality Low/High toggle, two-step reset progress B4 Stats - StatsManager tracks lifetime games/jumps/stomps/blocks/best combo, flushed at game over; MenuScene STATS overlay; hooks in GameScene/ScoreManager B5 Difficulty tuning via UNLOCK thresholds and rebalanced spawn rates Functionally verified in-browser via eval (no console errors, deterministic daily, content spawns, particles emit). Visual screenshot unavailable in the headless preview because the hidden tab pauses Phaser's loop.
46 lines
1.2 KiB
JavaScript
46 lines
1.2 KiB
JavaScript
/**
|
|
* Thin wrapper over Phaser's seedable RandomDataGenerator (Phaser.Math.RND).
|
|
* IMPORTANT: Phaser.Math.Between / FloatBetween use Math.random and are NOT
|
|
* seedable — gameplay spawning must use these helpers so a seed fully
|
|
* determines a run (Daily Challenge). Cosmetic randomness (particles) may keep
|
|
* Math.random.
|
|
*/
|
|
export const rng = {
|
|
seed(value) {
|
|
Phaser.Math.RND.sow([String(value)]);
|
|
},
|
|
|
|
/** Random float [0, 1). */
|
|
frac() {
|
|
return Phaser.Math.RND.frac();
|
|
},
|
|
|
|
/** Integer in [min, max] inclusive. */
|
|
between(min, max) {
|
|
return Phaser.Math.RND.between(min, max);
|
|
},
|
|
|
|
/** Float in [min, max). */
|
|
realBetween(min, max) {
|
|
return Phaser.Math.RND.realInRange(min, max);
|
|
},
|
|
|
|
/** Random element of an array. */
|
|
pick(arr) {
|
|
return Phaser.Math.RND.pick(arr);
|
|
},
|
|
|
|
/** +1 or -1. */
|
|
sign() {
|
|
return Phaser.Math.RND.frac() < 0.5 ? 1 : -1;
|
|
},
|
|
};
|
|
|
|
/** Returns today's date as a YYYY-MM-DD string (local time) for daily seeds. */
|
|
export function todaySeed() {
|
|
const d = new Date();
|
|
const mm = String(d.getMonth() + 1).padStart(2, '0');
|
|
const dd = String(d.getDate()).padStart(2, '0');
|
|
return `${d.getFullYear()}-${mm}-${dd}`;
|
|
}
|