From ce1a0ab8730fc4ba111b82daf4632c36e63e29a5 Mon Sep 17 00:00:00 2001 From: CI Action Date: Wed, 14 Jan 2026 12:26:56 +0000 Subject: [PATCH] chore: update asset versions [skip ci] --- index.html | 8 +- logo.78f51359.png | Bin 0 -> 6286 bytes script.d5454706.js | 468 ++++++++++++++++++++++++++++++++++++++++++++ styles.26a5b74f.css | 455 ++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 927 insertions(+), 4 deletions(-) create mode 100644 logo.78f51359.png create mode 100644 script.d5454706.js create mode 100644 styles.26a5b74f.css diff --git a/index.html b/index.html index 6875738..74489f2 100644 --- a/index.html +++ b/index.html @@ -7,8 +7,8 @@ - - + + @@ -38,7 +38,7 @@
- +

Juego del Impostor

ΒΏPodrΓ‘s descubrir quiΓ©n es el impostor?

@@ -186,7 +186,7 @@
- + diff --git a/logo.78f51359.png b/logo.78f51359.png new file mode 100644 index 0000000000000000000000000000000000000000..61cc5bf930b3f56101360075ccc05cbc47ddfee4 GIT binary patch literal 6286 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 e8N??>#zbZQ&j$JYr2>HgGk3 literal 0 HcmV?d00001 diff --git a/script.d5454706.js b/script.d5454706.js new file mode 100644 index 0000000..8e41f46 --- /dev/null +++ b/script.d5454706.js @@ -0,0 +1,468 @@ +const STORAGE_KEY = 'impostorGameStateV2'; +const MAX_PLAYERS = 10; +const MIN_PLAYERS = 3; +const POOLS_CACHE_KEY = 'impostorWordPoolsV1'; +const POOLS_MANIFEST_URL = 'word-pools/manifest.json'; + +const EMBEDDED_POOLS = [ + { id: 'animales_naturaleza', name: 'Animales y naturaleza', emoji: '🌿', words: ['Perro','Gato','Lobo','Zorro','Oso','Tigre','LeΓ³n','Pantera','Jaguar','Puma','Guepardo','Elefante','Rinoceronte','HipopΓ³tamo','Jirafa','Cebra','Camello','Dromedario','Canguro','Koala','Panda','Mapache','Nutria','Castor','Foca','Morsa','DelfΓ­n','Ballena','TiburΓ³n','Orca','Pulpo','Calamar','Medusa','Tortuga','Lagarto','Cocodrilo','Serpiente','Anaconda','Iguana','Rana','Sapo','BΓΊho','HalcΓ³n','Águila','CΓ³ndor','Gaviota','Loro','Flamenco','PingΓΌino','Avestruz','Gallina','Pato','Ganso','Cisne','Abeja','Hormiga','Mariquita','LibΓ©lula','Mariposa','Escarabajo','Grillo','Saltamontes','AraΓ±a','EscorpiΓ³n','Lombriz','Caracol','Estrella de mar','Coral','Musgo','Helecho','Pino','Roble','Encina','Palmera','Cactus','BambΓΊ','Rosa','TulipΓ‘n','Girasol','Lavanda','MontaΓ±a','RΓ­o','Lago','Mar','Playa','Desierto','Selva','Bosque','Pradera','Glaciar','VolcΓ‘n'] }, + { id: 'vida_cotidiana', name: 'Vida cotidiana', emoji: '🏠', words: ['Pan','Leche','CafΓ©','TΓ©','Agua','Jugo','Refresco','Cerveza','Vino','Pizza','Hamburguesa','SΓ‘ndwich','Taco','Burrito','Pasta','Arroz','Paella','Sushi','Ramen','Ensalada','Sopa','Croqueta','Tortilla','Empanada','Arepa','Queso','JamΓ³n','Chorizo','Pollo','Carne','Cerdo','Pescado','Marisco','Patata','Tomate','Cebolla','Ajo','Pimiento','Zanahoria','Lechuga','BrΓ³coli','Coliflor','Manzana','PlΓ‘tano','Naranja','Pera','Uva','Fresa','Mango','PiΓ±a','MelΓ³n','SandΓ­a','Yogur','Galletas','Chocolate','Helado','Cereales','Mantequilla','Aceite','Sal','Pimienta','AzΓΊcar','Harina','Huevo','Cuchara','Tenedor','Cuchillo','Plato','Vaso','Taza','Olla','SartΓ©n','Microondas','Horno','Nevera','Mesa','Silla','SofΓ‘','Cama','Almohada','SΓ‘bana','Toalla','Ducha','JabΓ³n','ChampΓΊ','Cepillo','Pasta de dientes'] }, + { id: 'deportes', name: 'Deportes', emoji: 'πŸ…', words: ['FΓΊtbol','Baloncesto','Tenis','PΓ‘del','BΓ‘dminton','Voleibol','BΓ©isbol','Rugby','Hockey hielo','Hockey cΓ©sped','Golf','Boxeo','MMA','Judo','Karate','Taekwondo','Esgrima','Tiro con arco','Halterofilia','Crossfit','Atletismo','MaratΓ³n','TriatlΓ³n','Ciclismo ruta','Ciclismo montaΓ±a','BMX','NataciΓ³n','Waterpolo','Surf','Vela','Remo','PiragΓΌismo','EsquΓ­','Snowboard','Patinaje artΓ­stico','Patinaje velocidad','Curling','Escalada','Senderismo','Trail running','Parkour','Gimnasia artΓ­stica','Gimnasia rΓ­tmica','TrampolΓ­n','Skate','Breakdance','Carreras coches','FΓ³rmula 1','Rally','Karting','Motociclismo','Enduro','Motocross','EquitaciΓ³n','Polo','CrΓ­quet','Billar','Dardos','Petanca','Pickleball','Ultimate frisbee','Paintball','Airsoft','eSports'] }, + { id: 'marcas', name: 'Marcas', emoji: 'πŸ›οΈ', words: ['Apple','Samsung','Google','Microsoft','Amazon','Meta','Tesla','Toyota','Honda','Ford','BMW','Mercedes','Audi','Volkswagen','Porsche','Ferrari','Lamborghini','Maserati','McLaren','Chevrolet','Nissan','Kia','Hyundai','Peugeot','Renault','Volvo','Jaguar','Land Rover','Fiat','Alfa Romeo','Ducati','Yamaha','Canon','Nikon','Sony','Panasonic','LG','Philips','Siemens','Bosch','Whirlpool','Ikea','Zara','H&M','Uniqlo','Nike','Adidas','Puma','Reebok','New Balance','Under Armour','Converse','Vans','Patagonia','The North Face','Columbia','Levi’s','Calvin Klein','Gucci','Prada','Louis Vuitton','Chanel','HermΓ¨s','Dior','Rolex','Omega','Casio','Pepsi','Coca-Cola','Fanta','Red Bull','Monster','Starbucks','Nespresso','NestlΓ©','Danone','Kellogg’s','Oreo','Intel','AMD','Nvidia','Qualcomm','TikTok','Netflix','Disney','Warner Bros','HBO','Spotify','Airbnb','Uber','Booking'] }, + { id: 'musica', name: 'MΓΊsica', emoji: '🎡', words: ['Guitarra','Piano','ViolΓ­n','BaterΓ­a','Bajo','SaxofΓ³n','Trompeta','Flauta','Clarinete','AcordeΓ³n','Ukelele','Arpa','Sintetizador','DJ','MicrΓ³fono','Altavoz','Concierto','Festival','Vinilo','Rock','Pop','Punk','Metal','Heavy','Thrash','Death metal','Jazz','Blues','Soul','Funk','R&B','Rap','Hip hop','Trap','ReggaetΓ³n','Salsa','Bachata','Merengue','Cumbia','Vallenato','Flamenco','Rumba','Bossa nova','Samba','Tango','Country','EDM','Techno','House','Trance','Dubstep','Drum and bass','Lo-fi','Reggae','Ska','K-pop','J-pop','Indie','Gospel','Γ“pera','SinfonΓ­a','Orquesta','Coro','Cantautor','Balada','Bolero','Ranchera','Corrido','Mariachi'] }, + { id: 'personajes', name: 'Personajes', emoji: 'πŸ§™', words: ['Sherlock Holmes','Harry Potter','Hermione Granger','Ron Weasley','Albus Dumbledore','Voldemort','Frodo BolsΓ³n','Sam Gamyi','Gandalf','Aragorn','Legolas','Gimli','Gollum','Bilbo BolsΓ³n','Katniss Everdeen','Peeta Mellark','Batman','Bruce Wayne','Joker','Harley Quinn','Superman','Clark Kent','Lois Lane','Wonder Woman','Diana Prince','Flash','Barry Allen','Aquaman','Arthur Curry','Spider-Man','Peter Parker','Iron Man','Tony Stark','CapitΓ‘n AmΓ©rica','Steve Rogers','Black Widow','Natasha Romanoff','Hulk','Bruce Banner','Thor','Loki','Thanos','Doctor Strange','Wanda Maximoff','Vision','Star-Lord','Gamora','Groot','Rocket','Drax','Deadpool','Wolverine','Magneto','Professor X','Storm','Cyclops','Jean Grey','Mystique','Darth Vader','Luke Skywalker','Leia Organa','Han Solo','Chewbacca','Yoda','Obi-Wan Kenobi','Anakin Skywalker','Rey','Kylo Ren','R2-D2','C-3PO','Indiana Jones','Lara Croft','James Bond','Mario','Luigi','Princesa Peach','Bowser','Link','Zelda','Geralt de Rivia','Ciri','Yennefer','Kratos','Atreus','Ellie','Joel Miller','Nathan Drake','Master Chief','Cortana','Sonic','Tails','Ash Ketchum','Pikachu','Goku','Vegeta','Naruto','Sasuke','Luffy','Zoro','Nami','Tanjiro','Nezuko','Saitama','Light Yagami','L Lawliet'] } +]; + +let availablePools = []; +let poolsCache = {}; + +let state = { + phase: 'setup', + numPlayers: 6, + numImpostors: 1, + gameTime: 180, + deliberationTime: 60, + playerNames: [], + roles: [], + civilianWord: '', + impostorWord: '', + currentReveal: 0, + startPlayer: 0, + turnDirection: 'horario', + revealOrder: [], + timerEndAt: null, + timerPhase: null, + votes: {}, + votingPlayer: 0, + selections: [], + executed: [], + selectedPool: 'animales_naturaleza', + votingPool: null, + isTiebreak: false, + tiebreakCandidates: [] +}; + +const saveState = () => localStorage.setItem(STORAGE_KEY, JSON.stringify(state)); +const loadState = () => { + const raw = localStorage.getItem(STORAGE_KEY); + if (!raw) return false; + try { state = JSON.parse(raw); return true; } catch { return false; } +}; +const clearState = () => localStorage.removeItem(STORAGE_KEY); + +const loadPoolsCache = () => { + try { poolsCache = JSON.parse(localStorage.getItem(POOLS_CACHE_KEY) || '{}'); } catch { poolsCache = {}; } +}; +const savePoolsCache = () => localStorage.setItem(POOLS_CACHE_KEY, JSON.stringify(poolsCache)); + +// ---------- Defaults ---------- +function defaultImpostors(nPlayers) { + const capped = Math.min(Math.max(nPlayers, MIN_PLAYERS), MAX_PLAYERS); + let impostors = 1; + if (capped > 7) impostors = 3; + else if (capped > 5) impostors = 2; + const halfCap = Math.max(1, Math.floor(capped / 2)); + return Math.min(impostors, halfCap); +} + +function defaultGameTime(nPlayers) { + const capped = Math.min(Math.max(nPlayers, MIN_PLAYERS), MAX_PLAYERS); + if (capped <= 4) return 300; + if (capped >= 10) return 900; + const extraPlayers = capped - 4; + const seconds = 300 + extraPlayers * 100; + return Math.round(seconds / 30) * 30; +} + +function defaultDeliberation(gameSeconds) { + return Math.max(30, Math.round(gameSeconds / 3)); +} + +// ---------- Pools ---------- +async function loadPoolsList() { + loadPoolsCache(); + let list = []; + try { + const res = await fetch(POOLS_MANIFEST_URL); + if (res.ok) list = await res.json(); + } catch (_) {} + if (!Array.isArray(list) || list.length === 0) { + list = EMBEDDED_POOLS.map(p => ({ id: p.id, name: p.name, emoji: p.emoji, count: p.words.length })); + } + availablePools = list; + renderPoolButtons(); +} + +function parseWordsFile(text) { + const lines = text.split(/\r?\n/).map(l => l.trim()).filter(Boolean); + if (!lines.length) return []; + if (lines[0].startsWith('#')) return lines.slice(1); + return lines; +} + +async function pickWords() { + const poolId = state.selectedPool || 'default'; + let words = []; + if (poolsCache[poolId]?.words) { + words = poolsCache[poolId].words; + } else if (poolId !== 'default') { + const res = await fetch(`word-pools/${poolId}.txt`); + if (!res.ok) throw new Error('No se pudo cargar el pool'); + const text = await res.text(); + words = parseWordsFile(text); + poolsCache[poolId] = { words, ts: Date.now() }; savePoolsCache(); + } else { + words = EMBEDDED_POOLS[0].words; + } + const shuffled = [...words].sort(() => Math.random() - 0.5); + return { civilian: shuffled[0], impostor: shuffled[1] }; +} + +function renderPoolButtons() { + const container = document.getElementById('pool-buttons'); + if (!container) return; + container.innerHTML = ''; + availablePools.forEach(pool => { + const btn = document.createElement('button'); + btn.type = 'button'; + btn.className = 'pool-btn'; + btn.textContent = `${pool.emoji || '🎲'} ${pool.name || pool.id}`; + if (state.selectedPool === pool.id) btn.classList.add('selected'); + btn.onclick = () => { state.selectedPool = pool.id; saveState(); renderPoolButtons(); }; + container.appendChild(btn); + }); +} + +// ---------- ConfiguraciΓ³n y nombres ---------- +function goToNames() { + let nPlayers = parseInt(document.getElementById('num-players').value) || MIN_PLAYERS; + nPlayers = Math.min(Math.max(nPlayers, MIN_PLAYERS), MAX_PLAYERS); + const maxImpostors = Math.max(1, Math.floor(nPlayers / 2)); + let nImpostors = parseInt(document.getElementById('num-impostors').value) || defaultImpostors(nPlayers); + nImpostors = Math.min(Math.max(1, nImpostors), maxImpostors); + let gTime = parseInt(document.getElementById('game-time').value) || defaultGameTime(nPlayers); + gTime = Math.min(Math.max(gTime, 60), 900); + let dTime = parseInt(document.getElementById('deliberation-time').value) || defaultDeliberation(gTime); + dTime = Math.min(Math.max(dTime, 30), Math.round(900 / 3)); + if (nImpostors >= nPlayers) { alert('Impostores debe ser menor que jugadores'); return; } + state.numPlayers = nPlayers; state.numImpostors = nImpostors; state.gameTime = gTime; state.deliberationTime = dTime; + buildNameInputs(); + showScreen('names-screen'); +} + +function buildNameInputs() { + const list = document.getElementById('player-names-list'); + list.innerHTML = ''; + for (let i = 0; i < state.numPlayers; i++) { + const div = document.createElement('div'); + div.className = 'player-name-item'; + div.innerHTML = `Jugador ${i+1}:`; + list.appendChild(div); + } +} + +// ---------- Inicio de partida ---------- +function startGame() { + state.playerNames = []; + for (let i = 0; i < state.numPlayers; i++) { + const val = document.getElementById(`player-name-${i}`).value.trim(); + state.playerNames.push(val || `Jugador ${i+1}`); + } + pickWords().then(({civilian, impostor}) => { + state.civilianWord = civilian; + state.impostorWord = impostor; + finalizeStart(); + }).catch(() => { + const fallback = EMBEDDED_POOLS[0].words; + const shuffled = [...fallback].sort(() => Math.random() - 0.5); + state.civilianWord = shuffled[0]; + state.impostorWord = shuffled[1]; + finalizeStart(); + }); +} + +function finalizeStart() { + state.roles = Array(state.numPlayers - state.numImpostors).fill('CIVIL').concat(Array(state.numImpostors).fill('IMPOSTOR')).sort(() => Math.random()-0.5); + state.startPlayer = Math.floor(Math.random() * state.numPlayers); + state.turnDirection = Math.random() < 0.5 ? 'horario' : 'antihorario'; + const step = state.turnDirection === 'horario' ? 1 : -1; + state.revealOrder = Array.from({length: state.numPlayers}, (_, k) => (state.startPlayer + step * k + state.numPlayers) % state.numPlayers); + state.currentReveal = 0; state.phase = 'pre-reveal'; state.votes = {}; state.votingPlayer = 0; state.selections = []; state.executed = []; state.timerEndAt = null; state.timerPhase = null; + state.votingPool = null; state.isTiebreak = false; state.tiebreakCandidates = []; + saveState(); + renderSummary(); + showScreen('pre-reveal-screen'); +} + +// Ajustar defaults cuando se edita el nΒΊ de jugadores +document.getElementById('num-players').addEventListener('change', () => { + let nPlayers = parseInt(document.getElementById('num-players').value) || MIN_PLAYERS; + nPlayers = Math.min(Math.max(nPlayers, MIN_PLAYERS), MAX_PLAYERS); + document.getElementById('num-players').value = nPlayers; + const imp = defaultImpostors(nPlayers); + const gTime = defaultGameTime(nPlayers); + const dTime = defaultDeliberation(gTime); + document.getElementById('num-impostors').max = Math.max(1, Math.floor(nPlayers / 2)); + document.getElementById('num-impostors').value = imp; + document.getElementById('game-time').value = gTime; + document.getElementById('deliberation-time').value = dTime; +}); + +function renderSummary() { + const el = document.getElementById('config-summary'); + const fmt = secs => `${Math.floor(secs/60)}:${(secs%60).toString().padStart(2,'0')}`; + const startName = state.playerNames[state.startPlayer] || `Jugador ${state.startPlayer+1}`; + const poolMeta = availablePools.find(p => p.id === state.selectedPool) || EMBEDDED_POOLS[0]; + el.innerHTML = ` +

