-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathscript.js
More file actions
240 lines (200 loc) · 9.77 KB
/
script.js
File metadata and controls
240 lines (200 loc) · 9.77 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
// Lista de palavras para usar no jogo
const wordsList = "vida casa tempo mundo amor dia trabalho hoje feliz novo grande melhor gente pais lugar parte vez bom ver mulher homem caso hora pai mae deus pessoa agua noite cidade".split(" ");
// Pega os elementos pelo ID para manipulação
const wordsDisplay = document.getElementById('words-display');
const typingInput = document.getElementById('typing-input');
const timerElement = document.getElementById('timer');
const scoreElement = document.getElementById('score');
const endScreen = document.getElementById('end-screen');
const finalScoreElement = document.getElementById('final-score');
const btnRestart = document.getElementById('btn-restart');
// Configurações iniciais do jogo
let gameTime = 30; // Duração da partida
let timeLeft = gameTime; // Armazena tempo restante (decrementa)
let timer = null; // Armazena ID do cronômetro fornecido pelo navegador
let gameActive = false; // Flag: Indica se a partida está ocorrendo no momento
let currentScore = 0; // Pontuação do jogador
let words = []; // Guarda as palavras geradas e seus elementos HTML
// Função que cria as palavras visualmente na div
function gerarPalavras(quantidade) {
// Cria uma caixa temporária na memória (fragmento), fora da tela.
// O script monta as palavras dentro da caixa (uma única vez) e
// envia para a tela para melhorar a performance
const fragmento = document.createDocumentFragment();
for (let i = 0; i < quantidade; i++) {
// Sorteia uma palavra aleatória da lista
const randIndex = Math.floor(Math.random() * wordsList.length);
const wordStr = wordsList[randIndex];
// Cria a tag <span> para a palavra inteira
const wordSpan = document.createElement('span');
wordSpan.className = 'word';
// Loop para criar um <span> para cada letra (para pintar de verde/vermelho depois)
wordStr.split('').forEach(char => {
const charSpan = document.createElement('span');
charSpan.innerText = char;
charSpan.className = 'letter';
wordSpan.appendChild(charSpan);
});
fragmento.appendChild(wordSpan);
// Armazena a referência do elemento e o texto original no array de controle
words.push({ element: wordSpan, text: wordStr });
}
// Envia todas as palavras na tela de uma única vez
wordsDisplay.appendChild(fragmento);
}
// Função para zerar jogo e preparar nova partida
function initGame() {
timeLeft = gameTime;
currentScore = 0;
gameActive = false;
words = [];
// Atualiza a interface visual com os valores resetados
timerElement.innerText = timeLeft;
scoreElement.innerText = currentScore;
typingInput.value = '';
wordsDisplay.scrollTop = 0; // Volta a rolagem para o topo
// Esconde a tela de Game Over
endScreen.classList.add('d-none');
wordsDisplay.innerHTML = ''; // Limpa as palavras antigas
gerarPalavras(80); // Gera novas palavras
// Marca a primeira palavra como ativa para indicar onde o usuário deve começar
if(words.length > 0) words[0].element.classList.add('active');
// Foca automaticamente no input para começar a digitar
typingInput.focus();
}
// Inicia a contagem regressiva
function startGame() {
// Garante que o timer inicia apenas se o jogo não estiver ativo
if (!gameActive) {
gameActive = true;
// Configura o intervalo que diminui o tempo a cada 1000ms (1 segundo)
timer = setInterval(() => {
timeLeft--;
timerElement.innerText = timeLeft;
// Verifica se o tempo acabou para encerrar a partida
if (timeLeft <= 0) {
endGame();
}
}, 1000);
}
}
// Função de Game Over
function endGame() {
// Para a execução do cronômetro
clearInterval(timer);
gameActive = false;
finalScoreElement.innerText = currentScore;
// Exibe a tela de fim de jogo removendo a classe que a esconde
endScreen.classList.remove('d-none');
// Manda a pontuação pro PHP salvar no banco via AJAX (fetch)
fetch('db/save_score.php', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ score: currentScore })
})
.then(response => response.json()) // Espera resposta JSON
.then(data => console.log("Salvo:", data))
.catch(error => console.error("Erro:", error));
}
// Inicia o jogo automaticamente assim que o usuário digita algo
typingInput.addEventListener('input', () => {
// Se digitou a primeira letra e o jogo estava inativo, inicia agora
if (!gameActive && timeLeft > 0) startGame();
// Localiza a palavra atual procurando pela primeira que ainda não foi completada
const typedValue = typingInput.value;
// Acha a palavra atual (a primeira que não tem a classe 'completed')
const currentWordObj = words.find(w => !w.element.classList.contains('completed'));
// Caso o script não achar nenhuma palavra (lista acabou),
// a função para a execução (trava de segurança)
if (!currentWordObj) return;
// Preparando as variáveis para comparação:
// Divide o objeto da palavra que encontrou para usar as partes separadas:
// Muda a cor da palavra inteira.
const currentWordSpan = currentWordObj.element; // O HTML da palavra (<div> ou <span>)
// Compara se o usuário digitou certo
const targetWord = currentWordObj.text; // O texto puro (conteúdo)
// Pinta cada caractere de verde ou vermelho
const letterSpans = currentWordSpan.querySelectorAll('.letter'); // Lista das letras individuais
// Verifica se apertou ESPAÇO (indica que terminou a palavra)
if (typedValue.endsWith(' ')) {
// Impede que espaços vazios contem como tentativa
// Usuário não consegue pular várias palavras segurando espaço
if (typedValue.trim() === '') {
typingInput.value = ''; // Limpa o input
return; // Trava o avanço
}
// Só aceita pular se digitou pelo menos o tamanho da palavra (quantidade de letras) + o espaço
if (typedValue.length > targetWord.length) {
// Tira o espaço extra pra comparar
const wordTrimmed = typedValue.trim().toLowerCase();
// Compara a palavra digitada com a palavra correta
if (wordTrimmed === targetWord) {
// Incrementa a pontuação pois a palavra está correta
currentWordSpan.classList.add('text-success');
currentScore++; // Ganha ponto
scoreElement.innerText = currentScore;
} else {
// Marca visualmente como erro se a palavra não bater (palavra riscada)
currentWordSpan.classList.add('text-danger');
currentWordSpan.style.textDecoration = "line-through";
}
// Marca palavra como feita e remove o cursor dela
currentWordSpan.classList.add('completed');
currentWordSpan.classList.remove('active');
// Se estiver acabando as palavras (menos de 50), gera mais (infinitamente)
const currentIndex = words.indexOf(currentWordObj);
if (words.length - currentIndex < 50) {
gerarPalavras(50);
}
// Passa o cursor (foco) para a próxima palavra
const nextWord = currentWordSpan.nextElementSibling;
if(nextWord) {
nextWord.classList.add('active');
// Realiza o scroll automático da caixa de texto se o cursor estiver muito abaixo
if (nextWord.offsetTop > wordsDisplay.scrollTop + 100) {
wordsDisplay.scrollTop = nextWord.offsetTop - 10;
}
}
typingInput.value = ''; // Limpa o input pra próxima palavra
// Remove as classes de cor das letras para limpar visualmente
letterSpans.forEach(span => {
span.classList.remove('correct', 'incorrect');
});
return; // Sai da função para não pintar as letras de novamente
}
}
// Transforma o texto digitado em letras minúsculas para comparação
const typedChars = typedValue.toLowerCase().split('');
// Percorre cada letra (span) da palavra que está na tela
letterSpans.forEach((span, index) => {
// Armazena a letra que o usuário digitou na posição específica
const charTyped = typedChars[index];
// Armazena a letra correta que deveria ser digitada (no HTML)
const charTarget = span.innerText.toLowerCase();
// Verifica se o usuário ainda não chegou na letra (ou se ele apagou)
if (charTyped == null) {
// Deixa a letra neutra (sem cor) visualmente
span.classList.remove('correct', 'incorrect');
// Compara se a letra digitada é igual à letra esperada
} else if (charTyped === charTarget) {
// Marca a letra como correta (verde)
span.classList.add('correct');
span.classList.remove('incorrect');
// Se a letra existe mas é diferente da esperada
} else {
// Marca a letra como incorreta (vermelho)
span.classList.remove('correct');
span.classList.add('incorrect');
}
});
});
// Garante que o foco fique no input se clicar fora (exceto no game over)
document.addEventListener('click', (e) => {
if(endScreen.classList.contains('d-none')) {
typingInput.focus();
}
});
// Botão de reiniciar chama o initGame
btnRestart.addEventListener('click', initGame);
// Começa o jogo assim que carrega o script
initGame();