Compare commits

..

6 commits

8 changed files with 252 additions and 91 deletions

View file

@ -1,8 +1,8 @@
<?xml version='1.0' encoding='utf-8'?>
<widget id="org.adrianvictor.impostor" version="1.0.0" xmlns="http://www.w3.org/ns/widgets" xmlns:cdv="http://cordova.apache.org/ns/1.0">
<widget id="org.adrianvictor.impostor" version="1.1.0" xmlns="http://www.w3.org/ns/widgets" xmlns:cdv="http://cordova.apache.org/ns/1.0">
<name>Impostor</name>
<description>
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.
</description>
<author email="adrianvictor@disroot.org" href="https://adrianvic.github.io">
Adrian Victor
@ -17,11 +17,6 @@
<icon src="res/icon/android/hdpi.png" density="hdpi" />
<icon src="res/icon/android/mdpi.png" density="mdpi" />
<icon src="res/icon/android/ldpi.png" density="ldpi" />
<splash src="res/screen/android/splash-port-ldpi.png" density="port-ldpi" />
<splash src="res/screen/android/splash-port-mdpi.png" density="port-mdpi" />
<splash src="res/screen/android/splash-port-hdpi.png" density="port-hdpi" />
<splash src="res/screen/android/splash-port-xhdpi.png" density="port-xhdpi" />
<splash src="res/screen/android/splash-port-xxhdpi.png" density="port-xxhdpi" />
<splash src="res/screen/android/splash-land-ldpi.png" density="land-ldpi" />
<preference name="SplashScreen" value="none"/>
</platform>
</widget>

1
docs Symbolic link
View file

@ -0,0 +1 @@
www

24
package-lock.json generated
View file

@ -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",

View file

@ -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"
}
}

4
res/colors.xml Normal file
View file

@ -0,0 +1,4 @@
<?xml version='1.0' encoding='utf-8'?>
<resources>
<color name="cdv_splashscreen_background">#000000</color>
</resources>

View file

@ -7,30 +7,39 @@
<link rel="stylesheet" href="style.css">
<script src="script.js" defer></script>
<script type="text/javascript" src="cordova.js"></script>
<script src="StayAwake.js"></script>
</head>
<body>
<header>
<img src="logo.png">
<h1 class="rainbowText">Impostor</h1>
</header>
<div id="playlistHolder">
<div id="playersList">
<div class="playerHolder">
<input type="text" placeholder="Nome do primeiro jogador"/>
</div>
<div class="playerHolder">
<input type="text" placeholder="Nome do segundo jogador"/>
</div>
<div class="playerHolder">
<input type="text" placeholder="Nome do terceiro jogador"/>
</div>
</div>
<button id="newPlayerButton">Adicionar jogador</button>
<div id="hintCheckHolder">
<div id="hintCheckHolder" class="optionsHolder">
<p>Dicas</p>
<input type="checkbox" id="hintCheck" checked>
</div>
<div id="timerInputHolder" class="optionsHolder">
<p>Temporizador (minutos)</p>
<input type="number" max="" id="timerInput" value="3" >
</div>
<div class="optionsHolder">
<p>Jogadores</p>
<div id="playersList">
<div class="playerHolder">
<input type="text" placeholder="Nome do primeiro jogador"/>
</div>
<div class="playerHolder">
<input type="text" placeholder="Nome do segundo jogador"/>
</div>
<div class="playerHolder">
<input type="text" placeholder="Nome do terceiro jogador"/>
</div>
</div>
<button id="newPlayerButton">Adicionar jogador</button>
</div>
</div>
<div name="gameLog" id="gameLog">
@ -38,6 +47,7 @@
</div>
<div id="bottomBar">
<button id="changeTheme" onclick="changeTheme()">Mudar tema</button>
<button id="startGame">Próximo</button>
</div>
</body>

View file

@ -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(`
<p>Entregue o dispositivo para:<br><span class="playerName">${currentName}</span><br>para continuar.</p>
`);
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;
<p>Entregue o dispositivo para:<br><span class="playerName">${currentName}</span><br>para continuar</p>
`);
stage = 1;
break;
case 2: {
clear("O jogo começou! Cada um deve falar uma palavra relacionada ao tema.\n<p id='timer'>Prepare-se!</p>");
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 <b>${impostor}</b>; a palavra era <b>${secret.word}</b>; a dica <b>${secret.hint}</b>.`);
stage += 1;
break;
}
default:
body.classList.remove("game");
stage = 0;
currentPlayer = 0;
break;
}
case 3: {
clear(`O impostor era <b>${impostor}</b>; a palavra era <b>${secret.word}</b>; a dica <b>${secret.hint}</b>.`);
stage += 1;
break;
});
function choose() {
const randomIndex = Math.floor(Math.random() * allPlayers.length);
return allPlayers[randomIndex].value;
}
function log(text) {
logArea.innerHTML = logArea.innerHtml + "<br>" + 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 + "<br>" + 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)
}
}
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");
}

View file

@ -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;
}
}