From 9b22a86785ee7c2318cde1dab1760bd7f8c8163c Mon Sep 17 00:00:00 2001 From: ZennilGreenherald <144579645+ZennilGreenherald@users.noreply.github.com> Date: Sat, 8 Nov 2025 17:53:33 -0800 Subject: [PATCH 1/4] Update twitch_chat.js --- src/twitch_chat.js | 70 +++++++++++++++++++--------------------------- 1 file changed, 28 insertions(+), 42 deletions(-) diff --git a/src/twitch_chat.js b/src/twitch_chat.js index 2360874..51d40e0 100644 --- a/src/twitch_chat.js +++ b/src/twitch_chat.js @@ -2,27 +2,30 @@ globalThis.twitchChatConnect = twitchChatConnect; const TwitchWebSocketUrl = 'wss://irc-ws.chat.twitch.tv:443'; -// const chatBody = (document.querySelector("#ChatMessages")); + let wsTwitch; let channelName; let parseChatCallback; + export function twitchChatConnect(name, chatParseCallback) { channelName = name; parseChatCallback = chatParseCallback; wsTwitch = new WebSocket(TwitchWebSocketUrl); - wsTwitch.onopen = ()=>{ + wsTwitch.onopen = () => { console.log("chat opened"); wsTwitch.send(`CAP REQ :twitch.tv/commands twitch.tv/tags`); wsTwitch.send(`NICK justinfan6969`); wsTwitch.send(`JOIN #${channelName}`); - console.log('WebSocket connection opened'); //debug + console.log('WebSocket connection opened'); let chatMSG = document.createElement("div"); let auth = document.createElement("div"); - parseChatCallback("", + parseChatCallback( + "", `Connected to ${channelName}'s chat!`, - auth, chatMSG + auth, + chatMSG ); } wsTwitch.onmessage = onMessage; @@ -32,72 +35,55 @@ export function twitchChatConnect(name, chatParseCallback) function onMessage(fullmsg) { - // console.log("fullmsg: ", fullmsg); - let txt = fullmsg.data; - // console.log("txt: ", txt); + const txt = fullmsg.data; + console.log("txt: ", txt); let name = ''; let outmsg = ''; let indx = 0; - // let just_tags = ''; - // let tags_obj = {}; - // const emote_list = []; if (txt[0] == '@') { - indx = txt.indexOf(' '); - // just_tags = txt.slice(0, indx); - indx++; - // tags_obj = parse_tags(just_tags); - // get_emote_list(tags_obj['emotes'], emote_list); + indx = txt.indexOf(' ') + 1; } if (txt[indx] == ':') { - // get the important data positions - let pos1 = txt.indexOf('@', indx) + 1; - let pos2 = txt.indexOf(".", pos1); - let pos3 = txt.indexOf(`#${channelName}`)+2; - pos3 += channelName.length + 1; - - // create strings based on those positions + const pos1 = txt.indexOf('@', indx) + 1; + const pos2 = txt.indexOf(".", pos1); name = txt.substring(pos1, pos2).trim(); + if (new Set([":tmi", "justinfan6969", "@emote-only=0;", ":justinfan6969"]).has(name)) { + console.log(`Invalid name: _${name}_`); + return; + } - if ( (name == ":tmi") - || (name == "justinfan6969") - || (name.includes("@emote-only=0;")) - || (name == ":justinfan6969")) - { return; } - + const pos3 = txt.indexOf(`#${channelName}`) + channelName.length + 3; outmsg = txt.substring(pos3).trim(); + console.log(`outmsg: ${outmsg}`); } else { - // handle pings - // other twitch specific things should - // be handled here too - let pos2 = txt.indexOf(":"); + // handle pings & other twitch specific things + const pos2 = txt.indexOf(":"); name = txt.slice(0, pos2).trim(); outmsg = txt.slice(pos2).trim(); if (name == 'PING') { - // console.log('PONG ' + outmsg); + console.log('PONG ' + outmsg); wsTwitch.send('PONG ' + outmsg); } return; } //not running bot commands here + console.log(`Invalid message: _${outmsg}_`); if (outmsg[0] == '!') { return; } - // return display_msg(name, outmsg); + console.log(`name ${name}, outmsg: ${outmsg}`); display_msg(name, outmsg); - // console.log("name", name); - // console.log("outmsg", outmsg); - } // display chat message on stream // calls parseChatCallback() with elements created here -//auth & chatMSG are both stylized here +// auth & chatMSG are both stylized here function display_msg(name, outmsg, tags_obj, emote_list) { let emote; @@ -118,13 +104,13 @@ function display_msg(name, outmsg, tags_obj, emote_list) auth.textContent = (tags_obj?.display_name || name) + ' '; if (tags_obj?.emotes) { - let parts = []; - let end_indx = outmsg.length; + let parts = []; + let end_indx = outmsg.length; for (let i = emote_list.length; --i >= 0; ) { emote = document.createElement("img"); emote.setAttribute('src', emote_list[i].url); - if (i!==0) { + if (i !== 0) { emote.style = 'margin-left: -14px'; } From 44e3f421a29bf9d7359540dabb07eac648dc6549 Mon Sep 17 00:00:00 2001 From: ZennilGreenherald <144579645+ZennilGreenherald@users.noreply.github.com> Date: Sat, 8 Nov 2025 17:54:08 -0800 Subject: [PATCH 2/4] Update movie_trivia.js --- src/movie_trivia.js | 907 ++++++++++++++++++-------------------------- 1 file changed, 369 insertions(+), 538 deletions(-) diff --git a/src/movie_trivia.js b/src/movie_trivia.js index 5c70c0e..833f4bc 100644 --- a/src/movie_trivia.js +++ b/src/movie_trivia.js @@ -1,267 +1,263 @@ "use strict"; let twitchChatConnect; -import ("./twitch_chat.js").then((e)=>{ - //this way only works with modules enabled - //(see script assignments in index.html) - //(L270 at time of writing this comment) - twitchChatConnect = e.twitchChatConnect; +import ("./twitch_chat.js").then(e => { + //this way only works with modules enabled (see script assignments in index.html) + twitchChatConnect = e.twitchChatConnect; + console.log(`twitchChatConnect is set via import!`); }); -// let lastTime; -// function fpsCounter() { -// let currentTime = new Date().getTime(); -// let counter = document.getElementById("fps"); -// counter.innerText = 1000/(currentTime-lastTime); -// lastTime = currentTime; -// requestAnimationFrame(animationCallback); -// } - -// play_movie_trivia(); - - -//FrankL81: -// you do a -// customElements.define("trivia-game", class extends HTMLElement { -// connectedCallback(){ //..... } )) - -//BakerStaunch: -// Yeah but I mean just have a setInterval -// once at the start and inside the -// interval callback check if it's -// paused or not rather than trying to -// manage the interval itself -//BakerStaunch: -// You can treat it like a "main loop" -// by having a single interval -// (or requestAnimationFrame) -//BakerStaunch: -// btw, I'd suggest using performance.now() -// for the time instead of Date.now() - system -// clocks can change, performance.now() -// is meant to be number of milliseconds -// since the page loaded - -const answerBtn = document.getElementById("answer"); -const playBtn = document.getElementById("pause"); -const prevBtn = document.getElementById("prev"); -const nextBtn = document.getElementById("next"); -const twitchName = document.getElementById("twitchName"); -const triviaDiv = document.getElementById("trivia"); -const connectBtn = document.getElementById("connectChatBtn"); -const timer = document.getElementById("timer"); -const sound = document.getElementById("sound"); -let question = document.createElement("img"); - - - -let maxMsgCount = 5; -let countdownTime = 30; -let answerTime = 15; -let questionCount = 10; -let twitchChatWS; +const randInt = n => Math.floor(Math.random() * n); +const getById = id => document.getElementById(id); +const createEl = (tagName, props) => { + const el = document.createElement(tagName); + Object.entries(props || {}).forEach(([k, v]) => el[k] = v); + return el; +} + + +const buttons = { + answerBtn: getById("answer"), + playBtn: getById("pause"), + prevBtn: getById("prev"), + nextBtn: getById("next"), + setAnswerText: function(t) { + this.answerBtn.innerText = t; + }, + setPlayText: function(t) { + this.playBtn.innerText = t; + }, + setAnswerOnClick: function(onclick) { + this.answerBtn.onclick = onclick; + }, + setPlayOnClick: function(onclick) { + this.playBtn.onclick = onclick; + }, + isShowingNext: function() { + return this.answerBtn.innerText === "Next"; + }, + isShowingAnswer: function() { + return this.answerBtn.innerText === "Answer"; + }, + setPrevOnClick: function(onclick) { + this.prevBtn.onclick = onclick; + }, + setPrevEnabled: function(b) { + this.prevBtn.disabled = !b; + }, + setNextOnClick: function(onclick) { + this.nextBtn.onclick = onclick; + }, + setDisabled: function(b) { + this.nextBtn.disabled = b; + this.prevBtn.disabled = b; + this.playBtn.disabled = b; + }, + showRestart: function(k) { + this.setDisabled(true); + this.setAnswerText("Restart?"); + + this.answerBtn.onclick = () => { + this.setDisabled(false); + this.setAnswerText("Answer"); + k(); + } + } +}; + + +const timer = { + label: getById("timer"), + state: 'running', + setText: function(t) { + this.label.innerText = t; + }, + getText: function() { + return +this.label.innerText; + }, + pause: function() { + this.state = "paused"; + }, + run: function() { + this.state = "running"; + }, + isRunning: function() { + return this.state == "running"; + } +}; + + +const stopwatch = { + setEnd: function(numSeconds) { + this.timeEnd = performance.now() + 1000 * numSeconds; + }, + setEndNow: function() { + this.setEnd(0); + }, + secondsTilExpiry: function() { + const msToGo = this.timeEnd - performance.now(); + return Math.floor(msToGo % (1000 * 60) / 1000) + }, + isShowAnswerTimeExpired: function(showAnswerTimeSecs) { + return performance.now() > this.timeEnd + showAnswerTimeSecs * 1000 + } +}; + + +const twitchName = getById("twitchName"); +const connectBtn = getById("connectChatBtn"); +const sound = getById("sound"); +let question = createEl("img"); + +let maxMsgCount = 5; +let countdownTime = 30; +let showAnswerTime = 15; +let questionCount = 10; + +let twitchChatWS; let tmdbList; + let answered = []; let winners = []; let triviaIndex = 0; let score = {}; -let endTime; + let correctAnsIdx; -let timerState = "running"; let triviaQuestions; + + async function play_trivia() { - let prevState; - - // triviaQuestions = await createQuestions(); await createQuestions(); initButtons(); - endTime = performance.now() + 1000*countdownTime; + stopwatch.setEnd(countdownTime); - // question.src = `/Movie-Tracker/bg/${triviaQuestions[0].question}`; question.src = await getTriviaURL(0); - // question.src = `/assets/junior-1994-0.gif`; - // let p = await new Promise((res)=>{ - // question.addEventListener('load', ()=>{ - // console.log("qa",question); - // res(true); - // }); - // question.addEventListener('error',()=>{ - // console.log("qb",question); - // res(false); - // }); - // }); - // console.log("p",await p); - // question.src = `https://quantumapprentice.github.io/movieTrivia-MiniGame/assets/junior-1994-1.gif` question.id = "question"; - triviaDiv.appendChild(question); - timer.innerText = countdownTime; + getById("trivia").appendChild(question); + timer.setText(countdownTime); - function updateButtons() { - if (triviaIndex === 0 || triviaIndex == triviaCount-1) { - //disable prevBtn if on first trivia or score screen - prevBtn.disabled = true; - } else { - prevBtn.disabled = false; - } - // const playBtn = document.getElementById("pause"); - // if (timerState === "paused") { - // playBtn.innerText = "Play"; - // } - // if (timerState === "running") { - // playBtn.innerText = "Pause"; - // } - } - - function updateTimer() { - playBtn.innerText = "Pause"; - if (answerBtn.innerText !== "Next") { - const now = performance.now(); - timer.innerText = Math.floor((endTime - now)%(1000*60)/1000); - } + multipleChoice(); + stateMachine(); +} - if (timer.innerText <= 0) { - answerBtn.innerText = "Next"; - timer.innerText = triviaQuestions[triviaIndex].answer; - } - if (performance.now() > endTime + answerTime*1000) { - //reset timer and load next trivia - answerBtn.innerText = "Answer"; - nextTrivia(); - } + +function updateTimer() { + if (!timer.isRunning()) { + return; } - function stateMachine() { - if (timerState !== prevState) { - prevState = timerState; - } - if (timerState !== "paused") { - updateTimer(); - updateButtons(); - } - requestAnimationFrame(stateMachine); + buttons.setPlayText("Pause"); + if (!buttons.isShowingNext()) { + timer.setText(stopwatch.secondsTilExpiry()); } - multipleChoice(); - stateMachine(); + if (timer.getText() <= 0) { + buttons.setAnswerText("Next"); + timer.setText(triviaQuestions[triviaIndex % triviaQuestions.length].answer); + } + if (stopwatch.isShowAnswerTimeExpired(showAnswerTime)) { + // reset timer and load next trivia + buttons.setAnswerText("Answer"); + nextTrivia(); + } + buttons.setPrevEnabled(triviaIndex > 0 && triviaIndex < triviaCount.value); +} + + +function stateMachine() { + updateTimer(); + requestAnimationFrame(stateMachine); } + async function getTriviaURL(index) { let baseURL = `https://quantumapprentice.github.io/Movie-Tracker/bg/${triviaQuestions[index].question}`; // let baseURL = `/Movie-Tracker/bg/${triviaQuestions[index].question}`; let imageName = triviaQuestions[index].question.slice(0,-4); - // let imageName = `junior-1994.jpg`.slice(0,-4); - // console.log("i",imageName); const img = new Image(); let difficulty = 0; let temp; let p = false; while (!p && difficulty < 3) { - temp = `/assets/${imageName}-${difficulty++}.gif`; + temp = `https://quantumapprentice.github.io/movieTrivia-MiniGame/assets/${imageName}-${difficulty++}.gif`; img.src = temp; - p = await new Promise(res=>{ - img.addEventListener('load', ()=>res(true) ); - img.addEventListener('error',()=>res(false)); + p = await new Promise(res => { + img.addEventListener('load', () => res(true)); + img.addEventListener('error',() => res(false)); }); } - if (p) { - console.log('t',temp); - return temp; - } - - return baseURL; - + return p ? temp : baseURL; } - - - - - - - - - - - - function initButtons() { //answer/next - answerBtn.onclick = (e)=>{ - if (e.target.innerText === "Answer") { - e.target.innerText = "Next"; - //show the answer in the timer label - timer.innerText = triviaQuestions[triviaIndex].answer; + buttons.setAnswerOnClick(e => { + if (buttons.isShowingAnswer()) { + buttons.setAnswerText("Next"); + timer.setText(triviaQuestions[triviaIndex].answer); } else { - e.target.innerText = "Answer"; + buttons.setAnswerText("Answer"); nextTrivia(); } - } + }); + //play/pause - playBtn.onclick = (e)=>{ - if (timerState === "paused") { - e.target.innerText = "Pause"; - timerState = "running"; + buttons.setPlayOnClick(e => { + if (!timer.isRunning()) { + buttons.setPlayText("Pause"); + timer.run(); restartTimer(); } else { - e.target.innerText = "Play"; - timerState = "paused"; + buttons.setPlayText("Play"); + timer.pause(); } - } + }); + //next >> - prevBtn.onclick = async ()=>{ + buttons.setPrevOnClick(async () => { triviaIndex -= 1; if (triviaIndex < 0) { - triviaIndex = triviaQuestions.length-1; + triviaIndex = triviaQuestions.length - 1; } - // question.src = `/Movie-Tracker/bg/${triviaQuestions[triviaIndex].question}`; question.src = await getTriviaURL(triviaIndex); resetTimer(); multipleChoice(); - // clearRound(); - answerBtn.innerText = "Answer"; - } + buttons.setAnswerText("Answer"); + }); //previous << - nextBtn.onclick = ()=>{ + buttons.setNextOnClick(() => { nextTrivia(); - answerBtn.innerText = "Answer"; - } + buttons.setAnswerText("Answer"); + }); //sound button - sound.onclick = (e)=>{ + sound.onclick = e => { if (e.currentTarget.classList.value === "soundOn") { e.currentTarget.classList = "soundOff"; - document.getElementById("sadTrombone").muted = true; + getById("sadTrombone").muted = true; } else { e.currentTarget.classList = "soundOn"; - document.getElementById("sadTrombone").muted = false; + getById("sadTrombone").muted = false; } } //twitch chat connection stuff //specifically attached to the connect button - twitchName.addEventListener("keypress", (e)=>{ + twitchName.addEventListener("keypress", e => { if (e.key === "Enter") { e.stopPropagation(); - // console.log("val",e.target.value); - - - if (!e.target.value) { - let chatMSG = document.createElement("div"); - let auth = document.createElement("div"); - parseChatCallback("TriviaBot", - `No channel-name provided. Add a channel name in ☰ Options.`, - auth, chatMSG); + parseChatCallback("TriviaBot", `No channel-name provided. Add a channel name in ☰ Options.`); return; } @@ -269,178 +265,158 @@ function initButtons() connectBtn.innerText = "Disconnect"; startChat(e.target.value, parseChatCallback); } else { - twitchChatWS.onclose = ()=>{ + twitchChatWS.onclose = () => { connectBtn.innerText = "Connect to Twitch Chat"; - let chatMSG = document.createElement("div"); - let auth = document.createElement("div"); - parseChatCallback("TriviaBot", - `Disconnected from twitch chat.`, - auth, chatMSG); + parseChatCallback("TriviaBot", `Disconnected from twitch chat.`); } twitchChatWS.close(); } } }); - connectBtn.onclick = (e)=>{ + + connectBtn.onclick = e => { e.stopPropagation(); if (!twitchName.value) { - let chatMSG = document.createElement("div"); - let auth = document.createElement("div"); - parseChatCallback("TriviaBot", - `No channel-name provided. Add a channel name in ☰ Options.`, - auth, chatMSG); + parseChatCallback("TriviaBot", `No channel-name provided. Add a channel name in ☰ Options.`); return; } - if (e.target.innerText === "Connect to Twitch Chat") { e.target.innerText = "Disconnect"; startChat(twitchName.value, parseChatCallback); } else { - twitchChatWS.onclose = ()=>{ + twitchChatWS.onclose = () => { e.target.innerText = "Connect to Twitch Chat"; - let chatMSG = document.createElement("div"); - let auth = document.createElement("div"); - parseChatCallback("TriviaBot", - `Disconnected from twitch chat.`, - auth, chatMSG); + parseChatCallback("TriviaBot", `Disconnected from twitch chat.`); } twitchChatWS.close(); } } //sidebar hamburger button - const hamburger = document.getElementById("hamburger"); - hamburger.onclick = (e)=>{ + const hamburger = getById("hamburger"); + hamburger.onclick = e => { e.stopPropagation(); e.currentTarget.classList.toggle("change"); - document.getElementById("sidebar").classList.toggle("change"); + getById("sidebar").classList.toggle("change"); } //number of trivia elements in this run - const triviaCount = document.getElementById("triviaCount"); + const triviaCount = getById("triviaCount"); if (triviaCount.value != questionCount) { questionCount = triviaCount.value; createQuestions(); } - triviaCount.onchange = (e)=>{ + + triviaCount.onchange = e => { questionCount = e.target.value; createQuestions(); } //amount of time trivia question will stay on screen - const triviaTime = document.getElementById("triviaTime"); + const triviaTime = getById("triviaTime"); if (triviaTime.value != countdownTime) { countdownTime = triviaTime.value; } - triviaTime.onchange = (e)=>{ + + triviaTime.onchange = e => { countdownTime = e.target.value; - const countdown = timer.innerText; - if (countdownTime < Number(countdown)) { + const countdown = timer.getText(); + if (countdownTime < countdown) { restartTimer(); } } - //amount of time the answer stays on screen - //before starting the next trivia question - const pauseTime = document.getElementById("pauseTime"); - if (pauseTime.value != answerTime) { - answerTime = pauseTime.value; + // amount of time the answer stays on screen before starting the next trivia question + const pauseTime = getById("pauseTime"); + if (pauseTime.value != showAnswerTime) { + showAnswerTime = pauseTime.value; } - pauseTime.onchange = (e)=>{ - answerTime = e.target.value; + + pauseTime.onchange = e => { + showAnswerTime = e.target.value; } } -//get the next trivia entry and fill -//the question.src with new entry + +// get the next trivia entry and fill the question.src with new entry async function nextTrivia() { triviaIndex += 1; if (triviaIndex >= triviaQuestions.length) { - timerState = "paused"; - // clearRound(); + timer.pause(); showScore(); return; } - //change the trivia question.src to match new index - // question.src = `/Movie-Tracker/bg/${triviaQuestions[triviaIndex].question}`; - // question.src = `https://quantumapprentice.github.io/Movie-Tracker/bg/${triviaQuestions[triviaIndex].question}`; - question.src = await getTriviaURL(triviaIndex); - //reset for next round + // must reset timer before the async call to getTriviaURL so that we don't keep cycling to next trivia questions. resetTimer(); + question.src = await getTriviaURL(triviaIndex); + + // reset for next round multipleChoice(); - // clearRound(); } + async function restartTrivia() { - // triviaQuestions = await createQuestions(); - - console.log("restarting"); resetAnswered(); resetWinners(); await createQuestions(); - // endTime = performance.now() + 1000*countdownTime; resetTimer(); triviaIndex = 0; multipleChoice(); - const title = document.getElementById("title"); - title.innerText = "Name That Movie"; - const scoreCard = document.getElementById("score"); - question = document.createElement("img"); - // question.src = `/Movie-Tracker/bg/${triviaQuestions[0].question}`; - question.src = await getTriviaURL(0); - question.id = "question"; + getById("title").innerText = "Name That Movie"; + const scoreCard = getById("score"); + question = createEl("img", { + src: await getTriviaURL(0), + id: "question" + }); scoreCard.replaceWith(question); - // triviaDiv.appendChild(question); - timer.innerText = countdownTime; + timer.setText(countdownTime); } + function restartTimer() { - const countdown = timer.innerText; - if (Number(countdown)) { - endTime = performance.now() + 1000*countdown; + const countdown = timer.getText(); + if (countdown) { + stopwatch.setEnd(+countdown); } - timerState = "running"; + timer.run(); } + + function resetTimer() { - timer.innerText = countdownTime; - endTime = performance.now() + countdownTime*1000; - timerState = "running"; + timer.setText(countdownTime); + stopwatch.setEnd(countdownTime); + timer.run(); } + async function startChat(chatName, chatParser) { - let chatMSG = document.createElement("div"); - let auth = document.createElement("div"); - if (!twitchChatWS) { - } else { + if (twitchChatWS) { if (twitchChatWS.readyState === twitchChatWS.OPEN) { twitchChatWS.close(); - parseChatCallback("TriviaBot", - `Disconnecting from ${chatName} and...`, - auth, chatMSG); + parseChatCallback("TriviaBot", `Disconnecting from ${chatName} and...`); } } - //used the globalThis variable to share twitch_chat - //this bypasses module requirements for import + // used the globalThis variable to share twitch_chat + // this bypasses module requirements for import twitchChatWS = await globalThis.twitchChatConnect(chatName, chatParser); - - // console.log("twitchChatWS?",twitchChatWS); - - } function parseChatCallback(name, outmsg, auth, chatMSG) { + if (!auth) auth = createEl('div'); + if (!chatMSG) chatMSG = createEl('div'); + const winner = parseTriviaChat(name, outmsg); //for some reason the twitch chat api sends a message @@ -448,22 +424,17 @@ function parseChatCallback(name, outmsg, auth, chatMSG) //but does nothing when chat connection succeeds if (outmsg === "This channel does not exist or has been suspended.") { connectBtn.innerText = "Connect to Twitch Chat"; - // console.log("a",auth); - // console.log("n",name); - // auth.innerText = name; } - // console.log('chatting?'); - - //option to hide chat except for - //those who guess correctly - const chatBody = document.getElementById("twitchChat"); + //option to hide chat except for those who guess correctly + const chatBody = getById("twitchChat"); let hideChat = false; if (hideChat) { if (winner.won) { - let msg = document.createElement("div"); + const msg = createEl("div", { + innerHTML: outmsg + }); msg.classList.add("msg"); - msg.innerHTML = outmsg; msg.classList.add("winner"); auth.classList.add("winner"); @@ -473,22 +444,22 @@ function parseChatCallback(name, outmsg, auth, chatMSG) chatBody.prepend(chatMSG); } } else { - let msg = document.createElement("div"); + const msg = createEl("div", { + innerHTML: outmsg + }); msg.classList.add("msg"); - msg.innerHTML = outmsg; if (winner.won) { msg.classList.add("winner"); auth.classList.add("winner"); - document.getElementById(`${Number(outmsg)}`).classList.add("winnerChoice"); - document.getElementById("sadTrombone").play(); + getById(`${Number(outmsg)}`).classList.add("winnerChoice"); + getById("sadTrombone").play(); } msg.innerText += winner.str; chatMSG.append(auth, msg); // chat message has to be prepended to appear on bottom - // const chatBody = document.getElementById("twitchChat"); chatBody.prepend(chatMSG); } chatMSG.classList.add("message_box"); @@ -497,27 +468,19 @@ function parseChatCallback(name, outmsg, auth, chatMSG) if (chatBody.children.length > maxMsgCount) { chatBody.lastElementChild.remove(); } +} - -} play_trivia(); + async function loadTMDB() { - // const res = await fetch("/Movie-Tracker/src/tmdbList.json"); - // const res = await fetch(`https://raw.githubusercontent.com/QuantumApprentice/Movie-Tracker/refs/heads/master/src/tmdbList.json`); - // if (!res.ok) { - // throw new Error(`Response failed? ${res.status}`); - // } - // return res.json(); - return fetch(`https://raw.githubusercontent.com/QuantumApprentice/Movie-Tracker/refs/heads/master/src/tmdbList.json`) - .then((r)=>{ - if (!r.ok) { - throw new Error(`Response failed? ${r.status}`); - } - return r.json() - }); + const r = await fetch(`https://raw.githubusercontent.com/QuantumApprentice/Movie-Tracker/refs/heads/master/src/tmdbList.json`); + if (!r.ok) { + throw new Error(`Response failed? ${JSON.stringify(r)}`); + } + return r.json(); } @@ -531,238 +494,143 @@ async function createQuestions() for (let i = 0; i < questionCount; i++) { let currIndex; do { - currIndex = Math.floor(Math.random() * tmdbList.length); - } while ( - indexArr.includes(currIndex) - || !tmdbList[currIndex].bg - ); + currIndex = randInt(tmdbList.length); + } while (indexArr.includes(currIndex) || !tmdbList[currIndex].bg); indexArr[i] = currIndex; } - triviaQuestions = indexArr.map((e)=>{ + triviaQuestions = indexArr.map(i => { return { - answer: tmdbList[e].title, - question: tmdbList[e].bg + answer: tmdbList[i].title, + question: tmdbList[i].bg } }); } -function parseTriviaChat(name, outmsg) + +function createAnswers() { - if (triviaIndex >= triviaQuestions.length) { - return {won: false, str: ""}; //should show scoreboard - } - if (winners[triviaIndex]) { - return {won: false, str: ""}; //round over, wait for next round - } - if (answered.includes(name)) { - return {won: false, str: " -- Oops, you already played this round."}; //already answered incorrectly - } + let nextQuestion = triviaQuestions[triviaIndex % triviaQuestions.length]; + + // prevent same answer index from appearing twice in a row + const nextAnswerIndex = ((correctAnsIdx || 1) + randInt(3)) % 4; + correctAnsIdx = nextAnswerIndex + 1; + const newAnswers = [{ ...nextQuestion }]; - // console.log("outmsg: ", outmsg); - // console.log("answer: ", correctAnsIdx); - if (Number(outmsg) === correctAnsIdx) { - winners.push(name); - endTime = performance.now(); - score[name] = score[name] ? (score[name]+=1) : 1; - return {won: true, str: ""}; //winner through multiple choice + const foundAnswerTitles = new Set(); + + while (newAnswers.length < 4) { + const rng = randInt(tmdbList.length); + if (!foundAnswerTitles.has(tmdbList[rng].title)) { + newAnswers.push({ + answer: tmdbList[rng].title, + question: tmdbList[rng].bg + }); + foundAnswerTitles.add(tmdbList[rng].title); + } } - if (outmsg.toLowerCase().indexOf(triviaQuestions[triviaIndex].answer) != -1) { - winners.push(name); - endTime = performance.now(); - score[name] = score[name] ? (score[name]+=1) : 1; - return {won: true, str: " -- Oh wow, you actually typed it out?"}; //won by typing name? + + if (nextAnswerIndex != 0) { + const t = newAnswers[nextAnswerIndex]; + newAnswers[nextAnswerIndex] = newAnswers[0]; + newAnswers[0] = t; } - if (!isNaN(outmsg) && (Number(outmsg) > 0 && Number(outmsg) < 5)) { - answered.push(name); - return {won: false, str: " -- Sorry, you didn't win this time."}; + + return newAnswers; +} + + +function parseTriviaChat(name, outmsg) +{ + const showScoreboard = triviaIndex >= triviaQuestions.length; + const roundOver = winners[triviaIndex]; + if (showScoreboard || roundOver) { + return { won: false, str: "" }; + } + else if (answered.includes(name)) { + return { won: false, str: " -- Oops, you already played this round." }; + } + else { + const guessedCorrectNumber = Number(outmsg) === correctAnsIdx; + const guessedPartialTitle = outmsg.toLowerCase().indexOf(triviaQuestions[triviaIndex].answer) != -1; + if (guessedCorrectNumber || guessedPartialTitle) { + winners.push(name); + stopwatch.setEndNow(); + score[name] = score[name] ? score[name] + 1 : 1; + return { won: true, str: guessedCorrectNumber ? "" : " -- Oh wow, you actually typed it out?" }; + } + else { + outmsg = +outmsg; + const isValidNumber = outmsg > 0 && outmsg < 5; + if (isValidNumber) { + answered.push(name); + return { won: false, str: " -- Sorry, you didn't win this time." }; + } + else { + // all regular chat + return { won: false, str: "" }; + } + } } - return {won: false, str: ""}; //all regular chat } +// reset so same people can answer again function resetAnswered() { - answered = []; //reset so same people can answer again + answered = []; } + function resetWinners() { winners = []; } -function createAnswers() -{ - resetAnswered(); - ///////////////////////////////////////////// - //QuantumApprentice - // let answers = new Set(); - // let ansArr = [triviaQuestions[triviaIndex].answer]; - // answers.add(triviaQuestions[triviaIndex].answer); - // while (answers.size < 4) { - // let randAnswer = triviaQuestions[Math.floor(Math.random() * triviaQuestions.length)].answer; - // if (!answers.has(randAnswer)) { - // answers.add(randAnswer); - // ansArr.push(randAnswer); - // } - // } - // let swap = Math.floor(Math.random()*4); - // let temp = ansArr[swap]; - // ansArr[0] = temp; - // ansArr[swap] = triviaQuestions[triviaIndex].answer; - // correctAnsIdx = swap+1; - // console.log(ansArr); - - - ///////////////////////////////////////////// - //BakerStaunch - let question = triviaQuestions[triviaIndex]; //does not modify original array - console.log("answer", question.answer); - - let answersB = Array(4); - let answerIndex; - //prevent same answer index from appearing twice in a row - do { - answerIndex = Math.floor(Math.random()*4); - } while (answerIndex == correctAnsIdx-1); - - correctAnsIdx = answerIndex+1; - answersB[answerIndex] = {...question}; - - for (let i = 1; i <= 3; i += 1) { - let wrongAnswer; - while (answersB.includes(wrongAnswer)) { - - //TODO: might need to swap out this while loop - // for something that deep checks objects? - // while (answersB.find((e)=>{ - // console.log("e",e); - // if (!e) { - // return false; - // } - // return (e?.answer === wrongAnswer?.answer); - // })) { - - - - //get another random answer and - //set it at index (answerIndex + i) % 4 - let rng = Math.floor(Math.random() * tmdbList.length); - wrongAnswer = { - answer: tmdbList[rng].title, - question: tmdbList[rng].bg - } - } - answersB[(answerIndex + i) %4] = wrongAnswer; - } - - const ansArr = answersB; - // console.log(answersB) - - //Eskiminha (chatGPT) - ///////////////////////////////////////////// - // const questions = triviaQuestions; - // const { q, a } = { - // q: questions[Math.floor(Math.random() * questions.length)], - // a: ((q, a = Array(4).fill(null), - // u = new Set(a[(i = Math.floor(Math.random() * 4))] = q.answer) - // ) => (Array(4).fill(0).forEach( - // (_, j) => j - i || ( - // () => { - // let x; - // do x = questions[Math.floor(Math.random() * questions.length)].answer; - // while ( - // u.has(x) - // ); - // a[j] = x; - // u.add(x); - // })()), - // a)) - // (questions[Math.floor( - // Math.random() * questions.length - // )]) - // }; - // console.log("Question:", q, "Answers:", a); - // const ignore = null; - - //tvjosh - ///////////////////////////////////////////// - // const ls = Array(10).fill().map((_, i) => i); - // const ls = triviaQuestions; - // const numToShuffle = 4; - // const copyLs = ls.slice().map((x, i, thisLs) => { - // if (i < numToShuffle) { - // // const k = Math.floor(Math.random() * (ls.length/numToShuffle) + i*(ls.length/numToShuffle)); - // const k = Math.floor(Math.random() * (ls.length - i)) + i; - // const t = thisLs[i]; - // thisLs[i] = thisLs[k]; - // thisLs[k] = t; - // return thisLs[i]; - // } - // return x - // }).slice(0, numToShuffle); - // console.log(copyLs); - - - ///////////////////////////////////////////// - //FrankL81 - // const Questions = triviaQuestions; - // console.log(Questions); - // const schuffle = (array) => array.sort(() => Math.random() < 0.5 ? 1 : -1); - // const currentQuestion = schuffle(Questions).slice(0,4).map((e)=>e.answer); - // // const [imgSrc,answer] = currentQuestion[0]; - // ansArr = currentQuestion; - // console.log("ansArr", ansArr); - // document.getElementById("canvas").innerHTML = - // `${imgSrc}
${schuffle(currentQuestion.map(([,answer],idx) => ``)).join("
")} - //

- // currentAnswer is: ${answer}`; - - - ///////////////////////////////////////////// - return ansArr; -} function multipleChoice() { - const choiceDiv = document.getElementById("choiceDiv"); + const choiceDiv = getById("choiceDiv"); choiceDiv.innerHTML = ""; //clear previous answer choices - // const timeStart = performance.now(); + resetAnswered(); + const ansArr = createAnswers(); - //create array of answer buttons - choiceBtnArr[] - //and fill with ansArr[] answers + //create array of answer buttons - choiceBtnArr[] and fill with ansArr[] answers let choiceBtnArr = []; for (let i = 0; i < 4; i++) { - const choiceBtnDiv = document.createElement("div"); - choiceBtnDiv.className = "choiceBtn"; - choiceBtnDiv.id = `${i+1}`; + const choiceBtnDiv = createEl("div", { + className: "choiceBtn", + id: `${i + 1}` + }); - const choiceAns = document.createElement("div"); - choiceAns.className = "choiceTxt"; - choiceAns.innerText = `${ansArr[i].answer}`; + const choiceAns = createEl("div", { + className: "choiceTxt", + innerText: `${ansArr[i].answer}` + }); - const choiceNum = document.createElement("div"); - choiceNum.className = "choiceNum"; - choiceNum.innerText = `${i+1}`; + const choiceNum = createEl("div", { + className: "choiceNum", + innerText: `${i + 1}` + }); choiceBtnDiv.append(choiceNum); choiceBtnDiv.append(choiceAns); choiceBtnDiv.onclick = handleClick; choiceBtnArr.push(choiceBtnDiv); + + choiceDiv.append(choiceBtnDiv); } - //onClick for the right answer only - //(maybe add wrong answer stuff?) - function handleClick(e) { - // console.log("e",e.currentTarget); + // onClick for the right answer only (maybe add wrong answer stuff?) + function handleClick(e) { //if button has the correct answer... if (e.currentTarget.lastChild.textContent === triviaQuestions[triviaIndex].answer) { //display correct answer in timer - if (answerBtn.innerText !== "Next") { - answerBtn.innerText = "Next"; - endTime = performance.now(); - timer.innerText = triviaQuestions[triviaIndex].answer; - score["Me"] = score["Me"] ? (score["Me"]+1) : 1; + if (!buttons.isShowingNext()) { + buttons.setAnswerText("Next"); + stopwatch.setEndNow(); + timer.setText(triviaQuestions[triviaIndex].answer); + score["Me"] = score["Me"] ? score["Me"] + 1 : 1; //TODO: wtf? why isn't this pointing // to the parent element directly? @@ -770,95 +638,58 @@ function multipleChoice() { // but it's null if just logging out "e" // need to log out currentTarget to see e.currentTarget.classList.add("winnerChoice"); - const sadTrombone = document.getElementById("sadTrombone"); + const sadTrombone = getById("sadTrombone"); sadTrombone.play(); } } else { e.currentTarget.classList.add("wrongChoice"); choiceBtnArr.forEach(e => { e.classList.add("disabledChoice"); - e.onclick = {}; + e.onclick = null; }); } } - - choiceBtnArr.map((e, i)=>{ - choiceDiv.append(e); - }); - -} - -function clearRound() -{ - const name = document.getElementById("twitchName"); - const chat = document.getElementById("chatMsg"); - name.innerText = ""; - chat.innerText = ""; } - - function showScore() { - // const timer = document.getElementById("timer"); - timer.innerText = ""; - const title = document.getElementById("title"); - title.innerText = "Score"; - - nextBtn.disabled = true; - prevBtn.disabled = true; - playBtn.disabled = true; - setTimeout(()=>{ - answerBtn.innerText = "Restart?"; - }, 10); - // console.log("answer:", answBtn.innerText); - answerBtn.onclick = ()=>{ - nextBtn.disabled = false; - prevBtn.disabled = false; - playBtn.disabled = false; - setTimeout(()=>{ - answerBtn.innerText = "Answer"; - }, 10); + timer.setText(""); + getById("title").innerText = "Score"; + buttons.showRestart(() => { initButtons(); restartTrivia(); - } + }); - // triviaDiv.removeChild(document.getElementById("question")); - const scoreCard = document.createElement("table"); - // triviaDiv.appendChild(scoreCard); - scoreCard.className = "scoreCard"; - scoreCard.id = "score"; + const scoreCard = createEl("table", { + className: "scoreCard", + id: "score" + }); question.replaceWith(scoreCard); const scoreArr = Object.entries(score); - scoreArr.sort((a, b)=>{ - return b[1]-a[1]; + scoreArr.sort((a, b) => { + return b[1] - a[1]; }); - // console.log("score", scoreArr); - scoreArr.forEach((e)=>{ - const row = document.createElement("tr"); - const td1 = document.createElement("td"); - const td2 = document.createElement("td"); - td1.className = "scoreName"; - td1.innerText = e[0]; - const r = Math.floor(Math.random()*255); - const g = Math.floor(Math.random()*255); - const b = Math.floor(Math.random()*255); - - td1.style = `color : rgb(${r}, ${g}, ${b});`; + scoreArr.forEach(e => { + const td1 = createEl("td", { + className: "scoreName", + innerText: e[0], + style: `color : rgb(${randInt(255)} ${randInt(255)} ${randInt(255)});` + }); - td2.innerText = e[1]; - td2.className = "scoreAmount"; + const td2 = createEl("td", { + innerText: e[1], + className: "scoreAmount" + }); + const row = createEl("tr"); row.appendChild(td1); row.appendChild(td2); scoreCard.appendChild(row); }); } -// #region this is cool - From aaff232fddae5adaa3fb65818523c115eb7bbad8 Mon Sep 17 00:00:00 2001 From: ZennilGreenherald <144579645+ZennilGreenherald@users.noreply.github.com> Date: Sat, 8 Nov 2025 17:54:34 -0800 Subject: [PATCH 3/4] Update index.html --- index.html | 382 +---------------------------------------------------- 1 file changed, 2 insertions(+), 380 deletions(-) diff --git a/index.html b/index.html index 45c9eae..61014b7 100644 --- a/index.html +++ b/index.html @@ -1,369 +1,7 @@ - + Movie Trivia @@ -404,18 +42,14 @@ -
-
Name That Movie!
-
-
@@ -429,26 +63,14 @@
- -
- -
- - - -
- - - - - \ No newline at end of file + From 34bd031f6246ebb2bd6252ba8e862a2d1d93c8fc Mon Sep 17 00:00:00 2001 From: ZennilGreenherald <144579645+ZennilGreenherald@users.noreply.github.com> Date: Sat, 8 Nov 2025 17:54:55 -0800 Subject: [PATCH 4/4] Create styles.css --- styles.css | 324 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 324 insertions(+) create mode 100644 styles.css diff --git a/styles.css b/styles.css new file mode 100644 index 0000000..d0b247e --- /dev/null +++ b/styles.css @@ -0,0 +1,324 @@ +* { + background-color: rgb(30, 30, 30); + box-sizing: border-box; +} +html, body { + margin: 0; + padding: 0; +} +#timer { + position: absolute; + font-size: 64px; + color: aliceblue; + background-color: blue; + border-radius: 15px; + max-width: 50vw; + text-align: center; + padding: 10px; +} +#buttons { + display: flex; +} +#buttons > button { + color: aliceblue; + width: 25vw; + height: 7vh; + font-size: 18px; + font-weight: bold; + border: solid; +} +#buttons > button:disabled { + border-color: black; + color: black; +} +.trivia { + height: 50vh; +} +.trivia > img { + max-width: 100vw; + /* max-height: 50vh; */ + height: 50vh; + /* object-fit: scale-down; */ +} +.triviaTitle { + background-color: rgb(70, 16, 120); + width: 100vw; + font-size: 24px; + text-align: center; + color: aliceblue; +} +.scoreCard { + width: 50vw; + height: 50vh; + font-size: 24px; + color: aliceblue; + background-color: rgb(66, 66, 66); +} +.scoreName { + font-size: 32px; + text-align: center; +} +.scoreAmount { + font-size: 32px; +} +.choiceDiv { + display: flex; + flex-direction: column; + justify-content: flex-end; +} +.choiceDiv >:nth-child(1) { + background-color: rgb(11, 29, 145); +} +.choiceDiv >:nth-child(2) { + background-color: rgb(0, 52, 14); +} +.choiceDiv >:nth-child(3) { + background-color: rgb(70, 16, 120); +} +.choiceDiv >:nth-child(4) { + background-color: rgb(85, 0, 0); +} + +#twitchName { + font-size: 28px; + width: 200px; + color: aqua; +} +.name { + font-size: 28px; + color: aqua; + background-color: black; +} +.msg { + font-size: 24px; + color:blueviolet; + background-color: black; +} +#twitchChat { + display: flex; + flex-direction: column-reverse; + background-color: black; + width: 100vw; + height: 50vh; +} +.winner { + background-color: blue; +} +.choiceBtn { + /* width: 100vw; */ + height: 9vh; + display: flex; + flex-direction: row; + align-content: space-between; +} +.choiceBtn:not(.disabledChoice):hover { + cursor: pointer; + background-color:lightslategray; +} +.choiceNum { + margin-left: 20px; + font-size: 48px; + color: chocolate; + background-color: inherit; + align-content: center; +} +.choiceTxt { + width: 100vw; + height: 100%; + background-color: inherit; + display: flex; + justify-content: center; + align-items: center; + text-align: center; + color: yellow; + font-size: 3.5vh; + font-weight: 500; + line-height: 1; + font-family: system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif; + text-shadow: 2px 2px 2px #000; +} +.choiceDiv >:nth-child(1).wrongChoice { + background: linear-gradient(black, rgb(11, 29, 145)); +} +.choiceDiv >:nth-child(2).wrongChoice { + background: linear-gradient(black, rgb(0, 52, 14)); +} +.choiceDiv >:nth-child(3).wrongChoice { + background: linear-gradient(black, rgba(70, 16, 120)); +} +.choiceDiv >:nth-child(4).wrongChoice { + background: linear-gradient(black, rgba(85, 0, 0)); +} +.winnerChoice { + background-color: lightslategray !important; + border: solid red; +} +.chatConnect { + display: flex; + flex-direction: column; + background-color: black; +} +.connectBtn { + color:aliceblue; + width: 200px; + min-width: 160px; + height: 65px; + font-size: 24px; + font-weight: bold; + line-height: 100%; + z-index: 1; + display: none; +} +.gameboard { + max-width: fit-content; + margin-inline: auto; +} +.gameboardChat { + display: flex; + align-content: center; + flex-direction: column; +} +.hamburger { + position: fixed; + margin-top: 0px; + border: solid red; + /* border: #00000000; */ + pointer-events: auto; + width: 50px; + height: 40px; +} +.hamburger > * { /*hamburger slices*/ + width: 25px; + height: 5px; + background-color: gray; + margin: 5px; + transition: .5s; +} +.hamburger.change > :nth-child(1) { + transform: translate(0, 14px) rotate(-45deg); + margin-top: 0px; +} +.hamburger.change > :nth-child(2) { + opacity: 0; +} +.hamburger.change > :nth-child(3) { + transform: translate(0,-14px) rotate(45deg); + margin-bottom: 0px; +} +.sidebar { + background-color: rgba(0, 0, 0, 0.25); + color: aqua; + position: fixed; + z-index: 1; + overflow-x: hidden; + width: 0; +} +.sidebar.change { + width: 240px; +} +.sidebar > div > input { + color: aqua; + font-size: 24px; + font-weight: bold; + width: 60px; +} +.sidebar > div > label { + color: aqua; + font-size: 24px; + font-weight: bold; + position: relative; +} +.tooltip { + font-size: 20px; +} +.tooltip .tooltiptext { + width: 120px; + background-color: #555; + position: absolute; + margin-left: 90px; + color: #fff; + z-index: 1; + opacity: 0; + transition: opacity 0.3s; +} +.tooltip:hover .tooltiptext { + opacity: 1; + /* position: relative; */ +} +.soundOn { + height: 60px; + width: 60px; + background-image: url("assets/speakerOn.svg"); +} +.soundOff { + height: 60px; + width: 60px; + background-image: url("assets/speakerOff.svg"); +} +/* for screens wider than 480px */ +@media only screen and (min-width:480px) { + .gameboardChat { + align-content: center; + flex-direction: row; + } + .gameboard { + margin-inline: auto; + display: flex; + flex-direction: row-reverse; + justify-content: center; + } + + .triviaBoard { + width: 65vw; + display: flex; + flex-direction: column; + align-items: stretch; + } + .triviaTitle { + font-size: 48px; + white-space: nowrap; + max-width: 100%; + } + .trivia > img { + /* max-width: 100%; */ + /* align-items: stretch; */ + width: 100%; + height: auto; + /* object-fit: scale-down; */ + } + #twitchChat { + display: flex; + flex-direction: column-reverse; + background-color: black; + width: 15vw; + } + #buttons > button{ + height: 50px; + font-size: 24px; + } + #connectChatBtn { + display:block + } + .choiceBtn { + height: 13.8vh; + width: 20vw; + } + .choiceTxt { + /* height:12vh; */ + } + .hamburger { + position: fixed; + margin-top: 5px; + border: solid red; + pointer-events: auto; + width: 65px; + height: 60px; + } + .hamburger > * { /*hamburger slices*/ + width: 35px; + height: 7px; + background-color: gray; + margin: 7px; + transition: .5s; + } +}