71 lines
2.2 KiB
TypeScript
71 lines
2.2 KiB
TypeScript
import type { EventBus } from './EventBus';
|
|
import type { HeatStrike, NightState } from '../data/types';
|
|
|
|
export function freshNightState(venueId: string, nightIndex: number, licensed: number): NightState {
|
|
return {
|
|
venueId,
|
|
nightIndex,
|
|
vibe: 50,
|
|
aggro: 0,
|
|
heatStrikes: [],
|
|
hype: 1,
|
|
capacity: { inside: 0, licensed, clickerShown: 0 },
|
|
clockMin: 0,
|
|
location: 'door',
|
|
incidents: [],
|
|
};
|
|
}
|
|
|
|
const clamp = (v: number, lo: number, hi: number) => Math.max(lo, Math.min(hi, v));
|
|
|
|
// Single writer for the night's numbers. Scenes emit meters:delta / heat:strike;
|
|
// nothing else may mutate NightState meters directly.
|
|
export class Meters {
|
|
private readonly offs: Array<() => void> = [];
|
|
|
|
constructor(
|
|
private readonly bus: EventBus,
|
|
public readonly state: NightState,
|
|
) {
|
|
this.offs.push(
|
|
bus.on('meters:delta', (d) => this.applyDelta(d)),
|
|
bus.on('heat:strike', (s) => this.applyStrike(s)),
|
|
bus.on('clock:tick', ({ clockMin }) => {
|
|
this.state.clockMin = clockMin;
|
|
}),
|
|
bus.on('night:phaseChange', ({ location }) => {
|
|
this.state.location = location;
|
|
}),
|
|
bus.on('incident:log', (incident) => {
|
|
this.state.incidents.push(incident);
|
|
}),
|
|
bus.on('door:clicker', ({ direction }) => {
|
|
this.state.capacity.clickerShown = Math.max(
|
|
0,
|
|
this.state.capacity.clickerShown + (direction === 'in' ? 1 : -1),
|
|
);
|
|
}),
|
|
);
|
|
}
|
|
|
|
private applyDelta(d: { vibe?: number; aggro?: number; hype?: number }): void {
|
|
const s = this.state;
|
|
if (d.vibe !== undefined) {
|
|
// Hype multiplies the good news only — queue theatre never softens a hit.
|
|
const scaled = d.vibe > 0 ? d.vibe * s.hype : d.vibe;
|
|
s.vibe = clamp(s.vibe + scaled, 0, 100);
|
|
}
|
|
if (d.aggro !== undefined) s.aggro = clamp(s.aggro + d.aggro, 0, 100);
|
|
if (d.hype !== undefined) s.hype = clamp(s.hype + d.hype, 1, 3);
|
|
this.bus.emit('meters:changed', { vibe: s.vibe, aggro: s.aggro, hype: s.hype });
|
|
}
|
|
|
|
private applyStrike(strike: HeatStrike): void {
|
|
this.state.heatStrikes.push(strike);
|
|
}
|
|
|
|
destroy(): void {
|
|
for (const off of this.offs) off();
|
|
}
|
|
}
|