Add Skins as a side game for Bingo Bango Bongo

Players can now opt into a Skins side game when creating a BBB round.
The side game uses the same skins settings (carryover, net/gross, skin
value, double back nine). The scorecard gains a third Skins tab during
play and on the summary; the BBB leaderboard continues to rank by BBB
points.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Rolf
2026-03-23 22:09:16 +01:00
parent c298c473f4
commit 5432734240
5 changed files with 56 additions and 18 deletions
+22 -6
View File
@@ -11,10 +11,10 @@
{# ── Header ── #}
<div class="game-header">
<div style="display:flex; align-items:center; gap:0.75rem;">
<button @click="view === 'score' ? prevHole() : cardView = 'strokes'" class="icon-btn"
<button @click="view === 'score' ? prevHole() : cardPrev()" class="icon-btn"
:disabled="view === 'score' ? currentHole <= 1 : cardView === 'strokes'"></button>
<div style="text-align:center; flex:1;">
<div class="hole-label" x-text="view === 'score' ? 'Hole ' + currentHole + ' / {{ game.holes_count }}' : (cardView === 'strokes' ? 'Strokes' : (GAME_FORMAT === 'skins' ? 'Skins' : 'Points'))"></div>
<div class="hole-label" x-text="view === 'score' ? 'Hole ' + currentHole + ' / {{ game.holes_count }}' : (cardView === 'strokes' ? 'Strokes' : cardView === 'points' ? 'Points' : 'Skins')"></div>
<div style="font-size:0.72rem; color:var(--text-muted); margin-top:0.1rem;">
<template x-if="view === 'score'">
<span x-text="(HOLE_PARS[currentHole] ? 'Par ' + HOLE_PARS[currentHole] : '') + (HOLE_SIS[currentHole] ? ' · SI ' + HOLE_SIS[currentHole] : '')"></span>
@@ -24,8 +24,8 @@
</template>
</div>
</div>
<button @click="view === 'score' ? nextHole() : cardView = (GAME_FORMAT === 'skins' ? 'skins' : 'points')" class="icon-btn"
:disabled="view === 'score' ? currentHole >= {{ game.holes_count }} : cardView !== 'strokes'"></button>
<button @click="view === 'score' ? nextHole() : cardNext()" class="icon-btn"
:disabled="view === 'score' ? currentHole >= {{ game.holes_count }} : cardView === (GAME_FORMAT === 'skins' || SIDE_SKINS_ENABLED ? 'skins' : 'points')"></button>
</div>
</div>
@@ -281,6 +281,7 @@ const SKINS_CARRYOVER = {{ carryover | tojson }};
const SKIN_VALUE = {{ skin_value | tojson }};
const SKIN_DOUBLE_BACK9 = {{ skin_double_back9 | tojson }};
const SKINS_NET = {{ skins_net | tojson }};
const SIDE_SKINS_ENABLED = {{ side_skins_enabled | tojson }};
const HOLE_PARS = {{ hole_pars | tojson }};
const HOLE_SIS = {{ hole_stroke_indices | tojson }};
const STROKES_PER_HOLE = {{ strokes_per_hole | tojson }};
@@ -457,6 +458,22 @@ function gameApp() {
prevHole() { if (this.currentHole > 1) { this.saveHole(this.currentHole); this.currentHole--; } },
nextHole() { if (this.currentHole < GAME_HOLES) { this.saveHole(this.currentHole); this.currentHole++; } },
cardTabs() {
if (GAME_FORMAT === 'skins') return ['strokes', 'skins'];
if (SIDE_SKINS_ENABLED) return ['strokes', 'points', 'skins'];
return ['strokes', 'points'];
},
cardNext() {
const tabs = this.cardTabs();
const idx = tabs.indexOf(this.cardView);
if (idx < tabs.length - 1) this.cardView = tabs[idx + 1];
},
cardPrev() {
const tabs = this.cardTabs();
const idx = tabs.indexOf(this.cardView);
if (idx > 0) this.cardView = tabs[idx - 1];
},
fmt(v) {
return Number.isInteger(v) || v % 1 === 0 ? v.toFixed(0) : v.toFixed(1);
},
@@ -548,8 +565,7 @@ function gameApp() {
if (this.view === 'score') {
if (dx < 0) this.nextHole(); else this.prevHole();
} else {
const secondary = GAME_FORMAT === 'skins' ? 'skins' : 'points';
this.cardView = dx < 0 ? secondary : 'strokes';
if (dx < 0) this.cardNext(); else this.cardPrev();
}
},
};