shalom
This commit is contained in:
@@ -34,6 +34,12 @@ python3 -m http.server 8080
|
||||
I posti con **jacuzzi** (Argo, Eolo, "Quanto te la rischi") sono i jackpot e
|
||||
vengono rivelati per ultimi, con celebrazione extra.
|
||||
|
||||
**Effetto SHALOM:** vincere una stanza con idromassaggio (Argo, Eolo, "Quanto te
|
||||
la rischi") fa partire un effetto a caso tra 4 — fiammate + leone infuocato, video
|
||||
a schermo intero con `shalom.mp3`, il combo (flashbang di fuoco poi video), oppure
|
||||
Gesù che scende dall'alto con luce evangelica e `gesu_song.mp3`. Tap per saltare.
|
||||
Asset in `image/`, `video/`, `sounds/`.
|
||||
|
||||
**Nessun match (~30%):** ogni tanto i 3 rulli si fermano su stanze diverse →
|
||||
effetto pazzo (sirena + strobo + glitch + timbro "NO MATCH"), le puntate del giro
|
||||
vengono annullate e la persona **ri-tira** (senza avanzare). Max 2 no-match di fila.
|
||||
|
||||
185
app.js
185
app.js
@@ -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,
|
||||
};
|
||||
})();
|
||||
|
||||
168
styles.css
168
styles.css
@@ -731,6 +731,174 @@ body {
|
||||
text-shadow: 0 0 16px var(--red);
|
||||
}
|
||||
|
||||
/* ===== Effetto SHALOM (vittoria stanze con idromassaggio) ===== */
|
||||
.shalom-overlay {
|
||||
position: fixed;
|
||||
inset: 0;
|
||||
z-index: 100;
|
||||
display: none;
|
||||
overflow: hidden;
|
||||
background: rgba(4, 2, 9, 0.94);
|
||||
}
|
||||
.shalom-overlay.on {
|
||||
display: block;
|
||||
animation: shalomBang 0.5s ease; /* flashbang bianco iniziale */
|
||||
}
|
||||
@keyframes shalomBang {
|
||||
0% { background: #fff; }
|
||||
35% { background: #fff; }
|
||||
100% { background: rgba(4, 2, 9, 0.94); }
|
||||
}
|
||||
.shalom-skip {
|
||||
position: absolute;
|
||||
bottom: 16px;
|
||||
right: 22px;
|
||||
z-index: 3;
|
||||
font-family: "Bebas Neue", sans-serif;
|
||||
letter-spacing: 2px;
|
||||
font-size: 16px;
|
||||
color: rgba(255, 255, 255, 0.55);
|
||||
}
|
||||
|
||||
/* fiammate dai due angoli in basso */
|
||||
.shalom-flames {
|
||||
position: absolute;
|
||||
bottom: -6%;
|
||||
width: 50vw;
|
||||
height: 92vh;
|
||||
pointer-events: none;
|
||||
opacity: 0;
|
||||
filter: blur(2px);
|
||||
mix-blend-mode: screen;
|
||||
background:
|
||||
radial-gradient(55% 42% at 50% 100%, rgba(255, 235, 140, 0.95), transparent 70%),
|
||||
linear-gradient(0deg, #ff2d00 0%, #ff7a00 26%, #ffd23f 48%, rgba(255, 210, 63, 0) 74%);
|
||||
}
|
||||
.shalom-flames.left {
|
||||
left: -8%;
|
||||
transform-origin: bottom left;
|
||||
}
|
||||
.shalom-flames.right {
|
||||
right: -8%;
|
||||
transform-origin: bottom right;
|
||||
}
|
||||
.shalom-overlay.mode-fire .shalom-flames {
|
||||
opacity: 1;
|
||||
animation: flames 0.26s infinite alternate;
|
||||
}
|
||||
.shalom-overlay.mode-fire .shalom-flames.right {
|
||||
animation-delay: 0.13s;
|
||||
}
|
||||
@keyframes flames {
|
||||
0% { transform: scaleY(1) scaleX(1) skewX(0deg); }
|
||||
100% { transform: scaleY(1.14) scaleX(0.92) skewX(4deg); }
|
||||
}
|
||||
|
||||
/* leone che irrompe */
|
||||
.shalom-lion {
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
width: min(46vw, 520px);
|
||||
border-radius: 16px;
|
||||
transform: translate(-50%, -50%) scale(0.2);
|
||||
opacity: 0;
|
||||
filter: drop-shadow(0 0 40px #ff6a00);
|
||||
}
|
||||
.shalom-overlay.mode-fire .shalom-lion {
|
||||
animation: lionSlam 0.6s cubic-bezier(0.2, 1.5, 0.4, 1) forwards,
|
||||
lionPulse 1.1s 0.6s infinite;
|
||||
}
|
||||
@keyframes lionSlam {
|
||||
0% { opacity: 0; transform: translate(-50%, -50%) scale(0.2) rotate(-7deg); }
|
||||
60% { opacity: 1; transform: translate(-50%, -50%) scale(1.12) rotate(2deg); }
|
||||
100% { opacity: 1; transform: translate(-50%, -50%) scale(1) rotate(0deg); }
|
||||
}
|
||||
@keyframes lionPulse {
|
||||
0%, 100% { filter: drop-shadow(0 0 40px #ff6a00); }
|
||||
50% { filter: drop-shadow(0 0 75px #ff2e00); }
|
||||
}
|
||||
|
||||
/* video a schermo intero */
|
||||
.shalom-video {
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
object-fit: cover;
|
||||
opacity: 0;
|
||||
}
|
||||
.shalom-overlay.mode-video .shalom-flames,
|
||||
.shalom-overlay.mode-video .shalom-lion {
|
||||
display: none;
|
||||
}
|
||||
.shalom-overlay.mode-video .shalom-video {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
/* effetto GESÙ: raggi di luce + immagine che scende dall'alto */
|
||||
.shalom-rays {
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
width: 180vmax;
|
||||
height: 180vmax;
|
||||
transform: translate(-50%, -50%);
|
||||
opacity: 0;
|
||||
pointer-events: none;
|
||||
mix-blend-mode: screen;
|
||||
background: repeating-conic-gradient(
|
||||
from 0deg,
|
||||
rgba(255, 240, 200, 0) 0deg,
|
||||
rgba(255, 240, 200, 0.3) 3.5deg,
|
||||
rgba(255, 240, 200, 0) 7deg
|
||||
);
|
||||
-webkit-mask: radial-gradient(closest-side, #000 26%, transparent 72%);
|
||||
mask: radial-gradient(closest-side, #000 26%, transparent 72%);
|
||||
}
|
||||
.shalom-overlay.mode-gesu .shalom-rays {
|
||||
opacity: 1;
|
||||
animation: shalomRays 26s linear infinite;
|
||||
}
|
||||
@keyframes shalomRays {
|
||||
to { transform: translate(-50%, -50%) rotate(360deg); }
|
||||
}
|
||||
.shalom-gesu {
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
width: min(42vw, 460px);
|
||||
border-radius: 14px;
|
||||
transform: translate(-50%, -160%);
|
||||
opacity: 0;
|
||||
filter: drop-shadow(0 0 55px rgba(255, 240, 180, 0.9));
|
||||
}
|
||||
.shalom-overlay.mode-gesu .shalom-gesu {
|
||||
animation: gesuDescend 1.7s cubic-bezier(0.16, 0.8, 0.3, 1) forwards,
|
||||
gesuFloat 4.5s 1.7s ease-in-out infinite;
|
||||
}
|
||||
@keyframes gesuDescend {
|
||||
0% { opacity: 0; transform: translate(-50%, -160%) scale(0.9); }
|
||||
55% { opacity: 1; }
|
||||
100% { opacity: 1; transform: translate(-50%, -50%) scale(1); }
|
||||
}
|
||||
@keyframes gesuFloat {
|
||||
0%, 100% { transform: translate(-50%, -50%) translateY(0); }
|
||||
50% { transform: translate(-50%, -50%) translateY(-14px); }
|
||||
}
|
||||
/* mostra solo gli elementi giusti per ciascuna modalità */
|
||||
.shalom-overlay.mode-fire .shalom-rays,
|
||||
.shalom-overlay.mode-fire .shalom-gesu,
|
||||
.shalom-overlay.mode-video .shalom-rays,
|
||||
.shalom-overlay.mode-video .shalom-gesu {
|
||||
display: none;
|
||||
}
|
||||
.shalom-overlay.mode-gesu .shalom-flames,
|
||||
.shalom-overlay.mode-gesu .shalom-lion,
|
||||
.shalom-overlay.mode-gesu .shalom-video {
|
||||
display: none;
|
||||
}
|
||||
|
||||
@media (max-width: 980px) {
|
||||
.stage {
|
||||
grid-template-columns: 1fr;
|
||||
|
||||
Reference in New Issue
Block a user