This commit is contained in:
2026-07-02 15:29:24 +02:00
parent efe2ef2a76
commit 7dcbc9582d
3 changed files with 356 additions and 3 deletions

185
app.js
View File

@@ -2,6 +2,17 @@
(function () {
"use strict";
/* ===== DEBUG / ANTEPRIMA EFFETTI =====================================
Per vedere gli effetti senza aspettare le vincite jacuzzi casuali.
Rimetti entrambi a null/false per il gioco normale. */
// Forza un effetto shalom fisso: "fire" | "video" | "combo" | "gesu" | null
const FORCE_SHALOM = null;
// Forza il risultato del giro su una stanza con idromassaggio (così l'effetto
// parte a ogni giro). true = jacuzzi a caso; oppure l'id di una stanza
// ("argo" | "eolo" | "rischio" | "zeus" | ...); false = risultato normale.
const FORCE_RESULT = false;
/* ==================================================================== */
const $ = (sel) => document.querySelector(sel);
const roomById = Object.fromEntries(ROOMS.map((r) => [r.id, r]));
const genderOf = Object.fromEntries(
@@ -161,6 +172,8 @@
win: "sounds/win.mp3", // jingle di vincita (match normale)
jackpot: "sounds/jackpot.mp3", // sirena di jackpot (idromassaggio)
coins: "sounds/coins.mp3", // pioggia di monete
shalom: "sounds/shalom.mp3", // musica dell'effetto "shalom" (vittoria jacuzzi)
gesuSong: "sounds/gesu_song.mp3", // canzone dell'effetto "Gesù"
},
el: {},
ready: {},
@@ -375,6 +388,146 @@
});
}
/* ---------- Effetto SHALOM (vittoria stanze con idromassaggio) ---------- */
let shalomOverlay = null;
function ensureShalomOverlay() {
if (shalomOverlay) return shalomOverlay;
const o = document.createElement("div");
o.className = "shalom-overlay";
o.innerHTML =
'<div class="shalom-flames left"></div>' +
'<div class="shalom-flames right"></div>' +
'<img class="shalom-lion" src="image/lion.jpg" alt="" />' +
'<div class="shalom-rays"></div>' +
'<img class="shalom-gesu" src="image/gesu.jpeg" alt="" />' +
'<video class="shalom-video" src="video/videoplayback.mp4" muted playsinline preload="auto"></video>' +
'<div class="shalom-skip">tap per saltare ▸</div>';
document.body.appendChild(o);
shalomOverlay = o;
return o;
}
function showShalom(o, mode) {
o.classList.remove("mode-fire", "mode-video");
o.classList.add("on", mode);
}
function hideShalom(o) {
const v = o.querySelector(".shalom-video");
try {
v.pause();
} catch (e) {}
o.classList.remove("on", "mode-fire", "mode-video");
}
// Attende: fine video, tetto di tempo, oppure tap sull'overlay (skip).
function shalomWait(v, capMs, o) {
return new Promise((resolve) => {
let done = false;
const finish = () => {
if (done) return;
done = true;
clearTimeout(to);
if (v) v.removeEventListener("ended", finish);
o.removeEventListener("pointerdown", finish);
resolve();
};
const to = setTimeout(finish, capMs);
if (v) v.addEventListener("ended", finish);
o.addEventListener("pointerdown", finish);
});
}
// EFFETTO 1: fiammate dai due angoli in basso + leone (flashbang) ~3.5s
async function shalomFireLion() {
const o = ensureShalomOverlay();
showShalom(o, "mode-fire");
screenShake();
const stop = Samples.play("shalom", {
vol: 0.9,
fallback: () => Audio.jackpot(),
});
await shalomWait(null, 3500, o);
if (stop) stop(0.4);
hideShalom(o);
}
// EFFETTO 2: video a schermo intero con la musica shalom
async function shalomVideoOnly() {
const o = ensureShalomOverlay();
const v = o.querySelector(".shalom-video");
showShalom(o, "mode-video");
const stop = Samples.play("shalom", {
vol: 0.9,
fallback: () => Audio.jackpot(),
});
try {
v.currentTime = 0;
await v.play();
} catch (e) {}
await shalomWait(v, 14000, o);
if (stop) stop(0.4);
hideShalom(o);
}
// EFFETTO 3: combo — flashbang fuoco+leone 3s, poi parte il video con la musica
async function shalomCombo() {
const o = ensureShalomOverlay();
showShalom(o, "mode-fire");
screenShake();
await shalomWait(null, 3000, o);
const v = o.querySelector(".shalom-video");
o.classList.remove("mode-fire");
o.classList.add("mode-video");
const stop = Samples.play("shalom", {
vol: 0.9,
fallback: () => Audio.jackpot(),
});
try {
v.currentTime = 0;
await v.play();
} catch (e) {}
await shalomWait(v, 14000, o);
if (stop) stop(0.4);
hideShalom(o);
}
// EFFETTO 4: Gesù scende dall'alto con luce evangelica + canzone
async function shalomGesu() {
const o = ensureShalomOverlay();
showShalom(o, "mode-gesu");
const stop = Samples.play("gesuSong", {
vol: 0.9,
fallback: () => Audio.fanfare(),
});
await shalomWait(null, 14000, o);
if (stop) stop(0.5);
hideShalom(o);
}
const SHALOM_EFFECTS = [
shalomFireLion,
shalomVideoOnly,
shalomCombo,
shalomGesu,
];
const SHALOM_BY_NAME = {
fire: shalomFireLion,
video: shalomVideoOnly,
combo: shalomCombo,
gesu: shalomGesu,
};
// "sacchetto" mescolato: a ogni vittoria jacuzzi esce un effetto DIVERSO
// (senza ripetizioni) finché non sono usciti tutti, poi si rimescola.
let shalomBag = [];
function playShalom() {
if (FORCE_SHALOM && SHALOM_BY_NAME[FORCE_SHALOM]) {
return SHALOM_BY_NAME[FORCE_SHALOM](); // effetto fisso (debug)
}
if (shalomBag.length === 0) {
shalomBag = Matching.shuffle(SHALOM_EFFECTS.slice(), Math.random);
}
const fx = shalomBag.shift();
return fx();
}
/* ---------- Sfondo three.js: pioggia di monete ---------- */
function initBackground() {
if (typeof THREE === "undefined") return;
@@ -744,7 +897,10 @@
function buildRevealOrder() {
// ordine completamente casuale: ogni persona (e quindi ogni stanza, anche
// quelle con idromassaggio) può uscire in qualsiasi momento
return Matching.shuffle(PARTICIPANTS.map((p) => p.nome), Math.random);
return Matching.shuffle(
PARTICIPANTS.map((p) => p.nome),
Math.random,
);
}
function newGame(keepChips) {
@@ -755,6 +911,7 @@
cursor = 0;
currentBet = null;
noMatchStreak = 0;
shalomBag = [];
if (!keepChips) {
players = [
{ name: "TU", chips: 100, you: true },
@@ -932,7 +1089,15 @@
Audio.resume();
const person = revealQueue[cursor];
const roomId = assignment.byPerson[person];
let roomId = assignment.byPerson[person];
if (FORCE_RESULT) {
// debug: forza il risultato su una jacuzzi (o su un id specifico)
const jac = ROOMS.filter((r) => r.jacuzzi);
roomId =
typeof FORCE_RESULT === "string"
? FORCE_RESULT
: jac[Math.floor(Math.random() * jac.length)].id;
}
const room = roomById[roomId];
$("#player-name").textContent = person;
@@ -952,7 +1117,8 @@
// 30% dei giri: NESSUN MATCH (3 stanze diverse) -> effetto pazzo + respin.
// Tetto di 2 no-match di fila per non bloccare nessuno.
const noMatch = noMatchStreak < 2 && Math.random() < NOMATCH_CHANCE;
const noMatch =
!FORCE_RESULT && noMatchStreak < 2 && Math.random() < NOMATCH_CHANCE;
if (noMatch) {
noMatchStreak++;
@@ -1021,6 +1187,12 @@
// la macchina resta zoomata per godersi la vittoria, poi torna al posto
setTimeout(zoomMachineOut, isJackpot ? 1200 : 800);
// EFFETTO SHALOM: vittoria di una stanza con idromassaggio -> effetto a caso
if (isJackpot) {
await sleep(600); // lascia vedere un attimo i coriandoli
await playShalom();
}
cursor++;
setProgress();
renderLeaderboard();
@@ -1187,4 +1359,11 @@
// hook di debug (per test/verifica; innocuo)
window.__celebrate = celebrate;
window.__shalom = {
fire: shalomFireLion,
video: shalomVideoOnly,
combo: shalomCombo,
gesu: shalomGesu,
random: playShalom,
};
})();