import { type NameOptions, type RNGState, SeededRandom } from "@common/random"; import { Component, World } from "../core/world"; import { component } from "../utils/decorators"; @component export class Random extends Component<{ random: RNGState }> implements Omit { private rng?: SeededRandom; constructor(random: SeededRandom | string | number | RNGState = Date.now()) { super({ random: (() => { const rng = random instanceof SeededRandom ? random : new SeededRandom(random); return rng.getState(); })() }); } nextFloat(): number { return this.use((rng) => rng.nextFloat()); } nextInt(min: number, max: number): number; nextInt(max: number): number; nextInt(minOrMax: number, max?: number): number { return this.use((rng) => rng.nextInt(minOrMax, max as number)); } nextBool(): boolean { return this.use((rng) => rng.nextBool()); } nextName(options?: NameOptions): string { return this.use((rng) => rng.nextName(options)); } choice(iterable: Iterable): T; choice(iterable: Iterable, k: number): T[]; choice(iterable: Iterable, k?: number): T | T[] { return this.use((rng) => rng.choice(iterable, k as number)); } weightedChoice(items: readonly T[], weights: readonly number[]): T; weightedChoice(items: readonly T[], weights: readonly number[], k: number): T[]; weightedChoice(items: readonly T[], weights: readonly number[], k?: number): T | T[] { return this.use((rng) => rng.weightedChoice(items, weights, k as number)); } shuffle(arr: T[]): T[] { return this.use((rng) => rng.shuffle(arr)); } toShuffled(arr: readonly T[]): T[] { return this.use((rng) => rng.toShuffled(arr)); } use(fn: (rng: SeededRandom) => T): T { if (this.rng) { this.rng.setState(this.state.random); } else { this.rng = new SeededRandom(this.state.random); } const result = fn(this.rng); this.state.random = this.rng.getState(); return result; } } export const getWorldRandom = (world: World): Random => { const random = world.findComponent(Random); if (random) { return random; } const entity = world.createEntity('random_*'); return entity.add(new Random()); }