bookquoy/js/hud.js
jing 53ad2030af BOOKQUOY v0.1 — Paddo, three disciplines, letters, gaps, 2:00 sesh
Level 01: Paddington Skatepark from the council plan + park guides —
heightAt(x,z) single-source terrain, grindable rails/ledges, named gaps,
B-O-O-K-Q-U-O-Y letters with the BOOKQUOYORK callback.

SKATE / BMX / BLADES (blades cop thrown rubbish, as is tradition).
character_kit two-rig banks: physics owns the root, clips are cosmetic.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-03 19:13:13 +10:00

77 lines
2.8 KiB
JavaScript

// BOOKQUOY HUD — score, combo, timer, the letters, and the callouts.
import { comboLabel } from './tricks.js';
export class Hud {
constructor() {
this.$ = id => document.getElementById(id);
this.letterOrder = ['B', 'O', 'O', 'K', 'Q', 'U', 'O', 'Y'];
this.reset();
}
reset() {
this.score = 0; this.combo = []; this.comboScore = 0;
this.lettersLit = 0;
this.$('score').textContent = '0';
this.$('combo').textContent = '';
this.renderLetters();
this.$('flash').textContent = '';
this.$('over').style.display = 'none';
}
renderLetters() {
this.$('letters').innerHTML = this.letterOrder
.map((c, i) => `<span class="${i < this.lettersLit ? 'lit' : ''}">${c}</span>`).join('');
}
addTrick(name, score) {
this.combo.push({ name, score }); this.comboScore += score;
this.$('combo').textContent = comboLabel(this.combo) + ` (${this.comboScore * this.combo.length})`;
}
addGrindPoints(pts) {
if (!this.combo.length) this.combo.push({ name: 'Grind', score: 0 });
this.comboScore += pts;
this.$('combo').textContent = comboLabel(this.combo) + ` (${Math.round(this.comboScore * this.combo.length)})`;
}
bank() {
if (!this.combo.length) return;
this.score += Math.round(this.comboScore * this.combo.length);
this.$('score').textContent = this.score.toLocaleString();
this.combo = []; this.comboScore = 0;
this.$('combo').textContent = '';
}
dropCombo() {
if (this.combo.length) this.flash('BAILED — combo lost', '#ff6b6b');
this.combo = []; this.comboScore = 0;
this.$('combo').textContent = '';
}
letter(ch) {
this.lettersLit = Math.min(8, this.lettersLit + 1);
this.renderLetters();
this.flash(ch + ' !', '#ffd93b');
return this.lettersLit === 8;
}
flash(text, color = '#9be89b') {
const f = this.$('flash');
f.textContent = text; f.style.color = color;
f.style.opacity = '1';
clearTimeout(this._ft);
this._ft = setTimeout(() => f.style.opacity = '0', 1400);
}
bigFlash(text) {
const f = this.$('bigflash');
f.textContent = text; f.style.opacity = '1';
clearTimeout(this._bt);
this._bt = setTimeout(() => f.style.opacity = '0', 2600);
}
timer(t) {
const m = Math.floor(Math.max(t, 0) / 60), s = Math.floor(Math.max(t, 0) % 60);
this.$('timer').textContent = `${m}:${String(s).padStart(2, '0')}`;
this.$('timer').style.color = t < 15 ? '#ff6b6b' : '#dde';
}
sessionOver(score) {
const best = Math.max(score, +(localStorage.getItem('bookquoy_best') || 0));
localStorage.setItem('bookquoy_best', best);
this.$('finalScore').textContent = score.toLocaleString();
this.$('bestScore').textContent = best.toLocaleString();
this.$('over').style.display = 'flex';
}
title(show) { this.$('title').style.display = show ? 'flex' : 'none'; }
}