orbit/js/combat/StarDrift.js

86 lines
3.0 KiB
JavaScript

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;
}
}