first commit

This commit is contained in:
天クマ 2026-04-29 20:43:53 -03:00
commit 888c6bf2da
31 changed files with 2293 additions and 0 deletions

BIN
www/impostor.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 34 KiB

44
www/index.html Normal file
View file

@ -0,0 +1,44 @@
<!DOCTYPE html>
<html lang="pt-BR">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Impostor</title>
<link rel="stylesheet" href="style.css">
<script src="script.js" defer></script>
<script type="text/javascript" src="cordova.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">
<p>Dicas</p>
<input type="checkbox" id="hintCheck" checked>
</div>
</div>
<div name="gameLog" id="gameLog">
<!-- game messages will be here -->
</div>
<div id="bottomBar">
<button id="startGame">Próximo</button>
</div>
</body>
</html>

BIN
www/logo.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 29 KiB

128
www/script.js Normal file
View file

@ -0,0 +1,128 @@
const nPlayerButton = document.querySelector("#newPlayerButton");
const playersList = document.querySelector("#playersList");
const startButton = document.querySelector("#startGame");
const logArea = document.querySelector("#gameLog");
const body = document.querySelector("body");
const hintCheckbox = document.querySelector("#hintCheck");
let stage = 0;
let currentPlayer = 0;
let impostor;
let allPlayers;
let currentName;
let secret;
let enableHint;
nPlayerButton.addEventListener('click', () => {
const holder = document.createElement("div");
const newPlayer = document.createElement("input");
const killButton = document.createElement("button");
holder.classList.add("playerHolder");
killButton.textContent = "X";
killButton.addEventListener('click', () => {
holder.remove();
});
holder.appendChild(newPlayer);
holder.appendChild(killButton);
playersList.appendChild(holder);
});
startButton.addEventListener('click', () => {
switch (stage) {
case 0:
allPlayers = Array.from(document.querySelectorAll("#playersList input"));
if (allPlayers.some(player => !player.value)) {
alert("Por favor, preencha o nome de todos os jogadores.");
return;
}
body.classList.add("game");
enableHint = hintCheckbox.checked;
clear("Prepare-se para iniciar o jogo.");
impostor = choose();
secret = getWordWithHint().then(result => {
secret = result;
});
stage = -1;
break;
case 1:
if (currentName == impostor) {
clear(currentName + " é impostor" + (enableHint ? (", sua dica é " + secret.hint + ".") : "."));
logClass('impostor');
} else {
clear(currentName + " é civil, a palavra é " + secret.word);
logClass('civil');
}
currentPlayer += 1;
if (currentPlayer == allPlayers.length
) {
stage += 1;
} else {
stage = -1;
}
break;
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;
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;
}
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);
}
}

202
www/style.css Normal file
View file

@ -0,0 +1,202 @@
:root {
--background: black;
--foreground: white;
--saBORbackground: rgba(54, 54, 54, 0.5);
--saBORforeground: darkgrey;
--impostorColor: rgb(60, 0, 0);
--civilColor: rgb(0, 50, 0);
}
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
background-color: var(--background);
color: var(--foreground);
line-height: 1.6;
display: flex;
flex-direction: column;
padding: 2em;
gap: 1em;
height: 100vh;
width: 30vw;
margin: auto;
}
header {
text-align: center;
}
header img {
height: 8em;
}
h1 {
font-size: 2.5rem;
font-family: monospace;
}
main {
text-align: center;
margin-top: 40px;
}
p {
font-size: 1.2rem;
margin-bottom: 20px;
}
#playersList input {
width: 100%;
}
button, input, textarea {
padding: .6em;
border: none;
border-radius: 5px;
}
input, #gameLog {
background-color: var(--saBORbackground);
color: var(--foreground);
}
#gameLog {
font-size: 1.4em;
border: medium solid var(--foreground);
padding: 1em;
border-radius: 5px;
}
input {
transition: .2s;
border: medium solid var(--saBORforeground);
}
input:focus {
border-color: var(--foreground);
background-color: var(--foreground);
color: var(--background);
outline: none;
}
button {
transition: .2s;
background-color: var(--foreground);
color: var(--background);
/* text-shadow: 0px 0px 2px rgba(255, 255, 255, 1), 0px 0px 2px rgba(255, 255, 255, 1); */
}
/* button:hover {
background-color: var(--foreground);
color: var(--background);
} */
.playerHolder {
margin-bottom: 1em;
display: flex;
gap: .6em;
button {
background-color: transparent;
font-family: monospace;
color: white;
text-shadow: none;
}
}
input:focus {
border-color: var(--foreground);
}
#newPlayerButton {
width: 100%;
margin-bottom: 1em;
}
#startGame {
margin-top: auto;
margin-left: auto;
}
#gameLog {
display: none;
margin: auto 0;
text-align: center;
transition: .4s;
p {
margin: 0;
}
}
#gameLog.impostor {
background-color: var(--impostorColor);
}
#gameLog.civil {
background-color: var(--civilColor);
}
body.game #playlistHolder {
display: none;
}
body.game #gameLog {
display: unset;
}
#bottomBar {
margin-top: auto;
display: flex;
gap: 1em;
}
#hintCheckHolder p {
margin: auto 0 auto 0;
}
#hintCheckHolder {
display: flex;
gap: .4em;
}
.playerName {
font-size: x-large;
font-weight: bolder;
}
.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;
color: transparent;
animation: rainbow_animation 10s ease-in-out infinite;
background-size: 400% 100%;
text-shadow: 0px 0px 10px rgba(255, 255, 255, 0.2);
}
@keyframes rainbow_animation {
0%,100% {
background-position: 0 0;
}
50% {
background-position: 100% 0;
}
}
@media (max-width: 1280px) {
body {
width: 60vw;
}
}
@media (max-width: 720px) {
body {
width: 96vw;
}
}

1022
www/words.json Normal file

File diff suppressed because it is too large Load diff