missing match crazy shit
This commit is contained in:
@@ -31,6 +31,10 @@ 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.
|
||||
|
||||
**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.
|
||||
|
||||
## Regole del matching
|
||||
|
||||
- Ogni stanza è **mono-genere** (M con M, F con F).
|
||||
|
||||
102
app.js
102
app.js
@@ -55,6 +55,22 @@
|
||||
buzz() {
|
||||
this.blip(140, 0.45, "sawtooth", 0.3, 70);
|
||||
},
|
||||
// sirena da allarme per il "nessun match"
|
||||
siren() {
|
||||
if (!this.on || !this.ctx) return;
|
||||
const t = this.ctx.currentTime;
|
||||
const o = this.ctx.createOscillator();
|
||||
const g = this.ctx.createGain();
|
||||
o.type = "sawtooth";
|
||||
const freqs = [880, 440, 880, 440, 880, 440, 880];
|
||||
freqs.forEach((f, i) => o.frequency.setValueAtTime(f, t + i * 0.16));
|
||||
g.gain.setValueAtTime(0.16, t);
|
||||
g.gain.setValueAtTime(0.16, t + 1.05);
|
||||
g.gain.exponentialRampToValueAtTime(0.0001, t + 1.2);
|
||||
o.connect(g).connect(this.master);
|
||||
o.start(t);
|
||||
o.stop(t + 1.25);
|
||||
},
|
||||
// nota di ottone per la fanfara
|
||||
brass(freq, when, dur, vol = 0.25) {
|
||||
if (!this.on || !this.ctx) return;
|
||||
@@ -198,6 +214,38 @@
|
||||
f.classList.add("go");
|
||||
}
|
||||
|
||||
// EFFETTO PAZZO quando i rulli non combaciano: sirena + strobo + glitch + timbro
|
||||
function crazyNoMatch() {
|
||||
Audio.siren();
|
||||
screenShake();
|
||||
|
||||
// strobo a schermo intero
|
||||
let strobe = $(".strobe");
|
||||
if (!strobe) {
|
||||
strobe = document.createElement("div");
|
||||
strobe.className = "strobe";
|
||||
document.body.appendChild(strobe);
|
||||
}
|
||||
strobe.classList.remove("go");
|
||||
void strobe.offsetWidth;
|
||||
strobe.classList.add("go");
|
||||
|
||||
// glitch sui rulli
|
||||
const win = $(".reel-window");
|
||||
win.classList.add("glitch");
|
||||
setTimeout(() => win.classList.remove("glitch"), 900);
|
||||
|
||||
// timbro "NESSUN MATCH"
|
||||
const stamp = document.createElement("div");
|
||||
stamp.className = "nomatch-stamp";
|
||||
stamp.innerHTML = "<span>❌ NO MATCH ❌</span>";
|
||||
document.body.appendChild(stamp);
|
||||
setTimeout(() => stamp.remove(), 1300);
|
||||
|
||||
// coriandoli "sbagliati" (solo rossi) per rincarare la beffa
|
||||
myConfetti({ particleCount: 120, spread: 180, startVelocity: 40, origin: { y: 0.4 }, colors: ["#ff3b5c", "#8b0022"] });
|
||||
}
|
||||
|
||||
/* ---------- Sfondo three.js: pioggia di monete ---------- */
|
||||
function initBackground() {
|
||||
if (typeof THREE === "undefined") return;
|
||||
@@ -332,8 +380,15 @@
|
||||
const roomAtIndex = (el, i) =>
|
||||
el.children[i] ? roomById[el.children[i].dataset.room] : null;
|
||||
|
||||
// Probabilità di ragebait (finto stop) sul rullo finale.
|
||||
// Probabilità di ragebait (finto stop) per colonna.
|
||||
const TEASE_CHANCE = 0.3;
|
||||
// Probabilità che il giro NON dia match (3 stanze diverse) -> respin.
|
||||
const NOMATCH_CHANCE = 0.3;
|
||||
|
||||
// 3 stanze DIVERSE per un "nessun match" (mai una linea vincente)
|
||||
function pickMismatch() {
|
||||
return Matching.shuffle(ROOMS.map((r) => r.id), Math.random).slice(0, 3);
|
||||
}
|
||||
|
||||
function bounceReel(el, finalY) {
|
||||
return el.animate(
|
||||
@@ -398,10 +453,11 @@
|
||||
// allineandosi sulla stanza assegnata. OGNI colonna può fare il ragebait
|
||||
// (in modo indipendente); le colonne più a destra tengono la suspense più a
|
||||
// lungo, così il dramma "cascata" verso l'ultimo rullo.
|
||||
async function spinReel(targetId) {
|
||||
const targetIndex = reelEls.map((el) => buildStripIn(el, targetId))[0]; // uguale per tutti
|
||||
async function spinReel(reelTargets, opts) {
|
||||
const tease = !opts || opts.tease !== false;
|
||||
// ogni rullo può atterrare su una stanza diversa (per il "nessun match")
|
||||
const targetIndex = reelEls.map((el, i) => buildStripIn(el, reelTargets[i]))[0];
|
||||
const finalY = -(targetIndex - 1) * ITEM_H;
|
||||
const targetRoom = roomById[targetId];
|
||||
$("#reel-result").innerHTML = " ";
|
||||
|
||||
// ZOOM + shake; lo shake si ferma a metà spin (quando inizia a rallentare)
|
||||
@@ -428,7 +484,7 @@
|
||||
await Promise.all(
|
||||
reelEls.map((el, i) => {
|
||||
// ogni colonna ha una sua chance di bluff; suspense crescente verso destra
|
||||
const teaseMs = Math.random() < TEASE_CHANCE ? 380 + i * 260 : 0;
|
||||
const teaseMs = tease && Math.random() < TEASE_CHANCE ? 380 + i * 260 : 0;
|
||||
return reelLand(el, targetIndex, finalY, durs[i], teaseMs);
|
||||
})
|
||||
);
|
||||
@@ -436,7 +492,6 @@
|
||||
ticking = false;
|
||||
stopRiser();
|
||||
zoomMachineSteady();
|
||||
$("#reel-result").innerHTML = `${targetRoom.jacuzzi ? "🛁 " : ""}<b>${targetRoom.nome}</b>${targetRoom.jacuzzi ? " 🛁" : ""}`;
|
||||
}
|
||||
|
||||
/* ---------- Zoom macchina ---------- */
|
||||
@@ -476,6 +531,7 @@
|
||||
let auto = false;
|
||||
let busy = false;
|
||||
let leverArmed = false;
|
||||
let noMatchStreak = 0; // no-match consecutivi (tetto per non incastrare)
|
||||
|
||||
// scommettitori (TU + banco simulato)
|
||||
let players = [];
|
||||
@@ -501,6 +557,7 @@
|
||||
ROOMS.forEach((r) => (occupants[r.id] = []));
|
||||
cursor = 0;
|
||||
currentBet = null;
|
||||
noMatchStreak = 0;
|
||||
if (!keepChips) {
|
||||
players = [
|
||||
{ name: "TU", chips: 100, you: true },
|
||||
@@ -686,7 +743,38 @@
|
||||
setTimeout(() => $("#lever").classList.remove("pull"), 320);
|
||||
if (window.__coinBoost) window.__coinBoost();
|
||||
|
||||
await spinReel(roomId);
|
||||
// 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;
|
||||
|
||||
if (noMatch) {
|
||||
noMatchStreak++;
|
||||
await spinReel(pickMismatch(), { tease: false });
|
||||
$("#reel-result").innerHTML = `<b class="rr-nomatch">NESSUN MATCH</b>`;
|
||||
crazyNoMatch();
|
||||
currentBet = null; // puntate annullate (push): nessun gettone cambia
|
||||
log(`${person}: nessun match! Si ri-tira 💥`);
|
||||
mc("💥 NESSUN MATCH! Ri-tira la leva! ⬇️");
|
||||
setTimeout(zoomMachineOut, 1000);
|
||||
busy = false;
|
||||
// stesso giocatore: NON avanza il cursore
|
||||
if (auto) {
|
||||
setTimeout(nextTurn, 1600);
|
||||
} else {
|
||||
setTimeout(() => {
|
||||
if (!busy && cursor < revealQueue.length && !auto) {
|
||||
armLever(true);
|
||||
mc("💥 NESSUN MATCH! Ri-tira la leva! ⬇️");
|
||||
}
|
||||
}, 1100);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// --- MATCH: i 3 rulli si allineano sulla stanza assegnata ---
|
||||
noMatchStreak = 0;
|
||||
await spinReel([roomId, roomId, roomId], { tease: true });
|
||||
$("#reel-result").innerHTML = `${room.jacuzzi ? "🛁 " : ""}<b>${room.nome}</b>${room.jacuzzi ? " 🛁" : ""}`;
|
||||
|
||||
// atterraggio
|
||||
occupants[roomId].push(person);
|
||||
|
||||
62
styles.css
62
styles.css
@@ -668,6 +668,68 @@ body {
|
||||
}
|
||||
}
|
||||
|
||||
/* ===== NESSUN MATCH: effetto pazzo ===== */
|
||||
.strobe {
|
||||
position: fixed;
|
||||
inset: 0;
|
||||
z-index: 58;
|
||||
pointer-events: none;
|
||||
opacity: 0;
|
||||
}
|
||||
.strobe.go {
|
||||
animation: strobe 1s linear;
|
||||
}
|
||||
@keyframes strobe {
|
||||
0%, 100% { opacity: 0; }
|
||||
8% { opacity: 1; background: rgba(255, 0, 60, 0.45); }
|
||||
16% { opacity: 1; background: rgba(0, 229, 255, 0.4); }
|
||||
24% { opacity: 1; background: rgba(255, 210, 63, 0.4); }
|
||||
40% { opacity: 1; background: rgba(255, 0, 60, 0.4); }
|
||||
56% { opacity: 1; background: rgba(0, 229, 255, 0.38); }
|
||||
72% { opacity: 1; background: rgba(255, 46, 136, 0.4); }
|
||||
88% { opacity: 1; background: rgba(255, 0, 60, 0.35); }
|
||||
}
|
||||
.reel-window.glitch {
|
||||
animation: glitch 0.08s steps(2) infinite;
|
||||
}
|
||||
@keyframes glitch {
|
||||
0% { transform: translate(0, 0) skewX(0); filter: hue-rotate(0deg); }
|
||||
25% { transform: translate(-7px, 3px) skewX(4deg); filter: hue-rotate(90deg); }
|
||||
50% { transform: translate(6px, -4px) skewX(-4deg); filter: hue-rotate(200deg); }
|
||||
75% { transform: translate(-5px, 5px) skewX(3deg); filter: hue-rotate(300deg); }
|
||||
100% { transform: translate(0, 0) skewX(0); filter: hue-rotate(0deg); }
|
||||
}
|
||||
.nomatch-stamp {
|
||||
position: fixed;
|
||||
inset: 0;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
z-index: 59;
|
||||
pointer-events: none;
|
||||
}
|
||||
.nomatch-stamp span {
|
||||
font-family: "Anton", sans-serif;
|
||||
font-size: clamp(44px, 10vw, 140px);
|
||||
letter-spacing: 3px;
|
||||
color: var(--red);
|
||||
text-shadow: 0 0 30px var(--red), 0 0 70px #ff004c;
|
||||
transform: rotate(-8deg) scale(0.3);
|
||||
opacity: 0;
|
||||
animation: stampIn 1.3s ease;
|
||||
}
|
||||
@keyframes stampIn {
|
||||
0% { opacity: 0; transform: rotate(-8deg) scale(0.3); }
|
||||
15% { opacity: 1; transform: rotate(-8deg) scale(1.15); }
|
||||
28% { transform: rotate(-8deg) scale(1); }
|
||||
82% { opacity: 1; transform: rotate(-8deg) scale(1); }
|
||||
100% { opacity: 0; transform: rotate(-8deg) scale(1.05); }
|
||||
}
|
||||
.rr-nomatch {
|
||||
color: var(--red);
|
||||
text-shadow: 0 0 16px var(--red);
|
||||
}
|
||||
|
||||
@media (max-width: 980px) {
|
||||
.stage {
|
||||
grid-template-columns: 1fr;
|
||||
|
||||
Reference in New Issue
Block a user