Jugadores: ${state.numPlayers}

+

Impostores: ${state.numImpostors}

+

Tiempo de partida: ${fmt(state.gameTime)}

+

Tiempo de deliberaciΓ³n: ${fmt(state.deliberationTime)}

+

Pool: ${poolMeta.emoji || '🎲'} ${poolMeta.name || poolMeta.id}

+

Empieza: ${startName} Β· Orden: ${state.turnDirection === 'horario' ? 'Horario' : 'Antihorario'}

+ `; +} + +// ---------- RevelaciΓ³n ---------- +function loadCurrentReveal() { + state.phase = 'reveal'; saveState(); + if (!state.revealOrder || state.revealOrder.length !== state.numPlayers) { + const step = state.turnDirection === 'horario' ? 1 : -1; + state.revealOrder = Array.from({length: state.numPlayers}, (_, k) => (state.startPlayer + step * k + state.numPlayers) % state.numPlayers); + } + const idx = state.revealOrder[state.currentReveal]; + const name = state.playerNames[idx]; + document.getElementById('current-player-name').textContent = name; + document.getElementById('curtain-cover').classList.remove('lifted'); + document.getElementById('next-player-btn').style.display = 'none'; + document.getElementById('start-game-btn').style.display = 'none'; +} + +function liftCurtain() { + const cover = document.getElementById('curtain-cover'); + if (cover.classList.contains('lifted')) return; + cover.classList.add('lifted'); + const idx = state.revealOrder[state.currentReveal]; + const role = state.roles[idx]; + const word = role === 'CIVIL' ? state.civilianWord : state.impostorWord; + document.getElementById('role-text').textContent = role; + document.getElementById('role-text').className = 'role ' + (role === 'CIVIL' ? 'civil' : 'impostor'); + document.getElementById('word-text').textContent = word; + setTimeout(() => { + if (state.currentReveal + 1 < state.numPlayers) document.getElementById('next-player-btn').style.display = 'block'; + else document.getElementById('start-game-btn').style.display = 'block'; + }, 700); +} + +function nextReveal() { state.currentReveal++; saveState(); loadCurrentReveal(); } + +// swipe support +(() => { + const curtain = document.getElementById('curtain'); + let startY = null; + curtain.addEventListener('touchstart', e => { startY = e.touches[0].clientY; }, {passive:true}); + curtain.addEventListener('touchmove', e => { if (startY === null) return; const dy = e.touches[0].clientY - startY; if (dy < -40) { liftCurtain(); startY = null; } }, {passive:true}); + curtain.addEventListener('click', liftCurtain); +})(); + +// ---------- Timers ---------- +let timerInterval = null; +function startPhaseTimer(phase, seconds, elementId, onEnd) { + if (timerInterval) clearInterval(timerInterval); + const now = Date.now(); + state.timerPhase = phase; + state.timerEndAt = now + seconds*1000; + saveState(); + const el = document.getElementById(elementId); + const tick = () => { + const remaining = Math.max(0, Math.round((state.timerEndAt - Date.now())/1000)); + updateTimerDisplay(el, remaining); + if (remaining <= 0) { clearInterval(timerInterval); playBeep(); onEnd(); } + }; + tick(); + timerInterval = setInterval(tick, 1000); +} + +function resumeTimerIfNeeded() { + if (!state.timerEndAt || !state.timerPhase) return; + const remaining = Math.round((state.timerEndAt - Date.now())/1000); + if (remaining <= 0) { state.timerEndAt = null; saveState(); return; } + if (state.timerPhase === 'game') { showScreen('game-screen'); startPhaseTimer('game', remaining, 'game-timer', startDeliberationPhase); } + else if (state.timerPhase === 'deliberation') { showScreen('deliberation-screen'); startPhaseTimer('deliberation', remaining, 'deliberation-timer', startVotingPhase); } +} + +function updateTimerDisplay(el, remaining) { + const minutes = Math.floor(remaining/60); const secs = remaining%60; + el.textContent = `${minutes}:${secs.toString().padStart(2,'0')}`; + el.className = 'timer'; + if (remaining <= 10) el.classList.add('danger'); else if (remaining <= 30) el.classList.add('warning'); +} + +function playBeep() { + const ctx = new (window.AudioContext || window.webkitAudioContext)(); + const osc = ctx.createOscillator(); const gain = ctx.createGain(); + osc.connect(gain); gain.connect(ctx.destination); osc.frequency.value = 820; osc.type = 'sine'; + gain.gain.setValueAtTime(0.3, ctx.currentTime); gain.gain.exponentialRampToValueAtTime(0.01, ctx.currentTime + 0.45); + osc.start(); osc.stop(ctx.currentTime + 0.45); +} + +// ---------- Fases ---------- +function startGamePhase() { state.phase = 'game'; saveState(); showScreen('game-screen'); startPhaseTimer('game', state.gameTime, 'game-timer', startDeliberationPhase); } +function startDeliberationPhase() { state.phase = 'deliberation'; saveState(); showScreen('deliberation-screen'); startPhaseTimer('deliberation', state.deliberationTime, 'deliberation-timer', startVotingPhase); } +function startVotingPhase(candidates = null, isTiebreak = false) { + state.phase = 'voting'; + state.votingPlayer = 0; + state.votes = {}; + state.selections = []; + state.votingPool = candidates; + state.isTiebreak = isTiebreak; + saveState(); + renderVoting(); + showScreen('voting-screen'); +} +function skipToDeliberation() { if (timerInterval) clearInterval(timerInterval); startDeliberationPhase(); } +function skipToVoting() { if (timerInterval) clearInterval(timerInterval); startVotingPhase(); } +function startTiebreakDeliberation(candidates) { + state.phase = 'deliberation'; + state.tiebreakCandidates = candidates; + saveState(); + showScreen('deliberation-screen'); + startPhaseTimer('deliberation', 60, 'deliberation-timer', () => startVotingPhase(candidates, true)); +} + +// ---------- VotaciΓ³n secreta ---------- +function renderVoting() { + const pool = state.votingPool || Array.from({length: state.numPlayers}, (_, i) => i); + const voter = state.playerNames[state.votingPlayer]; + document.getElementById('voter-name').textContent = voter; + document.getElementById('votes-needed').textContent = state.numImpostors; + state.selections = state.selections || []; + const list = document.getElementById('vote-list'); list.innerHTML = ''; + pool.forEach(i => { + const item = document.createElement('div'); + item.className = 'player-item'; + item.textContent = state.playerNames[i]; + if (state.votes[i]) item.innerHTML += `Votos: ${state.votes[i]}`; + if (state.selections.includes(i)) item.classList.add('selected'); + if (i === state.votingPlayer) { + item.classList.add('disabled'); + item.style.opacity = '0.5'; + item.style.pointerEvents = 'none'; + } else { + item.onclick = () => toggleSelection(i, item); + } + list.appendChild(item); + }); + updateConfirmButton(); +} + +function toggleSelection(idx, el) { + if (idx === state.votingPlayer) return; + if (state.selections.includes(idx)) state.selections = state.selections.filter(x => x !== idx); + else { + if (state.selections.length >= state.numImpostors) return; + state.selections.push(idx); + } + saveState(); + renderVoting(); +} + +function updateConfirmButton() { + const btn = document.getElementById('confirm-vote-btn'); + btn.disabled = state.selections.length !== state.numImpostors; +} + +function confirmCurrentVote() { + state.selections.forEach(t => { state.votes[t] = (state.votes[t] || 0) + 1; }); + state.votingPlayer++; + state.selections = []; + saveState(); + if (state.votingPlayer >= state.numPlayers) { handleVoteOutcome(); return; } + renderVoting(); +} + +// ---------- ResoluciΓ³n de voto ---------- +function handleVoteOutcome() { + const pool = state.votingPool || Array.from({length: state.numPlayers}, (_, i) => i); + const counts = pool.map(idx => ({ idx, votes: state.votes[idx] || 0 })); + counts.sort((a, b) => b.votes - a.votes); + + let slots = state.numImpostors; + const executed = []; + for (let i = 0; i < counts.length && slots > 0; ) { + const currentVotes = counts[i].votes; + const group = []; + let j = i; + while (j < counts.length && counts[j].votes === currentVotes) { group.push(counts[j].idx); j++; } + if (group.length <= slots) { + executed.push(...group); + slots -= group.length; + i = j; + } else { + // Tie for remaining slots + if (state.isTiebreak) { + // segunda vez empatados: ganan impostores + state.executed = []; + showResults(true); + return; + } + startTiebreakDeliberation(group); + return; + } + } + + state.executed = executed; + showResults(); +} + +// ---------- Resultados ---------- +function showResults(isTiebreak = false) { + state.phase = 'results'; saveState(); + const executed = state.executed || []; + let impostorsAlive = 0; + state.roles.forEach((r,i) => { if (r === 'IMPOSTOR' && !executed.includes(i)) impostorsAlive++; }); + const winner = impostorsAlive > 0 ? 'IMPOSTORES' : 'CIVILES'; + const results = document.getElementById('results-content'); + results.innerHTML = ` +

