new ragebait 3 col
This commit is contained in:
100
app.js
100
app.js
@@ -346,8 +346,46 @@
|
||||
).finished;
|
||||
}
|
||||
|
||||
// rullo che atterra dritto sulla target
|
||||
async function reelLand(el, finalY, duration) {
|
||||
// Atterraggio di un rullo sulla target. Se teaseMs > 0 fa il RAGEBAIT:
|
||||
// si ferma su una stanza adiacente sbagliata, tiene la suspense (teaseMs),
|
||||
// poi scatta sulla vera. Funziona su QUALSIASI colonna.
|
||||
async function reelLand(el, targetIndex, finalY, duration, teaseMs) {
|
||||
// eventuale bluff: scegli una stanza adiacente diversa dalla vera
|
||||
if (teaseMs > 0) {
|
||||
const targetRoom = roomAtIndex(el, targetIndex);
|
||||
let dir = Math.random() < 0.5 ? -1 : 1;
|
||||
if (roomAtIndex(el, targetIndex + dir)?.id === targetRoom.id) dir = -dir;
|
||||
if (roomAtIndex(el, targetIndex + dir)?.id === targetRoom.id) teaseMs = 0; // no adiacente
|
||||
if (teaseMs > 0) {
|
||||
const fakeRoom = roomAtIndex(el, targetIndex + dir);
|
||||
const fakeY = -(targetIndex + dir - 1) * ITEM_H;
|
||||
const col = el.parentElement;
|
||||
|
||||
// decelera e "si ferma" sulla stanza SBAGLIATA
|
||||
await el.animate(
|
||||
[{ transform: "translateY(0)" }, { transform: `translateY(${fakeY}px)` }],
|
||||
{ duration, easing: "cubic-bezier(0.08, 0.85, 0.14, 1)", fill: "forwards" }
|
||||
).finished;
|
||||
Audio.reelStop();
|
||||
|
||||
// suspense su quella colonna
|
||||
Audio.blip(330, 0.13, "sine", 0.14);
|
||||
col.classList.add("teasing");
|
||||
mc(`${fakeRoom.nome}...? 🤨`);
|
||||
await sleep(teaseMs);
|
||||
|
||||
// SCATTO alla stanza vera
|
||||
Audio.blip(200, 0.12, "sawtooth", 0.2, 90); // whoosh
|
||||
await el.animate(
|
||||
[{ transform: `translateY(${fakeY}px)` }, { transform: `translateY(${finalY}px)` }],
|
||||
{ duration: 480, easing: "cubic-bezier(0.34, 1.56, 0.64, 1)", fill: "forwards" }
|
||||
).finished;
|
||||
col.classList.remove("teasing");
|
||||
Audio.reelStop();
|
||||
return;
|
||||
}
|
||||
}
|
||||
// atterraggio diretto + micro-assestamento
|
||||
await el.animate(
|
||||
[{ transform: "translateY(0)" }, { transform: `translateY(${finalY}px)` }],
|
||||
{ duration, easing: "cubic-bezier(0.12, 0.85, 0.18, 1)", fill: "forwards" }
|
||||
@@ -356,42 +394,10 @@
|
||||
Audio.reelStop();
|
||||
}
|
||||
|
||||
// rullo con RAGEBAIT: si ferma sulla stanza sbagliata, suspense, poi scatta
|
||||
async function reelLandTease(el, targetIndex, finalY) {
|
||||
const targetRoom = roomAtIndex(el, targetIndex);
|
||||
let dir = Math.random() < 0.5 ? -1 : 1;
|
||||
if (roomAtIndex(el, targetIndex + dir)?.id === targetRoom.id) dir = -dir;
|
||||
if (roomAtIndex(el, targetIndex + dir)?.id === targetRoom.id)
|
||||
return reelLand(el, finalY, 4200); // niente adiacente valida
|
||||
const fakeRoom = roomAtIndex(el, targetIndex + dir);
|
||||
const fakeY = -(targetIndex + dir - 1) * ITEM_H;
|
||||
|
||||
// FASE 1: decelera e "si ferma" sulla stanza SBAGLIATA
|
||||
await el.animate(
|
||||
[{ transform: "translateY(0)" }, { transform: `translateY(${fakeY}px)` }],
|
||||
{ duration: 3600, easing: "cubic-bezier(0.08, 0.85, 0.14, 1)", fill: "forwards" }
|
||||
).finished;
|
||||
Audio.reelStop();
|
||||
|
||||
// FASE 2: gaslighting — sembra fatta
|
||||
Audio.blip(320, 0.18, "sine", 0.16);
|
||||
const win = $(".reel-window");
|
||||
win.classList.add("teasing");
|
||||
mc(`Ferma su ${fakeRoom.nome}...? 🤨`);
|
||||
await sleep(750);
|
||||
|
||||
// FASE 3: SCATTO alla stanza vera
|
||||
Audio.blip(200, 0.14, "sawtooth", 0.22, 90); // whoosh
|
||||
await el.animate(
|
||||
[{ transform: `translateY(${fakeY}px)` }, { transform: `translateY(${finalY}px)` }],
|
||||
{ duration: 520, easing: "cubic-bezier(0.34, 1.56, 0.64, 1)", fill: "forwards" }
|
||||
).finished;
|
||||
win.classList.remove("teasing");
|
||||
Audio.reelStop();
|
||||
}
|
||||
|
||||
// GIRO: i 3 rulli partono insieme e si fermano da sinistra a destra,
|
||||
// allineandosi tutti sulla stanza assegnata (l'ultimo può fare il ragebait).
|
||||
// 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
|
||||
const finalY = -(targetIndex - 1) * ITEM_H;
|
||||
@@ -403,7 +409,7 @@
|
||||
const mainDur = 4200;
|
||||
setTimeout(zoomMachineSteady, mainDur / 2);
|
||||
|
||||
const stopRiser = Audio.riser(5.0);
|
||||
const stopRiser = Audio.riser(5.6);
|
||||
|
||||
// tick sincronizzati sul 3° rullo (gira più a lungo)
|
||||
let ticking = true;
|
||||
@@ -418,16 +424,14 @@
|
||||
requestAnimationFrame(loop);
|
||||
})();
|
||||
|
||||
const wantTease = Math.random() < TEASE_CHANCE;
|
||||
|
||||
// stop staggerato sinistra -> destra
|
||||
await Promise.all([
|
||||
reelLand(reelEls[0], finalY, 2600),
|
||||
reelLand(reelEls[1], finalY, 3300),
|
||||
wantTease
|
||||
? reelLandTease(reelEls[2], targetIndex, finalY)
|
||||
: reelLand(reelEls[2], finalY, 4200),
|
||||
]);
|
||||
const durs = [2600, 3300, 4200];
|
||||
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;
|
||||
return reelLand(el, targetIndex, finalY, durs[i], teaseMs);
|
||||
})
|
||||
);
|
||||
|
||||
ticking = false;
|
||||
stopRiser();
|
||||
|
||||
Reference in New Issue
Block a user