Better visuals, add timer, add vibration on mobile, add light theme.
This commit is contained in:
parent
edf3d840c0
commit
37ca135c28
7 changed files with 243 additions and 88 deletions
11
config.xml
11
config.xml
|
|
@ -1,8 +1,8 @@
|
||||||
<?xml version='1.0' encoding='utf-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>
|
<name>Impostor</name>
|
||||||
<description>
|
<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>
|
</description>
|
||||||
<author email="adrianvictor@disroot.org" href="https://adrianvic.github.io">
|
<author email="adrianvictor@disroot.org" href="https://adrianvic.github.io">
|
||||||
Adrian Victor
|
Adrian Victor
|
||||||
|
|
@ -17,11 +17,6 @@
|
||||||
<icon src="res/icon/android/hdpi.png" density="hdpi" />
|
<icon src="res/icon/android/hdpi.png" density="hdpi" />
|
||||||
<icon src="res/icon/android/mdpi.png" density="mdpi" />
|
<icon src="res/icon/android/mdpi.png" density="mdpi" />
|
||||||
<icon src="res/icon/android/ldpi.png" density="ldpi" />
|
<icon src="res/icon/android/ldpi.png" density="ldpi" />
|
||||||
<splash src="res/screen/android/splash-port-ldpi.png" density="port-ldpi" />
|
<preference name="SplashScreen" value="none"/>
|
||||||
<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" />
|
|
||||||
</platform>
|
</platform>
|
||||||
</widget>
|
</widget>
|
||||||
24
package-lock.json
generated
24
package-lock.json
generated
|
|
@ -9,7 +9,9 @@
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"license": "Apache-2.0",
|
"license": "Apache-2.0",
|
||||||
"devDependencies": {
|
"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": {
|
"node_modules/@netflix/nerror": {
|
||||||
|
|
@ -190,6 +192,12 @@
|
||||||
"node": ">=20.17.0 || >=22.9.0"
|
"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": {
|
"node_modules/cordova-common": {
|
||||||
"version": "6.0.0",
|
"version": "6.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/cordova-common/-/cordova-common-6.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/cordova-common/-/cordova-common-6.0.0.tgz",
|
||||||
|
|
@ -209,6 +217,20 @@
|
||||||
"node": ">=20.9.0"
|
"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": {
|
"node_modules/cross-spawn": {
|
||||||
"version": "7.0.6",
|
"version": "7.0.6",
|
||||||
"resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz",
|
"resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz",
|
||||||
|
|
|
||||||
12
package.json
12
package.json
|
|
@ -1,7 +1,7 @@
|
||||||
{
|
{
|
||||||
"name": "org.adrianvictor.impostor",
|
"name": "org.adrianvictor.impostor",
|
||||||
"displayName": "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.",
|
"description": "A game of word guessing where an impostor is trying to get along.",
|
||||||
"main": "index.js",
|
"main": "index.js",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
|
|
@ -15,9 +15,15 @@
|
||||||
"cordova": {
|
"cordova": {
|
||||||
"platforms": [
|
"platforms": [
|
||||||
"android"
|
"android"
|
||||||
]
|
],
|
||||||
|
"plugins": {
|
||||||
|
"cordova-plugin-vibration": {},
|
||||||
|
"cordova-android-stayawake": {}
|
||||||
|
}
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"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
4
res/colors.xml
Normal file
|
|
@ -0,0 +1,4 @@
|
||||||
|
<?xml version='1.0' encoding='utf-8'?>
|
||||||
|
<resources>
|
||||||
|
<color name="cdv_splashscreen_background">#000000</color>
|
||||||
|
</resources>
|
||||||
|
|
@ -7,6 +7,7 @@
|
||||||
<link rel="stylesheet" href="style.css">
|
<link rel="stylesheet" href="style.css">
|
||||||
<script src="script.js" defer></script>
|
<script src="script.js" defer></script>
|
||||||
<script type="text/javascript" src="cordova.js"></script>
|
<script type="text/javascript" src="cordova.js"></script>
|
||||||
|
<script src="StayAwake.js"></script>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<header>
|
<header>
|
||||||
|
|
@ -15,6 +16,17 @@
|
||||||
</header>
|
</header>
|
||||||
|
|
||||||
<div id="playlistHolder">
|
<div id="playlistHolder">
|
||||||
|
<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 id="playersList">
|
||||||
<div class="playerHolder">
|
<div class="playerHolder">
|
||||||
<input type="text" placeholder="Nome do primeiro jogador"/>
|
<input type="text" placeholder="Nome do primeiro jogador"/>
|
||||||
|
|
@ -27,9 +39,6 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<button id="newPlayerButton">Adicionar jogador</button>
|
<button id="newPlayerButton">Adicionar jogador</button>
|
||||||
<div id="hintCheckHolder">
|
|
||||||
<p>Dicas</p>
|
|
||||||
<input type="checkbox" id="hintCheck" checked>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
@ -38,6 +47,7 @@
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div id="bottomBar">
|
<div id="bottomBar">
|
||||||
|
<button id="changeTheme" onclick="changeTheme()">Mudar tema</button>
|
||||||
<button id="startGame">Próximo</button>
|
<button id="startGame">Próximo</button>
|
||||||
</div>
|
</div>
|
||||||
</body>
|
</body>
|
||||||
|
|
|
||||||
|
|
@ -4,6 +4,13 @@ const startButton = document.querySelector("#startGame");
|
||||||
const logArea = document.querySelector("#gameLog");
|
const logArea = document.querySelector("#gameLog");
|
||||||
const body = document.querySelector("body");
|
const body = document.querySelector("body");
|
||||||
const hintCheckbox = document.querySelector("#hintCheck");
|
const hintCheckbox = document.querySelector("#hintCheck");
|
||||||
|
const timerInput = document.getElementById("timerInput");
|
||||||
|
|
||||||
|
const theme = localStorage.getItem('theme');
|
||||||
|
|
||||||
|
if (theme == "light") {
|
||||||
|
body.classList.add("light-theme");
|
||||||
|
}
|
||||||
|
|
||||||
let stage = 0;
|
let stage = 0;
|
||||||
let currentPlayer = 0;
|
let currentPlayer = 0;
|
||||||
|
|
@ -12,6 +19,7 @@ let allPlayers;
|
||||||
let currentName;
|
let currentName;
|
||||||
let secret;
|
let secret;
|
||||||
let enableHint;
|
let enableHint;
|
||||||
|
let timer;
|
||||||
|
|
||||||
nPlayerButton.addEventListener('click', () => {
|
nPlayerButton.addEventListener('click', () => {
|
||||||
const holder = document.createElement("div");
|
const holder = document.createElement("div");
|
||||||
|
|
@ -66,18 +74,31 @@ startButton.addEventListener('click', () => {
|
||||||
case -1:
|
case -1:
|
||||||
currentName = allPlayers[currentPlayer].value;
|
currentName = allPlayers[currentPlayer].value;
|
||||||
clear(`
|
clear(`
|
||||||
<p>Entregue o dispositivo para:<br><span class="playerName">${currentName}</span><br>para continuar.</p>
|
<p>Entregue o dispositivo para:<br><span class="playerName">${currentName}</span><br>para continuar</p>
|
||||||
`);
|
`);
|
||||||
stage = 1;
|
stage = 1;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 2: {
|
case 2: {
|
||||||
clear("O jogo começou! Cada um deve falar uma palavra relacionada ao tema.\nProssiga após a votação.");
|
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 (!!navigator.vibrate) {
|
||||||
|
navigator.vibrate(0);
|
||||||
|
}
|
||||||
|
if (!!StayAwake.enableScreenTimeout) {
|
||||||
|
StayAwake.enableScreenTimeout();
|
||||||
|
}
|
||||||
|
clearInterval(timer);
|
||||||
|
clear("O jogo acabou! Votem para expulsar um jogador.")
|
||||||
stage += 1;
|
stage += 1;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
case 3: {
|
case 4: {
|
||||||
clear(`O impostor era <b>${impostor}</b>; a palavra era <b>${secret.word}</b>; a dica <b>${secret.hint}</b>.`);
|
clear(`O impostor era <b>${impostor}</b>; a palavra era <b>${secret.word}</b>; a dica <b>${secret.hint}</b>.`);
|
||||||
stage += 1;
|
stage += 1;
|
||||||
break;
|
break;
|
||||||
|
|
@ -126,3 +147,40 @@ async function getWordWithHint() {
|
||||||
console.error('Error loading words:', 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 (!!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 (!!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");
|
||||||
|
}
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
:root {
|
:root {
|
||||||
--background: black;
|
--background: black;
|
||||||
|
--backgroundLighter: rgb(20, 20, 20);
|
||||||
--foreground: white;
|
--foreground: white;
|
||||||
--saBORbackground: rgba(54, 54, 54, 0.5);
|
--saBORbackground: rgba(54, 54, 54, 0.5);
|
||||||
--saBORforeground: darkgrey;
|
--saBORforeground: darkgrey;
|
||||||
|
|
@ -7,10 +8,24 @@
|
||||||
--civilColor: rgb(0, 50, 0);
|
--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;
|
margin: 0;
|
||||||
padding: 0;
|
padding: 0;
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
|
scrollbar-color: var(--foreground) transparent;
|
||||||
|
scrollbar-width: thin;
|
||||||
|
user-select: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
body {
|
body {
|
||||||
|
|
@ -23,8 +38,11 @@ body {
|
||||||
padding: 2em;
|
padding: 2em;
|
||||||
gap: 1em;
|
gap: 1em;
|
||||||
height: 100vh;
|
height: 100vh;
|
||||||
|
max-height: 100vh;
|
||||||
width: 30vw;
|
width: 30vw;
|
||||||
margin: auto;
|
margin: auto;
|
||||||
|
overflow: hidden;
|
||||||
|
scroll-behavior: smooth;
|
||||||
}
|
}
|
||||||
|
|
||||||
header {
|
header {
|
||||||
|
|
@ -100,11 +118,13 @@ button {
|
||||||
margin-bottom: 1em;
|
margin-bottom: 1em;
|
||||||
display: flex;
|
display: flex;
|
||||||
gap: .6em;
|
gap: .6em;
|
||||||
|
overflow-y: auto;
|
||||||
|
overflow-x: hidden;
|
||||||
|
|
||||||
button {
|
button {
|
||||||
background-color: transparent;
|
background-color: transparent;
|
||||||
font-family: monospace;
|
font-family: monospace;
|
||||||
color: white;
|
color: var(--foreground);
|
||||||
text-shadow: none;
|
text-shadow: none;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -115,12 +135,11 @@ input:focus {
|
||||||
|
|
||||||
#newPlayerButton {
|
#newPlayerButton {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
margin-bottom: 1em;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#startGame {
|
#startGame {
|
||||||
margin-top: auto;
|
/* margin-top: auto;
|
||||||
margin-left: auto;
|
margin-left: auto; */
|
||||||
}
|
}
|
||||||
|
|
||||||
#gameLog {
|
#gameLog {
|
||||||
|
|
@ -128,6 +147,7 @@ input:focus {
|
||||||
margin: auto 0;
|
margin: auto 0;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
transition: .4s;
|
transition: .4s;
|
||||||
|
border-width: medium 0;
|
||||||
|
|
||||||
p {
|
p {
|
||||||
margin: 0;
|
margin: 0;
|
||||||
|
|
@ -142,6 +162,17 @@ input:focus {
|
||||||
background-color: var(--civilColor);
|
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 {
|
body.game #playlistHolder {
|
||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
|
|
@ -150,14 +181,20 @@ body.game #gameLog {
|
||||||
display: unset;
|
display: unset;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
body.game header {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
#bottomBar {
|
#bottomBar {
|
||||||
margin-top: auto;
|
|
||||||
display: flex;
|
display: flex;
|
||||||
gap: 1em;
|
gap: .6em;
|
||||||
|
flex-shrink: 0;
|
||||||
|
margin-top: 0;
|
||||||
|
justify-content: end;
|
||||||
}
|
}
|
||||||
|
|
||||||
#hintCheckHolder p {
|
#hintCheckHolder p {
|
||||||
margin: auto 0 auto 0;
|
margin: auto auto auto 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
#hintCheckHolder {
|
#hintCheckHolder {
|
||||||
|
|
@ -165,11 +202,34 @@ body.game #gameLog {
|
||||||
gap: .4em;
|
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 {
|
.playerName {
|
||||||
font-size: x-large;
|
font-size: x-large;
|
||||||
font-weight: bolder;
|
font-weight: bolder;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#timer {
|
||||||
|
font-size: xx-large;
|
||||||
|
}
|
||||||
|
|
||||||
.rainbowText {
|
.rainbowText {
|
||||||
background: linear-gradient(to right, var(--foreground), var(--background), var(--foreground), var(--impostorColor), var(--foreground), var(--civilColor)); -webkit-background-clip: text;
|
background: linear-gradient(to right, var(--foreground), var(--background), var(--foreground), var(--impostorColor), var(--foreground), var(--civilColor)); -webkit-background-clip: text;
|
||||||
background-clip: text;
|
background-clip: text;
|
||||||
|
|
@ -197,6 +257,6 @@ body.game #gameLog {
|
||||||
|
|
||||||
@media (max-width: 720px) {
|
@media (max-width: 720px) {
|
||||||
body {
|
body {
|
||||||
width: 96vw;
|
width: 100vw;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Loading…
Add table
Add a link
Reference in a new issue