diff --git a/config.xml b/config.xml index 43001ec..079f2f8 100644 --- a/config.xml +++ b/config.xml @@ -1,8 +1,8 @@ - + Impostor - A game of word guessing where an impostor is trying to get along. + A game of guessing where everyone but the impostor knows the word. Adrian Victor @@ -17,11 +17,6 @@ - - - - - - + \ No newline at end of file diff --git a/docs b/docs new file mode 120000 index 0000000..baf12b4 --- /dev/null +++ b/docs @@ -0,0 +1 @@ +www \ No newline at end of file diff --git a/package-lock.json b/package-lock.json index 6dc2446..4a2a7d6 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,7 +9,9 @@ "version": "1.0.0", "license": "Apache-2.0", "devDependencies": { - "cordova-android": "^15.0.0" + "cordova-android": "^15.0.0", + "cordova-android-stayawake": "github:rootzoll/cordova-android-stayawake", + "cordova-plugin-vibration": "^3.1.1" } }, "node_modules/@netflix/nerror": { @@ -190,6 +192,12 @@ "node": ">=20.17.0 || >=22.9.0" } }, + "node_modules/cordova-android-stayawake": { + "version": "0.0.1", + "resolved": "git+ssh://git@github.com/rootzoll/cordova-android-stayawake.git#19e13d80ed9870b7e833de1ff3049ee477179491", + "dev": true, + "license": "Apache 2.0" + }, "node_modules/cordova-common": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/cordova-common/-/cordova-common-6.0.0.tgz", @@ -209,6 +217,20 @@ "node": ">=20.9.0" } }, + "node_modules/cordova-plugin-vibration": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/cordova-plugin-vibration/-/cordova-plugin-vibration-3.1.1.tgz", + "integrity": "sha512-qgv67Rueo4Pydfant3TwnXeFiN9dl+6lKMM6h5jYg9XewiGAGOr8vfWsTvQssC3m3xMKGS1ap3xPNH+BzZ4RMA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "cordovaDependencies": { + "4.0.0": { + "cordova": ">100" + } + } + } + }, "node_modules/cross-spawn": { "version": "7.0.6", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", diff --git a/package.json b/package.json index e9bde4e..e9d3353 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "org.adrianvictor.impostor", "displayName": "Impostor", - "version": "1.0.0", + "version": "1.1.0", "description": "A game of word guessing where an impostor is trying to get along.", "main": "index.js", "scripts": { @@ -15,9 +15,15 @@ "cordova": { "platforms": [ "android" - ] + ], + "plugins": { + "cordova-plugin-vibration": {}, + "cordova-android-stayawake": {} + } }, "devDependencies": { - "cordova-android": "^15.0.0" + "cordova-android": "^15.0.0", + "cordova-android-stayawake": "github:rootzoll/cordova-android-stayawake", + "cordova-plugin-vibration": "^3.1.1" } } diff --git a/res/colors.xml b/res/colors.xml new file mode 100644 index 0000000..dab53b8 --- /dev/null +++ b/res/colors.xml @@ -0,0 +1,4 @@ + + + #000000 + \ No newline at end of file diff --git a/www/index.html b/www/index.html index 152f3d9..cafb183 100644 --- a/www/index.html +++ b/www/index.html @@ -7,30 +7,39 @@ +

Impostor

- +
-
-
- -
-
- -
-
- -
-
- -
+

Dicas

+
+

Temporizador (minutos)

+ +
+ +
+

Jogadores

