import Phaser from '../vendor/phaser.js'; import { config } from '../config/Config.js'; import { toColor } from '../utils/Color.js'; import { canvasTexture } from '../utils/Textures.js'; const STAR_KEY = '__combat_star'; /** * A slow diagonal-drifting starfield for the FIXED combat arena. * * The main game's Starfield (js/visuals/Starfield.js) is camera-driven — * here the camera never moves, so the stars drift on their own and wrap * at the screen edges, with a gentle per-star twinkle. Count + colors * reuse data/game.json → starfield so the range matches the game sky. */ export class StarDrift { /** @param {object} scene the combat scene */ constructor(scene) { this.scene = scene; const cfg = config.get('game.starfield', {}); this.enabled = cfg.enabled !== false; this.count = Math.max(0, cfg.count ?? 140); this.colors = (cfg.colors ?? ['#ffffff']).map((c) => toColor(c, 0xffffff)); this.speed = 4; // px/s diagonal drift (the arena's ambient current) this.stars = []; this.vx = 0; this.vy = 0; } /** Lay the stars down across the screen. */ create(w, h) { if (!this.enabled) return; const scene = this.scene; if (!scene.textures.exists(STAR_KEY)) { canvasTexture(scene, STAR_KEY, 8, 8, (ctx, w, h) => { const c = Math.min(w, h) / 2; const g = ctx.createRadialGradient(c, c, 0, c, c, c); g.addColorStop(0, 'rgba(255,255,255,1)'); g.addColorStop(0.6, 'rgba(255,255,255,0.7)'); g.addColorStop(1, 'rgba(255,255,255,0)'); ctx.fillStyle = g; ctx.fillRect(0, 0, w, h); }); } const driftAngle = -0.6; // rad — a slow down-right current this.vx = Math.cos(driftAngle) * this.speed; this.vy = Math.sin(driftAngle) * this.speed; for (let i = 0; i < this.count; i++) { const star = scene .add.image(Phaser.Math.FloatBetween(0, w), Phaser.Math.FloatBetween(0, h), STAR_KEY) .setScale(Phaser.Math.FloatBetween(0.25, 1.1)) .setAlpha(Phaser.Math.FloatBetween(0.2, 0.8)) .setTint(this.colors[i % this.colors.length] ?? 0xffffff) .setDepth(0); star.baseAlpha = star.alpha; star.tw = Phaser.Math.FloatBetween(0.5, 2.2); // twinkle rate (rad/s) star.ph = Math.random() * Math.PI * 2; this.stars.push(star); } } /** Drift + wrap + twinkle. @param {number} time game ms @param {number} deltaMs */ update(time, deltaMs) { if (!this.enabled || this.stars.length === 0) return; const dt = deltaMs / 1000; const w = this.scene.scale.width; const h = this.scene.scale.height; for (const star of this.stars) { star.x += this.vx * dt; star.y += this.vy * dt; if (star.x < 0) star.x += w; else if (star.x > w) star.x -= w; if (star.y < 0) star.y += h; else if (star.y > h) star.y -= h; star.alpha = star.baseAlpha * (0.72 + 0.28 * Math.sin(time * 0.001 * star.tw + star.ph)); } } destroy() { for (const star of this.stars) star.destroy(); this.stars.length = 0; } }