${winner === 'CIVILES' ? 'βœ… Β‘GANAN LOS CIVILES!' : '❌ Β‘GANAN LOS IMPOSTORES!'}

+

Ejecutados: ${executed.length ? executed.map(i => state.playerNames[i]).join(', ') : 'Nadie'}

+

Votos: ${Object.keys(state.votes).length ? '' : 'Sin votos'}

+

Roles revelados

+ ${state.roles.map((role,i) => { + const word = role === 'CIVIL' ? state.civilianWord : state.impostorWord; + const killed = executed.includes(i) ? 'executed' : ''; + return `
${state.playerNames[i]}: ${role} β€” "${word}" ${killed ? '☠️' : ''}
`; + }).join('')} + `; + showScreen('results-screen'); +} + +// ---------- Utilidades ---------- +function showScreen(id) { + document.querySelectorAll('.screen').forEach(s => s.classList.remove('active')); + document.getElementById(id).classList.add('active'); + state.phase = id.replace('-screen',''); + saveState(); +} + +function newMatch() { clearState(); state = { ...state, phase:'setup', timerEndAt:null, timerPhase:null, votingPool:null, isTiebreak:false, tiebreakCandidates:[] }; location.reload(); } + +// ---------- RehidrataciΓ³n ---------- +(function init() { + const restored = loadState(); + showScreen('setup-screen'); + loadPoolsList(); + if (!state.turnDirection) state.turnDirection = 'horario'; + if (typeof state.startPlayer !== 'number') state.startPlayer = 0; + switch (state.phase) { + case 'setup': showScreen('setup-screen'); break; + case 'names': buildNameInputs(); showScreen('names-screen'); break; + case 'pre-reveal': renderSummary(); showScreen('pre-reveal-screen'); break; + case 'reveal': showScreen('reveal-screen'); loadCurrentReveal(); break; + case 'game': showScreen('game-screen'); resumeTimerIfNeeded(); break; + case 'deliberation': showScreen('deliberation-screen'); resumeTimerIfNeeded(); break; + case 'voting': showScreen('voting-screen'); renderVoting(); break; + case 'results': showResults(); break; + default: showScreen('setup-screen'); + } +})(); diff --git a/styles.26a5b74f.css b/styles.26a5b74f.css new file mode 100644 index 0000000..5b64e14 --- /dev/null +++ b/styles.26a5b74f.css @@ -0,0 +1,455 @@ +/* ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + EXPEDIENTE CLASIFICADO - IMPOSTOR GAME + Noir Cyberpunk Interrogation Aesthetic + ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ */ + +@import url('https://fonts.googleapis.com/css2?family=Bebas+Neue&family=Special+Elite&display=swap'); + +:root { + /* LIGHT THEME: Interrogation Room */ + --bg-primary: #dcd9d2; + --bg-secondary: #c8c3b8; + --bg-overlay: rgba(0, 0, 0, 0.05); + + --surface-glass: rgba(255, 255, 255, 0.85); + --surface-card: rgba(255, 255, 255, 0.95); + --surface-hover: rgba(255, 255, 255, 1); + + --text-primary: #0a0a0a; + --text-secondary: #2a2a2a; + --text-tertiary: #5a5a5a; + --text-inverted: #ffffff; + + --accent-warning: #e6a73c; + --accent-danger: #d93626; + --accent-success: #2d8b3d; + --accent-info: #2e4e7a; + + --border-light: rgba(0, 0, 0, 0.18); + --border-medium: rgba(0, 0, 0, 0.35); + --border-heavy: rgba(0, 0, 0, 0.55); + + --shadow-sm: 0 3px 12px rgba(0, 0, 0, 0.15); + --shadow-md: 0 6px 24px rgba(0, 0, 0, 0.22); + --shadow-lg: 0 12px 48px rgba(0, 0, 0, 0.28); + --shadow-harsh: 6px 6px 0px rgba(0, 0, 0, 0.25); + + --grain-opacity: 0.05; + --scanline-opacity: 0.025; + + /* Spotlight effect */ + --spotlight-color: rgba(255, 235, 180, 0.08); + --vignette-intensity: 0.4; +} + +[data-theme="dark"] { + /* DARK THEME: Night Investigation */ + --bg-primary: #050505; + --bg-secondary: #0f0f0f; + --bg-overlay: rgba(255, 255, 255, 0.03); + + --surface-glass: rgba(25, 25, 25, 0.9); + --surface-card: rgba(35, 35, 35, 0.95); + --surface-hover: rgba(45, 45, 45, 1); + + --text-primary: #f5f5f5; + --text-secondary: #d0d0d0; + --text-tertiary: #909090; + --text-inverted: #0a0a0a; + + --accent-warning: #ffb84d; + --accent-danger: #ff3d2e; + --accent-success: #3dd46b; + --accent-info: #4d8ce0; + + --border-light: rgba(255, 255, 255, 0.12); + --border-medium: rgba(255, 255, 255, 0.22); + --border-heavy: rgba(255, 255, 255, 0.35); + + --shadow-sm: 0 3px 12px rgba(0, 0, 0, 0.6); + --shadow-md: 0 6px 24px rgba(0, 0, 0, 0.8); + --shadow-lg: 0 12px 48px rgba(0, 0, 0, 0.95); + --shadow-harsh: 6px 6px 0px rgba(0, 0, 0, 0.7); + + --grain-opacity: 0.07; + --scanline-opacity: 0.035; + + /* Spotlight effect */ + --spotlight-color: rgba(255, 200, 100, 0.04); + --vignette-intensity: 0.6; +} + +/* ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + BASE STYLES & TYPOGRAPHY + ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ */ + +* { + margin: 0; + padding: 0; + box-sizing: border-box; + -webkit-tap-highlight-color: transparent; +} + +body { + font-family: 'JetBrains Mono', 'Courier Prime', 'Courier New', monospace; + background: + radial-gradient(ellipse 80% 50% at 50% 20%, var(--spotlight-color) 0%, transparent 50%), + radial-gradient(circle at 20% 30%, rgba(230, 167, 60, 0.08) 0%, transparent 40%), + radial-gradient(circle at 80% 70%, rgba(217, 54, 38, 0.06) 0%, transparent 40%), + var(--bg-primary); + min-height: 100vh; + min-height: 100dvh; + display: flex; + justify-content: center; + align-items: center; + padding: 70px 16px 16px; + color: var(--text-primary); + position: relative; + overflow: hidden; + font-size: 14px; + letter-spacing: 0px; + transition: background 0.5s ease, color 0.3s ease; +} + +/* Film grain texture overlay */ +body::before { + content: ''; + position: fixed; + top: 0; + left: 0; + width: 100%; + height: 100%; + background-image: url("data:image/svg+xml,%3Csvg viewBox='0 0 400 400' xmlns='http://www.w3.org/2000/svg'%3E%3Cfilter id='noise'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.9' numOctaves='4' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23noise)' opacity='0.5'/%3E%3C/svg%3E"); + opacity: var(--grain-opacity); + pointer-events: none; + z-index: 9999; + mix-blend-mode: overlay; + animation: grain 8s steps(10) infinite; +} + +@keyframes grain { + 0%, 100% { transform: translate(0, 0); } + 10% { transform: translate(-5%, -10%); } + 20% { transform: translate(-15%, 5%); } + 30% { transform: translate(7%, -25%); } + 40% { transform: translate(-5%, 25%); } + 50% { transform: translate(-15%, 10%); } + 60% { transform: translate(15%, 0%); } + 70% { transform: translate(0%, 15%); } + 80% { transform: translate(3%, 35%); } + 90% { transform: translate(-10%, 10%); } +} + +/* Scanlines */ +body::after { + content: ''; + position: fixed; + top: 0; + left: 0; + width: 100%; + height: 100%; + background: repeating-linear-gradient( + 0deg, + transparent, + transparent 2px, + rgba(0, 0, 0, 0.1) 2px, + rgba(0, 0, 0, 0.1) 4px + ); + opacity: var(--scanline-opacity); + pointer-events: none; + z-index: 9998; +} + +/* Dramatic vignette overlay */ +.vignette-overlay { + position: fixed; + inset: 0; + background: radial-gradient(ellipse at center, transparent 40%, rgba(0,0,0,var(--vignette-intensity)) 100%); + pointer-events: none; + z-index: 9997; +} + +/* VHS interference effect */ +@keyframes vhsInterference { + 0%, 100% { opacity: 0; } + 5% { opacity: 0.03; transform: translateX(-2px); } + 10% { opacity: 0; } + 15% { opacity: 0.02; transform: translateX(1px); } + 20% { opacity: 0; } +} + +.vhs-line { + position: fixed; + left: 0; + width: 100%; + height: 3px; + background: linear-gradient(90deg, transparent, rgba(255,255,255,0.8), transparent); + pointer-events: none; + z-index: 9996; + animation: vhsScan 8s linear infinite; + opacity: 0.04; +} + +@keyframes vhsScan { + 0% { top: -10px; } + 100% { top: 110%; } +} + +h1 { + font-family: 'Bebas Neue', 'Crimson Text', Georgia, serif; + text-align: center; + margin-bottom: 20px; + font-size: 2.6em; + font-weight: 400; + letter-spacing: 4px; + text-transform: uppercase; + position: relative; + text-shadow: 3px 3px 0px var(--bg-secondary), 0 0 30px rgba(230, 167, 60, 0.2); + line-height: 1.1; + animation: titleReveal 0.6s cubic-bezier(0.22, 1, 0.36, 1) forwards; +} + +@keyframes titleReveal { + from { + opacity: 0; + letter-spacing: 20px; + filter: blur(8px); + } + to { + opacity: 1; + letter-spacing: 4px; + filter: blur(0); + } +} + +h1::after { + content: ''; + display: block; + width: 80px; + height: 4px; + background: linear-gradient(90deg, var(--accent-danger) 0%, var(--accent-warning) 50%, var(--accent-danger) 100%); + background-size: 200% 100%; + margin: 14px auto 0; + box-shadow: 0 0 15px rgba(230, 167, 60, 0.5); + animation: shimmer 3s ease-in-out infinite; +} + +@keyframes shimmer { + 0%, 100% { background-position: 0% 50%; } + 50% { background-position: 100% 50%; } +} + +h2 { + font-family: 'Crimson Text', Georgia, serif; + text-align: center; + margin: 16px 0; + font-size: 1.4em; + font-weight: 700; + letter-spacing: 0.5px; +} + +h3 { + font-family: 'JetBrains Mono', monospace; + font-size: 1em; + font-weight: 800; + text-transform: uppercase; + letter-spacing: 1.5px; + margin-bottom: 12px; +} + +/* ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + CONTAINER & SCREENS + ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ */ + +.container { + width: 100%; + max-width: 480px; + background: var(--surface-glass); + backdrop-filter: blur(20px) saturate(150%); + border-radius: 0; + padding: 28px 22px; + box-shadow: var(--shadow-harsh), var(--shadow-lg); + border: 4px solid var(--border-heavy); + display: flex; + flex-direction: column; + transition: all 0.4s ease; + margin-bottom: 20px; + position: relative; + overflow: hidden; + clip-path: polygon( + 0 20px, + 20px 0, + 100% 0, + 100% calc(100% - 20px), + calc(100% - 20px) 100%, + 0 100% + ); +} + +.container::before { + content: 'β¬’ CLASSIFIED β¬’'; + position: absolute; + top: 8px; + left: 50%; + transform: translateX(-50%); + font-size: 0.65em; + letter-spacing: 3px; + opacity: 0.4; + font-weight: 800; + color: var(--accent-danger); + text-shadow: 0 0 10px rgba(217, 54, 38, 0.3); + animation: classifiedPulse 4s ease-in-out infinite; +} + +@keyframes classifiedPulse { + 0%, 100% { opacity: 0.4; text-shadow: 0 0 10px rgba(217, 54, 38, 0.3); } + 50% { opacity: 0.6; text-shadow: 0 0 20px rgba(217, 54, 38, 0.6); } +} + +/* Diagonal classified stamp */ +.container::after { + content: 'EXPEDIENTE'; + position: absolute; + bottom: 15px; + right: -30px; + font-family: 'Special Elite', 'Courier Prime', monospace; + font-size: 0.7em; + letter-spacing: 3px; + color: var(--accent-danger); + opacity: 0.12; + transform: rotate(-45deg); + font-weight: 400; + white-space: nowrap; + pointer-events: none; +} + +.screen { + display: none; + animation: screenEnter 0.35s cubic-bezier(0.22, 1, 0.36, 1); + flex: 1; + overflow: hidden; + min-height: 0; +} + +.screen.active { + display: flex; + flex-direction: column; +} + +@keyframes screenEnter { + 0% { + opacity: 0; + transform: translateY(30px) scale(0.95); + filter: blur(4px); + } + 60% { + opacity: 1; + filter: blur(0); + } + 100% { + opacity: 1; + transform: translateY(0) scale(1); + filter: blur(0); + } +} + +/* Staggered children animation */ +.screen.active > * { + animation: fadeSlideUp 0.5s cubic-bezier(0.22, 1, 0.36, 1) backwards; +} + +.screen.active > *:nth-child(1) { animation-delay: 0.05s; } +.screen.active > *:nth-child(2) { animation-delay: 0.1s; } +.screen.active > *:nth-child(3) { animation-delay: 0.15s; } +.screen.active > *:nth-child(4) { animation-delay: 0.2s; } +.screen.active > *:nth-child(5) { animation-delay: 0.25s; } +.screen.active > *:nth-child(6) { animation-delay: 0.3s; } +.screen.active > *:nth-child(7) { animation-delay: 0.35s; } +.screen.active > *:nth-child(8) { animation-delay: 0.4s; } + +@keyframes fadeSlideUp { + from { + opacity: 0; + transform: translateY(15px); + } + to { + opacity: 1; + transform: translateY(0); + } +} + +/* ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + FORM CONTROLS + ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ */ + +.form-group { + margin-bottom: 16px; +} + +.form-group.compact { + margin-bottom: 12px; +} + +label { + display: block; + margin-bottom: 8px; + font-weight: 700; + font-size: 0.8em; + color: var(--text-secondary); + text-transform: uppercase; + letter-spacing: 1.2px; +} + +input { + width: 100%; + padding: 12px 14px; + border: 2px solid var(--border-medium); + border-radius: 0; + font-size: 0.95em; + font-family: 'JetBrains Mono', monospace; + background: var(--surface-card); + color: var(--text-primary); + transition: all 0.2s ease; + box-shadow: inset 2px 2px 4px rgba(0, 0, 0, 0.1); +} + +input:focus { + outline: none; + border-color: var(--accent-warning); + box-shadow: inset 2px 2px 4px rgba(0, 0, 0, 0.1), 0 0 0 3px rgba(212, 165, 116, 0.2); + transform: translateY(-1px); +} + +/* ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + BUTTONS + ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ */ + +button { + width: 100%; + padding: 16px 20px; + border: 3px solid var(--text-primary); + border-radius: 0; + font-size: 0.9em; + font-weight: 800; + font-family: 'JetBrains Mono', monospace; + cursor: pointer; + background: var(--text-primary); + color: var(--text-inverted); + box-shadow: var(--shadow-harsh); + transition: all 0.15s cubic-bezier(0.4, 0, 0.2, 1); + margin-top: 12px; + text-transform: uppercase; + letter-spacing: 0.8px; + position: relative; + overflow: hidden; + clip-path: polygon( + 0 0, + calc(100% - 12px) 0, + 100% 12px, + 100% 100%, + 12px 100%, + 0 calc(100% - 12px) + ); +} + +