+
+
+ +
+
+ +
+
+ +
+
+ +
@@ -38,6 +47,7 @@
+
diff --git a/www/script.js b/www/script.js index fecea56..bf18cab 100644 --- a/www/script.js +++ b/www/script.js @@ -4,6 +4,15 @@ const startButton = document.querySelector("#startGame"); const logArea = document.querySelector("#gameLog"); const body = document.querySelector("body"); const hintCheckbox = document.querySelector("#hintCheck"); +const timerInput = document.getElementById("timerInput"); + +const isCordova = typeof cordova !== 'undefined'; + +const theme = localStorage.getItem('theme'); + +if (theme == "light") { + body.classList.add("light-theme"); +} let stage = 0; let currentPlayer = 0; @@ -12,6 +21,7 @@ let allPlayers; let currentName; let secret; let enableHint; +let timer; nPlayerButton.addEventListener('click', () => { const holder = document.createElement("div"); @@ -66,63 +76,116 @@ startButton.addEventListener('click', () => { case -1: currentName = allPlayers[currentPlayer].value; clear(` -

Entregue o dispositivo para:
${currentName}
para continuar.

- `); - stage = 1; - break; - - case 2: { - clear("O jogo começou! Cada um deve falar uma palavra relacionada ao tema.\nProssiga após a votação."); - stage += 1; +

Entregue o dispositivo para:
${currentName}
para continuar

+ `); + stage = 1; + break; + + case 2: { + clear("O jogo começou! Cada um deve falar uma palavra relacionada ao tema.\n

Prepare-se!

"); + startTimer(timerInput.value); + stage += 1; + break; + } + case 3: { + if (isCordova && !!navigator.vibrate) { + navigator.vibrate(0); + } + + if (isCordova && !!StayAwake.enableScreenTimeout) { + StayAwake.enableScreenTimeout(); + } + clearInterval(timer); + clear("O jogo acabou! Votem para expulsar um jogador.") + stage += 1; + break; + } + + case 4: { + clear(`O impostor era ${impostor}; a palavra era ${secret.word}; a dica ${secret.hint}.`); + stage += 1; + break; + } + + default: + body.classList.remove("game"); + stage = 0; + currentPlayer = 0; break; } - - case 3: { - clear(`O impostor era ${impostor}; a palavra era ${secret.word}; a dica ${secret.hint}.`); - stage += 1; - break; + }); + + function choose() { + const randomIndex = Math.floor(Math.random() * allPlayers.length); + return allPlayers[randomIndex].value; + } + + function log(text) { + logArea.innerHTML = logArea.innerHtml + "
" + text; + } + + function clear(text) { + logArea.classList.forEach(item => { + logArea.classList.remove(item); + }) + logArea.innerHTML = text; + } + + function logClass(className) { + logArea.classList.toggle(className); + } + + async function getWordWithHint() { + try { + const response = await fetch('words.json'); + const data = await response.json(); + const randomWordObj = data[Math.floor(Math.random() * data.length)]; + const randomHint = randomWordObj.hints[Math.floor(Math.random() * randomWordObj.hints.length)]; + + return { + word: randomWordObj.word, + hint: randomHint + }; + } catch (error) { + console.error('Error loading words:', error); } - - default: - body.classList.remove("game"); - stage = 0; - currentPlayer = 0; - break; } -}); - -function choose() { - const randomIndex = Math.floor(Math.random() * allPlayers.length); - return allPlayers[randomIndex].value; -} - -function log(text) { - logArea.innerHTML = logArea.innerHtml + "
" + text; -} - -function clear(text) { - logArea.classList.forEach(item => { - logArea.classList.remove(item); - }) - logArea.innerHTML = text; -} - -function logClass(className) { - logArea.classList.toggle(className); -} - -async function getWordWithHint() { - try { - const response = await fetch('words.json'); - const data = await response.json(); - const randomWordObj = data[Math.floor(Math.random() * data.length)]; - const randomHint = randomWordObj.hints[Math.floor(Math.random() * randomWordObj.hints.length)]; - - return { - word: randomWordObj.word, - hint: randomHint - }; - } catch (error) { - console.error('Error loading words:', error); + + function startTimer(minutes) { + const now = new Date(); + const minutesLater = new Date(now.getTime() + minutes * 60 * 1000); + timer = setInterval(() => { + const timerElement = document.getElementById("timer"); + if (!timerElement) { return; } + const rightNow = new Date().getTime(); + var distance = minutesLater.getTime() - rightNow; + var minutes = Math.floor((distance % (1000 * 60 * 60)) / (1000 * 60)); + var seconds = Math.floor((distance % (1000 * 60)) / 1000); + timerElement.innerText = `${minutes.toString().padStart(2, '0')}:${seconds.toString().padStart(2, '0')}`; + + if (isCordova && !!StayAwake.disableScreenTimeout) { + StayAwake.disableScreenTimeout(); + } + + if (distance < 0) { + clearInterval(timer); + + if (!!navigator.vibrate) { + navigator.vibrate([1000, 500, 1000, 2000, 1000, 500, 1000, 2000, 1000, 500, 1000, 2000]); + } + + if (isCordova && !!StayAwake.enableScreenTimeout) { + StayAwake.enableScreenTimeout(); + } + + timerElement.innerText = "Acabou!"; + } + }, 1000) } -} \ No newline at end of file + + + function changeTheme(){ + body.classList.toggle('light-theme'); + console.log(body.classList.contains('light-theme') ? "light" : "dark") + localStorage.setItem('theme', body.classList.contains('light-theme') ? "light" : "dark"); + } \ No newline at end of file diff --git a/www/style.css b/www/style.css index 82b0f6c..e44f0cb 100644 --- a/www/style.css +++ b/www/style.css @@ -1,5 +1,6 @@ :root { --background: black; + --backgroundLighter: rgb(20, 20, 20); --foreground: white; --saBORbackground: rgba(54, 54, 54, 0.5); --saBORforeground: darkgrey; @@ -7,10 +8,24 @@ --civilColor: rgb(0, 50, 0); } +.light-theme { + --background: white; + --backgroundLighter: rgb(230, 230, 230); + --foreground: black; + --saBORbackground: rgba(200, 200, 200, 0.5); + --saBORforeground: darkgrey; + --impostorColor: rgb(200, 120, 120); + --civilColor: rgb(120, 200, 120); +} + * { + transition: .2s; margin: 0; padding: 0; box-sizing: border-box; + scrollbar-color: var(--foreground) transparent; + scrollbar-width: thin; + user-select: none; } body { @@ -23,8 +38,11 @@ body { padding: 2em; gap: 1em; height: 100vh; + max-height: 100vh; width: 30vw; margin: auto; + overflow: hidden; + scroll-behavior: smooth; } header { @@ -92,19 +110,21 @@ button { } /* button:hover { - background-color: var(--foreground); - color: var(--background); +background-color: var(--foreground); +color: var(--background); } */ .playerHolder { margin-bottom: 1em; display: flex; gap: .6em; + overflow-y: auto; + overflow-x: hidden; button { background-color: transparent; font-family: monospace; - color: white; + color: var(--foreground); text-shadow: none; } } @@ -115,12 +135,11 @@ input:focus { #newPlayerButton { width: 100%; - margin-bottom: 1em; } #startGame { - margin-top: auto; - margin-left: auto; + /* margin-top: auto; + margin-left: auto; */ } #gameLog { @@ -128,6 +147,7 @@ input:focus { margin: auto 0; text-align: center; transition: .4s; + border-width: medium 0; p { margin: 0; @@ -142,6 +162,17 @@ input:focus { background-color: var(--civilColor); } +#playlistHolder { + flex: 1; + overflow-y: auto; + overflow-x: hidden; + padding-right: 0.5em; + padding: 1em .4em 0 .4em; + background-color: var(--backgroundLighter); + border-top: medium solid var(--saBORbackground); + border-radius: 0 0 10px 10px; +} + body.game #playlistHolder { display: none; } @@ -150,14 +181,20 @@ body.game #gameLog { display: unset; } +body.game header { + display: none; +} + #bottomBar { - margin-top: auto; display: flex; - gap: 1em; + gap: .6em; + flex-shrink: 0; + margin-top: 0; + justify-content: end; } #hintCheckHolder p { - margin: auto 0 auto 0; + margin: auto auto auto 0; } #hintCheckHolder { @@ -165,11 +202,34 @@ body.game #gameLog { gap: .4em; } +#timerInput { + width: 100%; +} + +.optionsHolder { + border-bottom: medium solid var(--saBORbackground); + margin-bottom: 1em; + border-radius: 5px; + padding: 0 .8em 1em; + + p { + margin-bottom: .4em; + } +} + +.optionsHolder:last-child { + border-bottom: none; +} + .playerName { font-size: x-large; font-weight: bolder; } +#timer { + font-size: xx-large; +} + .rainbowText { background: linear-gradient(to right, var(--foreground), var(--background), var(--foreground), var(--impostorColor), var(--foreground), var(--civilColor)); -webkit-background-clip: text; background-clip: text; @@ -197,6 +257,6 @@ body.game #gameLog { @media (max-width: 720px) { body { - width: 96vw; + width: 100vw; } } \ No newline at end of file