74 lines
2.7 KiB
JavaScript
74 lines
2.7 KiB
JavaScript
import Phaser from '../vendor/phaser.js';
|
|
|
|
/**
|
|
* Converts a config color (number or CSS string like '#1b2540')
|
|
* into the integer format Phaser expects.
|
|
*/
|
|
export function toColor(value, fallback = 0xffffff) {
|
|
if (typeof value === 'number' && Number.isFinite(value)) {
|
|
return value >>> 0;
|
|
}
|
|
if (typeof value === 'string' && value.length > 0) {
|
|
const color = Phaser.Display.Color.ValueToColor(value.trim());
|
|
if (color) return color.color;
|
|
}
|
|
return fallback;
|
|
}
|
|
|
|
/**
|
|
* Converts a color (number or CSS string) to a '#rrggbb' CSS string.
|
|
*
|
|
* Phaser v4 (4.2.1 Giedi) quirk: Text styles are written straight to the
|
|
* canvas — `fillStyle = style.color` — so a numeric color (e.g. 0xeaf6ff)
|
|
* is an *invalid* fillStyle and the text silently renders black. Canvas-
|
|
* backed text (add.text / TextStyle) must therefore always get CSS strings;
|
|
* keep toColor for Graphics/shape APIs that want numbers.
|
|
*/
|
|
export function toCss(value, fallback = '#ffffff') {
|
|
const n = toColor(value, toColor(fallback));
|
|
return '#' + (n >>> 0).toString(16).padStart(6, '0');
|
|
}
|
|
|
|
/**
|
|
* Rotate a color's HUE (degrees; + is clockwise on the hue wheel) and
|
|
* rescale its saturation and lightness (1 = unchanged). Returns a 0xRRGGBB
|
|
* number (ready for setTint()).
|
|
*
|
|
* Used by the nebula FRONT gas layer: the accent tint is the system's base
|
|
* color rotated a little and lifted — two gas species of one nebula
|
|
* (see data/game.json → nebula.front.accent). Pure math (no Phaser), so it
|
|
* is testable in bare Node; grays stay grays (s=0 ⇒ hue is irrelevant),
|
|
* and black/white are preserved.
|
|
*/
|
|
export function shiftHue(color, { hueShift = 0, satScale = 1, lightScale = 1 } = {}) {
|
|
const n = toColor(color, 0xffffff) >>> 0;
|
|
const r = ((n >> 16) & 255) / 255;
|
|
const g = ((n >> 8) & 255) / 255;
|
|
const b = (n & 255) / 255;
|
|
const max = Math.max(r, g, b);
|
|
const min = Math.min(r, g, b);
|
|
const d = max - min;
|
|
const l = (max + min) / 2;
|
|
let h = 0;
|
|
const s = d === 0 ? 0 : d / (1 - Math.abs(2 * l - 1));
|
|
if (d !== 0) {
|
|
if (max === r) h = ((g - b) / d) % 6;
|
|
else if (max === g) h = (b - r) / d + 2;
|
|
else h = (r - g) / d + 4;
|
|
h *= 60;
|
|
if (h < 0) h += 360;
|
|
}
|
|
h = (h + hueShift) % 360;
|
|
const s2 = Math.min(1, Math.max(0, s * satScale));
|
|
const l2 = Math.min(1, Math.max(0, l * lightScale));
|
|
const c = (1 - Math.abs(2 * l2 - 1)) * s2;
|
|
const hp = h / 60;
|
|
const x = c * (1 - Math.abs((hp % 2) - 1));
|
|
const [r2, g2, b2] =
|
|
hp < 1 ? [c, x, 0] : hp < 2 ? [x, c, 0] : hp < 3 ? [0, c, x] :
|
|
hp < 4 ? [0, x, c] : hp < 5 ? [x, 0, c] : [c, 0, x];
|
|
const m = l2 - c / 2;
|
|
const to255 = (v) => Math.min(255, Math.max(0, Math.round((v + m) * 255)));
|
|
return ((to255(r2) << 16) | (to255(g2) << 8) | to255(b2)) >>> 0;
|
|
}
|