// ATENÇÃO, ANTES DE SUBIR O CODIGO PARA O ARDUINO INSTALE AS BIBLIOTECAS ABAIXO: WIRE, KEYPAD E LIQUID CRYSTAL
#include <Wire.h> //BAIXAR WIER DATA
#include <Keypad.h> // BAIXAR DO AUTOR MARK STANLEY, ALEXANDER BREVIG
#include <LiquidCrystal_I2C.h> // USAR A DO FRANK DE BRABANDER
// LIGAÇÃO DO LCD: Aqui configuramos quais pinos físicos do Arduino estão conectados à tela LCD.
// Se um dia você trocar os fios de lugar, é aqui que os números devem ser atualizados.
//
/*LIGAÇÃO LCD COM MODULOS I2C:
- VCC do I2C -> 5V do Arduino
- GND do I2C -> GND do Arduino
- SDA do I2C -> Pino A4 do Arduino
- SCL do I2C -> Pino A5 do Arduino
- Mudanças necessárias no código:
A) Instale a biblioteca "LiquidCrystal_I2C" na sua IDE.
*/
LiquidCrystal_I2C lcd(0x27, 16, 2);
const byte ROWS = 4; //quatro linhas
const byte COLS = 4; //quatro colunas
// TECLADO : Este é o "mapa" do seu teclado matricial.
//Se você apertar um botão e aparecer a letra errada na tela,
// significa que a ordem dos fios está diferente e você pode
//consertar apenas trocando as letras/números de lugar aqui nesta matriz.
char keys[ROWS][COLS] = {
{'1','2','3','a'}
,
{'4','5','6','b'}
,
{'7','8','9','c'}
,
{'*','0','#','d'}
};
// INSTRUÇÃO LIGAÇÃO DO TECLADO MATRICIAL (4x4)
// O teclado tem 8 pinos de saída em fileira. Olhando o teclado de frente (da esquerda para a direita):
// - Fio 1 (Linha 1) -> Ligar no Pino 12 do Arduino
// - Fio 2 (Linha 2) -> Ligar no Pino 13 do Arduino
// - Fio 3 (Linha 3) -> Ligar no Pino 5 (Digital) do Arduino
// - Fio 4 (Linha 4) -> Ligar no Pino 6 (Digital) do Arduino
// - Fio 5 (Coluna 1) -> Ligar no Pino A3 do Arduino
// - Fio 6 (Coluna 2) -> Ligar no Pino A2 do Arduino
// - Fio 7 (Coluna 3) -> Ligar no Pino A1 do Arduino
// - Fio 8 (Coluna 4) -> Ligar no Pino A0 do Arduino
//NO FINAL DO CODIGO TEM ORIENTAÇÃO DE COMO MUDAR MODELO DO TECLADO
byte rowPins[ROWS] = {
12, 13, 5, 6}; //conecta aos pinos das linhas do teclado matricial
byte colPins[COLS] = {
A3, A2, A1, A0
}; //conecta aos pinos das colunas do teclado matricial
Keypad keypad = Keypad( makeKeymap(keys), rowPins, colPins, ROWS, COLS );
char codeInput[8];
byte time[4];
boolean refresh=true;//1 atualiza uma vez...
char password[8];
int key=-1;
char lastKey;
char var;
boolean passwordEnable=false;
//Botões para o shield lcd
char BT_RIGHT = '4';
char BT_UP = 'a';
char BT_DOWN = 'b';
char BT_LEFT = '6';
char BT_SEL = 'd'; // Tecla Ok
char BT_CANCEL = 'c';
char BT_DEFUSER = 'x'; // não implementado
// LIGAÇÃO DOS LEDs
// - LED VERMELHO: Ligar a perna maior (Positivo/Anodo) no Pino 11 do Arduino.
// - LED VERDE: Ligar a perna maior (Positivo/Anodo) no Pino 10 do Arduino.
// - As pernas menores (Negativo/Catodo) de ambos os LEDs devem ir para o GND do Arduino, MAS SEMPRE com um resistor
//(ex: 220 ohms ou 330 ohms) no meio do caminho para não queimar o LED.
//O IDEAL É UM RESISTOR 220 PARA CADA LED
const int BLUELED = 11; //ANTIGO REDLED AGORA VIROU BLUELED
const int YELLOWLED = 10; // ANTIGO GREENLED VIROU YELLOWLED
//PINO DO RELÉ
boolean relayEnable = false;
// LIGAÇÃO DO MÓDULO DE RELÉ E SIRENE (ATENÇÃO AQUI)
// O Relé é a ponte entre a placa (fraca) e a sirene (forte).
// 1. LIGAÇÃO DO MÓDULO DE RELÉ NO ARDUINO:
// - Pino IN (ou S / Sinal) do Relé -> Ligar no Pino 9 do Arduino.
// - Pino VCC (ou DC+) do Relé -> Ligar no 5V do Arduino.
// - Pino GND (ou DC-) do Relé -> Ligar no GND do Arduino.
// 2. LIGAÇÃO DA SIRENE NA BATERIA VIA RELÉ:
// - O fio NEGATIVO da sirene vai DIRETO no NEGATIVO da bateria.
// - O fio POSITIVO da bateria entra no borne do meio do relé chamado "COM" (Comum).
// - O fio POSITIVO da sirene sai do borne do relé chamado "NO" (Normalmente Aberto).
// * NUNCA ligue a sirene diretamente nos pinos do Arduino, ou a placa vai queimar na hora!
const int RELAYPIN = 9; // DOMINAÇÃO É O VERDE)
const int RELAY_TIME = 5000;
// --- NOVOS PINOS E VARIÁVEIS ADICIONADOS ---
const int RELAY2PIN = 3; // Relé 2 (Para dominação (VERMELHO) / Explosão Dupla)
const int PINO_PENDRIVE = 4; // Leitor do Pendrive
boolean pendriveEnable = false; // Define se o pendrive será exigido
// -------------------------------------------
//VARIÁVEIS DE TEMPO
int GAMEHOURS = 0;
int GAMEMINUTES = 45;
int BOMBMINUTES = 4;
int ACTIVATESECONDS = 5;
boolean endGame = false;
boolean sdStatus = false; //ativa o jogo buscar e destruir usado na config
boolean saStatus = false; //o mesmo mas para Sabotagem
boolean doStatus = false; //para Demolição (Dominação)
boolean start = true;
boolean defusing;
boolean cancelando;
// TONS DE SOM
boolean soundEnable = true;
// ORIENTAÇÃO DE LIGAÇÃO DO DO BUZZER (BIPE)
// - Fio Vermelho (ou perna maior / Positivo) -> Ligar no Pino 8 do Arduino.
// - Fio Preto (ou perna menor / Negativo) -> Ligar no GND do Arduino.
int tonepin = 8; // Pino para o som
int alarmTone1 = 700;
int alarmTone2 = 2600;
int activeTone = 1330;
int errorTone = 100;
//USIGNED: Variáveis do tipo "unsigned long" são usadas para guardar números GIGANTES.
// O Arduino usa a função millis() para contar o tempo (como um cronômetro que inicia quando ele liga).
// Como esse número cresce muito rápido, precisamos desse tipo de variável para não travar o sistema.
unsigned long iTime;
unsigned long timeCalcVar;
unsigned long blueTime;
unsigned long yellowTime;
unsigned long iZoneTime;//tempo inicial para a zona
byte team=0; // 0 = neutro, 1 = time verde, 2 = time vermelho
// SETUP: A função setup() roda UMA ÚNICA VEZ quando você liga o Arduino.
// É aqui que ele "acorda", configura a tela, os LEDs e emite o som inicial.
void setup(){
// 1. Defina o estado desejado (HIGH = Desligado) ANTES de declarar como saída
// SOBRE OS RELÉS, O CERTO É ESTAR "HIGH" NO PADRAO INICIAL, SE ESTIVER LOW ELE FICARA SEMPRE ARMADO. O SITE WOKWI USA UM SISTEMA INVERTIDO
digitalWrite(RELAYPIN, HIGH);
pinMode(RELAYPIN, OUTPUT);
// --- CONFIGURAÇÃO DO RELÉ 2 E PENDRIVE ---
digitalWrite(RELAY2PIN, HIGH);
pinMode(RELAY2PIN, OUTPUT);
pinMode(PINO_PENDRIVE, INPUT_PULLUP); // Mantém energia alta até o pendrive fechar o curto no GND
lcd.init(); lcd.backlight();
Serial.begin(9600);
tone(tonepin,2400,30); // Bipe de inicialização
// PRIMEIRA LINHA DA TELA (Máximo 16 letras)
lcd.setCursor(0,0); // Mudei para 0,0 para começar no canto esquerdo
lcd.print("COLISEU AIRSOFT");
// SEGUNDA LINHA DA TELA (Máximo 16 letras)
lcd.setCursor(0,1);
lcd.print("@coliseu_airsoft");
// TEMPO QUE A MENSAGEM FICA NA TELA ANTES DE IR PRO MENU
delay(4000); // 1000 milissegundos = 1 segundo
// Continua o setup normal...
keypad.setHoldTime(50);
keypad.setDebounceTime(50);
keypad.addEventListener(keypadEvent);
//Modos dos Pinos (PinModes)
pinMode(YELLOWLED, OUTPUT);
pinMode(BLUELED, OUTPUT);
// ##ORIENTAÇÃO GEMINI## Este bloco gigante de "B10000" serve para desenhar customizadamente
// os blocos da barra de progresso (aquelas barrinhas que enchem quando você está desarmando).
// O LCD não tem esses desenhos de fábrica, então nós "ensinamos" ele a desenhar pixel por pixel.
byte bar1[8] = { B10000, B10000, B10000, B10000, B10000, B10000, B10000, B10000 };
byte bar2[8] = { B11000, B11000, B11000, B11000, B11000, B11000, B11000, B11000 };
byte bar3[8] = { B11100, B11100, B11100, B11100, B11100, B11100, B11100, B11100 };
byte bar4[8] = { B11110, B11110, B11110, B11110, B11110, B11110, B11110, B11110 };
byte bar5[8] = { B11111, B11111, B11111, B11111, B11111, B11111, B11111, B11111 };
byte up[8] = { B00000, B00100, B01110, B11111, B11111, B00000, B00000 };
byte down[8] = { B00000, B00000, B11111, B11111, B01110, B00100, B00000 };
lcd.createChar(0,bar1);
lcd.createChar(1,bar2);
lcd.createChar(2,bar3);
lcd.createChar(3,bar4);
lcd.createChar(4,bar5);
lcd.createChar(5,up);
lcd.createChar(6,down);
}
// LOOP: O loop() roda infinitamente. Aqui ele só manda o sistema ficar abrindo o Menu Principal.
void loop(){
menuPrincipal();
}
// KEYPAD EVENT: Esta função keypadEvent monitora como você aperta os botões.
// Ela sabe diferenciar um "toque rápido" de um botão "sendo segurado" (HOLD).
// É essencial para a mecânica de segurar o botão 'D' para desarmar a bomba.
void keypadEvent(KeypadEvent key){
switch (keypad.getState()){
case RELEASED:
switch (key){
case 'd': defusing=false;
break;
case 'c': cancelando=false;
break;
}
break;
case HOLD:
switch (key){
case 'd': defusing= true;
break;
case 'c': cancelando=true;
break;
}
break;
}
}
// TEXTOS PRINCIPAIS DAS TELAS
char* menu1[]={"Armar e Desarmar"," Sabotagem "," Dominacao ", " Configuracao "};
char* menu2[]={" Teste Rele ", " Auto Teste "};
char* GAME_TIME="Tempo de Jogo:";
char* BOMB_TIME="Tempo Bomba:";
char* ZERO_MINUTES="00 minutos";
char* ARM_TIME="Tempo P/ Armar:";
char* ZERO_SECS="00 segundos";
char* ENABLE_SOUND="Ativar SOM?";
char* YES_OR_NOT="A : Sim B : Nao";
char* ENABLE_RELAYPIN="Ativar RELE?";
char* ENABLE_CODE="Ativar SENHA?";
char* GAME_TIME_TOP="TEMPO DE JOGO";
char* ARMING_BOMB = "ARMANDO BOMBA!";
char* ENTER_CODE = "SENHA:";
char* CODE_ERROR = "SENHA ERRADA!";
char* BOMB_ARMED = "BOMBA ARMADA!";
char* DETONATION_IN = "EXPLOSAO EM:";
char* DISARMING = " DESARMANDO " ;
char* DISARM = "DESARMANDO";
char* GAME_OVER = " FIM DE JOGO! ";
char* DEFENDERS_WIN = " DEFESA GANHOU! ";
char* SABOTAGE_FAIL= "SABOTAGEM FALHOU";
//MENU PRINCIPAL
void menuPrincipal(){
digitalWrite(YELLOWLED, LOW);
digitalWrite(BLUELED, LOW);
// se começarmos um novo jogo a partir de outro, precisamos reiniciar corretamente estas variáveis
saStatus=false;
sdStatus=false;
doStatus=false;
//Desenha o menu
cls();//limpa o lcd e define o cursor para 0,0
int i=0;
// AQUI VOCÊ PODE ADICIONAR MAIS ITENS NO MENU PRINCIPAL
lcd.print(menu1[i]);
lcd.setCursor(15,1);
checkArrows(i,3); // CORRIGIDO PARA 3
while(1){
var = keypad.waitForKey();
if(var == BT_UP && i>0){
tone(tonepin,2400,30);
i--;
cls();
lcd.print(menu1[i]);
checkArrows(i,3); // CORRIGIDO PARA 3
delay(50);
}
if(var == BT_DOWN && i<3){ // CORRIGIDO PARA 3 AQUI
tone(tonepin,2400,30);
i++;
cls();
lcd.print(menu1[i]);
checkArrows(i,3); // CORRIGIDO PARA 3
delay(50);
}
// ##ORIENTAÇÃO GEMINI## "switch (i)" é como um trem trocando de trilho.
// Se "i" (o número da linha selecionada) for 0, ele vai pro caso 0. Se for 1, pro caso 1...
if(var == BT_SEL){
tone(tonepin,2400,30);
cls();
switch (i){
case 0:
sdStatus=true;
configQuickGame();
startGameCount();
search();
break;
case 1:
saStatus=true;
configQuickGame();
startGameCount();
sabotage();
break;
case 2:
doStatus=true;
configQuickGame();
startGameCount();
domination();
break;
case 3:
config();
break;
}
}
}
}
//ESTRUTURA DO MENU > CONFIGURAÇÕES
void config(){
//Desenha o menu
lcd.clear();
lcd.setCursor(0, 0);
int i=0;
delay(500);
lcd.print(menu2[i]);
checkArrows(i,1); // Máximo ajustado para 1 (2 itens)
while(1){
var=keypad.waitForKey();
if(var == BT_UP && i>0){
tone(tonepin,2400,30);
i--;
lcd.clear();
lcd.print(menu2[i]);
checkArrows(i,1); // Máximo ajustado para 1
delay(50);
}
if(var == BT_DOWN && i<1){ // Limite de descida ajustado para 1
tone(tonepin,2400,30);
i++;
lcd.clear();
lcd.print(menu2[i]);
checkArrows(i,1); // Máximo ajustado para 1
delay(50);
}
if(var == BT_CANCEL){
tone(tonepin,2400,30);
menuPrincipal();
break; // Adicionado break para garantir a saída do loop
}
if(var == BT_SEL){
tone(tonepin,2400,30);
lcd.clear();
switch (i){
case 0: // RELAY TEST (Antigo item 2, agora é o 0)
cls();
lcd.print("RELES ATIVADOS!");
digitalWrite(RELAYPIN, LOW); // Ativa o Relé
delay(2000); // Aguarda 2 segundos
digitalWrite(RELAY2PIN, LOW);
delay(2000);
cls();
lcd.print("RELES DESATIVADOS!");
digitalWrite(RELAYPIN, HIGH); // Desliga o Relé
digitalWrite(RELAY2PIN, HIGH);
delay(2000);
config(); // Volta para o menu config
break;
case 1: // AUTO TEST (Antigo item 3, agora é o 1)
autoTest(); // Chama a sua função de Auto Teste
config(); // Volta para o menu config
break;
}
}
}
}
// CONFIG QUICK GAME: Esta função faz toda a coleta de dados antes de a partida iniciar:
// Tempo de jogo, tempo de bomba, som ligado/desligado, uso do relé, uso de senha.
void configQuickGame(){
cls();
//TEMPO DE JOGO
if(sdStatus || doStatus || saStatus){
menu1:
cls();
lcd.print(GAME_TIME);
delay(100);
lcd.setCursor(0,1);
lcd.print("00:00 hh:mm");
lcd.cursor();
lcd.blink();
lcd.setCursor(0,1);
byte var2=0;
for(int i=0;i<4;i++){
while(1){
if(i==2 && var2==0){
lcd.print(":");
var2=1;
}
byte varu= getRealNumber();
if(varu !=11){
time[i] = varu;
Serial.print(varu);
lcd.print(varu);
tone(tonepin,2400,30);
break;
}
}
}
lcd.noCursor();
lcd.noBlink();
lcd.setCursor(13,1);
lcd.print("OK?");
//área onde passamos os itens para
//redesenhar
while(1){
var = keypad.waitForKey();
if(var == 'd') // Aceitar
{
tone(tonepin,2400,30);
// ##ORIENTAÇÃO GEMINI## A matemática aqui transforma os 4 dígitos digitados no formato de minutos reais para o código processar
GAMEMINUTES= ((time[0]*600)+(time[1]*60)+(time[2]*10)+(time[3]));
break;
}
if(var == 'c') // Cancelar ou Botão de Voltar :')
{
tone(tonepin,2400,30);
goto menu1;
}
}
tone(tonepin,2400,30);
cls();
}
//TEMPO DA BOMBA
if(sdStatus || saStatus){
menu2:
cls();
lcd.print(BOMB_TIME);
delay(100);
lcd.setCursor(0,1);
lcd.print(ZERO_MINUTES);
lcd.cursor();
lcd.blink();
lcd.setCursor(0,1);
for(int i=0;i<2;i++){
while(1){
byte varu= getRealNumber();
if(varu !=11){
time[i] = varu;
lcd.print(varu);
tone(tonepin,2400,30);
break;
}
}
}
lcd.noCursor();
lcd.noBlink();
lcd.setCursor(13,1);
lcd.print("OK?");
//área onde passamos os itens para
//redesenhar
while(1){
var = keypad.waitForKey();
if(var == 'd') //
{
tone(tonepin,2400,30);
BOMBMINUTES= ((time[0]*10)+(time[1]));
break;
}
if(var == 'c') // Cancelar ou Botão de Voltar :')
{
tone(tonepin,2400,30);
goto menu2;
}
}
tone(tonepin,2400,30);
cls();
}
cls();
//TEMPO DE ARMAR
if(sdStatus || doStatus || saStatus){
menu3:
cls();
lcd.print(ARM_TIME);
delay(100);
lcd.setCursor(0,1);
lcd.print(ZERO_SECS);
lcd.cursor();
lcd.blink();
lcd.setCursor(0,1);
for(int i=0;i<2;i++){
while(1){
byte varu= getRealNumber();
if(varu !=11){
time[i] = varu;
lcd.print(varu);
tone(tonepin,2400,30);
break;
}
}
}
lcd.noCursor();
lcd.noBlink();
lcd.setCursor(13,1);
lcd.print("OK?");
//área onde passamos os itens para
//redesenhar
while(1){
var = keypad.waitForKey();
if(var == 'd') // Aceitar
{
tone(tonepin,2400,30);
ACTIVATESECONDS= ((time[0]*10)+(time[1]));
break;
}
if(var == 'c') // Cancelar ou Botão de Voltar :')
{
tone(tonepin,2400,30);
goto menu3;
}
}
tone(tonepin,2400,30);
cls();
}
//som??
if(sdStatus || saStatus || doStatus){
cls();
lcd.print(ENABLE_SOUND);
lcd.setCursor(0,1);
lcd.print(YES_OR_NOT);
while(1)
{
var = keypad.waitForKey();
if(var == 'a' ){
soundEnable=true;
tone(tonepin,2400,30);
break;
}
if(var == 'b' ){
soundEnable=false;
tone(tonepin,2400,30);
break;
}
}
}
//Ativar RELÉ quando o jogo dos Terroristas acabar??? Cabum!
if(sdStatus || saStatus || doStatus){
cls();
lcd.print(ENABLE_RELAYPIN);
lcd.setCursor(0,1);
lcd.print(YES_OR_NOT);
while(1)
{
var = keypad.waitForKey();
if(var == 'a' ){
relayEnable=true;
tone(tonepin,2400,30);
break;
}
if(var == 'b' ){
relayEnable=false;
tone(tonepin,2400,30);
break;
}
}
}
//Você quuer um jogo com senha ativada/desativada?
if(sdStatus || saStatus){
cls();
lcd.print(ENABLE_CODE);
lcd.setCursor(0,1);
lcd.print(YES_OR_NOT);
while(1)
{
var = keypad.waitForKey();
if(var == 'a' ){
tone(tonepin,2400,30);
setNewPass();
passwordEnable = true;
break;
}
if(var == 'b' ){
tone(tonepin,2400,30);
passwordEnable = false;
break;
}
}
tone(tonepin,2400,30);
}
// =======================================================
// INÍCIO DO CÓDIGO DO PASSO 3B (PERGUNTA DO PENDRIVE)
// =======================================================
if(sdStatus || saStatus){
cls();
lcd.print("ATIVAR PENDRIVE?");
lcd.setCursor(0,1);
lcd.print(YES_OR_NOT);
while(1)
{
var = keypad.waitForKey();
if(var == 'a' ){
tone(tonepin,2400,30);
pendriveEnable = true;
break;
}
if(var == 'b' ){
tone(tonepin,2400,30);
pendriveEnable = false;
break;
}
}
tone(tonepin,2400,30);
}
//Continuar o jogo :D
}
//CHECK DE TAMANHO DA SENHA: Esta função compara as variáveis codeInput[8] e password[8]
boolean comparePassword(){
for(int i=0;i<8;i++){
if(codeInput[i]!=password[i])return false;
}
return true;
}
//SET CODE: Define a variável de senha
void setCode(){
lcd.setCursor(0, 1);
for(int i=0;i<8;i++){
while(1){
var= getNumber();
if(var !='x'){
codeInput[i] = var;
if (i != 0){
lcd.setCursor(i-1,1);
lcd.print("*");
lcd.print(var);
}
else
{
lcd.print(var);
}
tone(tonepin,2400,30);
break;
}
}
}
}
//SET CODE: Define a variável de senha
void setCodeTime(){
timeCalcVar=millis();
for(int i=0;i<8;i++){
while(1){
if(ACTIVATESECONDS*1000+timeCalcVar-millis()<=100){
codeInput[i]='x';
break;
}
lcd.setCursor(11,0);
printTimeDom(ACTIVATESECONDS*1000+timeCalcVar-millis(),false);
var= getNumber();
if(var !='x'){
codeInput[i] = var;
if (i != 0){
lcd.setCursor(i-1,1);
lcd.print("*");
lcd.print(var);
}
else
{
lcd.print(var);
}
tone(tonepin,2400,30);
break;
}
}
}
}
//SET PASS: Define a variável de senha
void setPass(){
lcd.setCursor(0, 1);
for(int i=0;i<8;i++){
while(1){
var= getNumber();
if(var !='x'){
password[i] = var;
if (i != 0){
lcd.setCursor(i-1,1);
lcd.print("*");
lcd.print(var);
}
else
{
lcd.print(var);
}
tone(tonepin,2400,30);
break;
}
}
}
}
//MECANISMO CONFIGURAÇÃO TEXTOS SENHA
void setNewPass(){
while(1){
lcd.clear();
lcd.setCursor(0, 0);
lcd.print("DEFINA A SENHA:");
setPass();
lcd.clear();
lcd.setCursor(0, 0);
lcd.print("REPITA A SENHA:");
setCode();
if(comparePassword()){
lcd.clear();
lcd.setCursor(0, 0);
lcd.print("SENHA DEFINIDA!");
delay(2000);
break;
}
else {
lcd.clear();
lcd.setCursor(0, 0);
lcd.print("SENHAS DIVERGEM!");
if(soundEnable)tone(tonepin,errorTone,200);
delay(2000);
}
}
}
//Espera até um botão ser pressionado, se for um número retorna o número 'char', senão retorna x
char getNumber(){
while(1){
var = keypad.getKey();
if (var){//
switch (var) {
case 'a': return 'x'; break;
case 'b': return 'x'; break;
case 'c': return 'x'; break;
case 'd': return 'x'; break;
case '*': return 'x'; break;
case '#': return 'x'; break;
default: return var; break;
}
}
return 'x';
}
}
//CONFIGURAÇÃO DO TECLADO PARA NUMEROS
byte getRealNumber(){
while(1){
var = keypad.waitForKey();
if (var){//
switch (var) {
case '1': return 1; break;
case '2': return 2; break;
case '3': return 3; break;
case '4': return 4; break;
case '5': return 5; break;
case '6': return 6; break;
case '7': return 7; break;
case '8': return 8; break;
case '9': return 9; break;
case '0': return 0; break;
default: return 11; break;
}
}
return 11;
}
}
// DRAWBAR: # Esta função faz uma matemática simples para saber quantos dos 16 blocos da tela devem
// ser preenchidos de acordo com a porcentagem (0 a 100%) da bomba sendo armada/desarmada.
void drawBar(byte porcent){
//TODO: Otimizar este código
int box=(8*porcent)/10;
lcd.setCursor(0,1);
while(box>=5){
if(box>=5)
{
lcd.write(4);
box-=5;
}
}
switch(box){
case 0: break;
case 1: lcd.write((uint8_t)0); break;
case 2: lcd.write(1); break;
case 3: lcd.write(2); break;
case 4: lcd.write(3); break;
}
}
// CLS: Função atalho para apagar tudo da tela e colocar o "cursor de escrita" no canto superior esquerdo (0,0).
void cls(){
lcd.clear();
lcd.setCursor(0,0);
}
//CONFIGURAÇÕES DE TEMPO
void printTime(unsigned long minutos, unsigned long aTiempo){
timeCalcVar=minutos-aTiempo/60000;
//Horas
if(timeCalcVar/60==0 && refresh){
lcd.clear();
refresh=false;
//delay(100);
lcd.setCursor(3,1);
Serial.println("!!!!");
}
if(timeCalcVar/60>=1){
if(timeCalcVar/60<10)
{
lcd.setCursor(2,1);
lcd.print("0");
lcd.print(timeCalcVar/60);
}
else
{
lcd.print(timeCalcVar/60);
}
lcd.print(":");
}
//minutos
if(timeCalcVar%60<10)
{
lcd.print("0");
lcd.print(timeCalcVar%60);
}
else
{
lcd.print(timeCalcVar%60);
}
lcd.print(":");
//segundos
timeCalcVar=aTiempo/1000;
if(59-(timeCalcVar%60)<10)
{
lcd.print("0");
lcd.print(59-(timeCalcVar%60));
}
else
{
lcd.print(59-(timeCalcVar%60));
}
lcd.print(":");
//isso não bate com o tempo real, é apenas um efeito, diz 999 porque millis%1000 às vezes dá 0 KKK
lcd.print(999-(millis()%1000));
}
//CONFIGURAÇÕES DE TEMPO
void printTimeDom(unsigned long aTiempo, boolean showMillis){
//minutos
if((aTiempo/60000)<10)
{
lcd.print("0");
lcd.print(aTiempo/60000);
}
else
{
lcd.print(aTiempo/60000);
}
lcd.print(":");
//segundos
if(((aTiempo/1000)%60)<10)
{
lcd.print("0");
lcd.print((aTiempo/1000)%60);
}
else
{
lcd.print((aTiempo/1000)%60);
}
if(showMillis){
lcd.print(":");
//isso não bate com o tempo real, é apenas um efeito, diz 999 porque millis%1000 às vezes dá 0 KKK
lcd.print(999-millis()%1000);
}
}
//START GAME COUNT: Mecanismo que controla a tela de inicio dos jogos (entre config e o jogo).
void startGameCount(){
lcd.clear();
lcd.setCursor(1,0);
lcd.print("VAMOS COMECAR!");
lcd.setCursor(0,1);
lcd.print("PRESSIONE #");
keypad.waitForKey();//se você pressionar um botão o jogo começa
cls();
lcd.setCursor(1,0);
lcd.print("COMECANDO JOGO");
for(int i = 5; i > 0 ; i--){ // INICIA A CONTAGEM PARA COMEÇAR O JOGO
lcd.setCursor(5,1);
tone(tonepin,2000,100);
lcd.print("EM ");
lcd.print(i);
delay(1000);
}
cls();
}
//SEM INFORMAÇÕES
void checkArrows(byte i,byte maxx ){
if(i==0){
lcd.setCursor(15,1);
lcd.write(6);
}
if(i==maxx){
lcd.setCursor(15,0);
lcd.write(5);
}
if(i>0 && i<maxx){
lcd.setCursor(15,1);
lcd.write(6);
lcd.setCursor(15,0);
lcd.write(5);
}
}
//ACTIVATE RELAY: Meacnismo de acionamento do RELE
//Comando direto do RELE
void activateRelay(){
/*
A ativação e desativação do relé estao com termos invertidos, no sistema WOKWI,
o rele de teste funcionava inversamente ao padrão por isso foi invertido
cuidar ao montar o projeto qual sera o comportamento do rele, se necessario desinverter.
*/
digitalWrite(RELAYPIN, LOW); // Aciona relé 1
digitalWrite(RELAY2PIN, LOW); // Aciona relé 2
delay(RELAY_TIME); // Tempo que a sirene fica ligada
digitalWrite(RELAYPIN, HIGH); // Desliga relé 1
digitalWrite(RELAY2PIN, HIGH); // Desliga relé 2
}
// EPLODE SPLASH: Mecanismo de EXPLOSÃO quando tempo encerra com bomba acionada.
//Aciona a função do Rele
void explodeSplash(){
digitalWrite(BLUELED, LOW);
digitalWrite(YELLOWLED, LOW);
cls();
delay(100);
endGame = false;
lcd.setCursor(1,0);
lcd.print("ATAQUE GANHOU");
lcd.setCursor(4,1);
lcd.print("FIM DE JOGO");
for(int i = 200; i>0; i--)// este é o som de explosão em ultra alta definição xD
{
tone(tonepin,i);
delay(20);
}
noTone(tonepin);
if(relayEnable){
activateRelay();
}
delay(5000);
cls();
//código final
lcd.print("JOGAR NOVAMENTE?");
lcd.setCursor(0,1);
lcd.print("A : Sim B : Nao");
while(1)
{
var = keypad.waitForKey();
if(var == 'a' ){
tone(tonepin,2400,30);
//Temos duas opções, jogar buscar & destruir e sabotagem novamente, então!
if(sdStatus){
startGameCount();
search();
}
if(saStatus){
saStatus=true;
startGameCount();
start=true; //para definir iTime para o millis() atual :D
sabotage();
}
}
if(var == 'b' ){
tone(tonepin,2400,30);
menuPrincipal();
break;
}
}
}
//FAIL SPLASH: Mecanismo de avisos da tela em caso de FIM DO TEMPO da bomba.
void failSplash(){
digitalWrite(BLUELED, LOW);
digitalWrite(YELLOWLED, LOW);
cls();
delay(100);
endGame = false;
lcd.setCursor(1,0);
lcd.print(" TEMPO ESGOTADO");
lcd.setCursor(4,1);
lcd.print("FIM DE JOGO");
for(int i = 200; i>0; i--)// este é o som de explosão em ultra alta definição xD
{
tone(tonepin,i);
delay(20);
}
noTone(tonepin);
delay(5000);
cls();
//código final
lcd.print("JOGAR NOVAMENTE?");
lcd.setCursor(0,1);
lcd.print("A : Sim B : Nao");
while(1)
{
var = keypad.waitForKey();
if(var == 'a' ){
tone(tonepin,2400,30);
//Temos duas opções, jogar buscar & destruir e sabotagem novamente, então!
if(sdStatus){
startGameCount();
search();
}
if(saStatus){
saStatus=true;
startGameCount();
start=true; //para definir iTime para o millis() atual :D
sabotage();
}
}
if(var == 'b' ){
tone(tonepin,2400,30);
menuPrincipal();
break;
}
}
}
//DISARMED SPLASH: Mecanismo de avisos da tela em caso de DESARME da bomba.
void disarmedSplash(){
endGame = false;
digitalWrite(BLUELED, LOW);
digitalWrite(YELLOWLED, LOW);
if(sdStatus || saStatus){
lcd.clear();
lcd.setCursor(0,0);
lcd.print("BOMBA DESARMADA");
lcd.setCursor(3,1);
lcd.print("DEFESA GANHOU");
digitalWrite(YELLOWLED, HIGH);
delay(5000);
digitalWrite(YELLOWLED, LOW);
}
//código final
lcd.clear();
lcd.print("JOGAR NOVAMENTE?");
lcd.setCursor(0,1);
lcd.print("A : Sim B : Nao");
digitalWrite(BLUELED, LOW);
digitalWrite(YELLOWLED, LOW);
while(1)
{
var = keypad.waitForKey();
if(var == 'a' ){
tone(tonepin,2400,30);
//Temos duas opções, jogar buscar & destruir e sabotagem novamente, então!
if(sdStatus){
startGameCount();
search();
}
if(saStatus){
saStatus=true;
startGameCount();
start=true; //para definir iTime para o millis() atual :D
sabotage();
}
}
if(var == 'b' ){
tone(tonepin,2400,30);
menuPrincipal();
break;
}
}
}
// SEARCH "BUSCAR E DESTUIR / ARMAR E DESARMAR"): A lógica do modo "Buscar e Destruir" começa aqui.
// A bomba não está armada ainda, apenas o relógio da partida está contando.
//Se o tempo da partida zerar, é Fim de Jogo.
void search() {
refresh = true;
cls();
digitalWrite(BLUELED, LOW);
digitalWrite(YELLOWLED, LOW);
//CONFIGURA O TEMPO INICIAL
int minutos = GAMEMINUTES - 1;
unsigned long iTime = millis(); // tempo inicial em milissegundos
unsigned long aTime;
//var='o';
//Código de Início de Jogo
while (1) { // este é o código importante, é um pouco bagunçado mas funciona bem.
//Se você falhar ao desarmar.
if (endGame) {
failSplash();
}
//Código para o piscar do led
timeCalcVar = (millis() - iTime) % 1000;
if (timeCalcVar >= 0 && timeCalcVar <= 50)digitalWrite(YELLOWLED, HIGH);
if (timeCalcVar >= 90 && timeCalcVar <= 130)digitalWrite(YELLOWLED, LOW);
lcd.setCursor(3, 0);
lcd.print(GAME_TIME_TOP);
aTime = millis() - iTime;
lcd.setCursor(3, 1);
//IMPRIME O TEMPO NO LCD
printTime(minutos, aTime);
//###########################VERIFICAÇÕES##################
//Verifica Se o Jogo Acabou
if (minutos - aTime / 60000 == 0 && 59 - ((aTime / 1000) % 60) == 0)failSplash();
//Serial.println(keypad.getKey());
//USADO NO JOGO COM SENHA
if ('d' == keypad.getKey() && passwordEnable) {
lcd.clear();
lcd.setCursor(2, 0);
lcd.print(ARMING_BOMB);
delay(1000);//um pequeno atraso para pensar na senha
lcd.clear();
lcd.setCursor(0, 0);
lcd.print(ENTER_CODE);
setCodeTime();// precisamos definir a variável de comparação primeiro, ela escreve em codeInput[]
//então compara :D
if (comparePassword()) destroy(); // Chama a função que inica a contagem pra explodir
lcd.clear();
lcd.setCursor(3, 0);
lcd.print(CODE_ERROR);
if (soundEnable)tone(tonepin, errorTone, 200);
delay(500);
cls();
}
//Verifica Se Está Ativando
while (defusing && !passwordEnable)
{
digitalWrite(YELLOWLED, LOW);
lcd.clear();
lcd.setCursor(2, 0);
lcd.print(ARMING_BOMB);
lcd.setCursor(0, 1);
unsigned int percent = 0;
unsigned long xTime = millis(); //inicia o tempo de desativação
while (defusing)
{
keypad.getKey();
percent = (millis() - xTime) / (ACTIVATESECONDS * 10);
drawBar(percent);
//verifica se o tempo de jogo acaba durante a desativação
aTime = millis() - iTime;
Serial.println(millis()-xTime);
if ((minutos - aTime / 60000 == 0 && 59 - ((aTime / 1000) % 60) == 0) || minutos - aTime / 60000 > 4000000000) {
endGame = true;
}
timeCalcVar = (millis() - xTime) % 1000;
if ( timeCalcVar >= 0 && timeCalcVar <= 40)
{
digitalWrite(BLUELED, HIGH);
if (soundEnable)tone(tonepin, alarmTone1, 200);
}
if (timeCalcVar >= 480 && timeCalcVar <= 520)
{
if (soundEnable)tone(tonepin, alarmTone2, 200);
digitalWrite(BLUELED, LOW);
}
if (percent >= 100)
{
digitalWrite(YELLOWLED, LOW);
destroy();// pula para o próximo modo de jogo (A contagem regressiva da explosão)
}
}
cls();
digitalWrite(BLUELED, LOW);
}
}
}
// DESTROY: A bomba foi ativada! Esta função "destroy" gerencia o desespero: bipando mais rápido
// conforme o tempo cai e piscando a luz vermelha. Se o tempo zerar chama a "explodeSplash".
void destroy() {
lcd.clear();
lcd.setCursor(3, 0);
lcd.print(BOMB_ARMED);
delay(1000);
lcd.setCursor(0, 0);
lcd.print(" ");
int minutos = BOMBMINUTES - 1;
unsigned long iTime = millis();
unsigned long aTime;
int largoTono = 50;
//LOOP PRINCIPAL
while (1) {
//Se você falhar ao desarmar.
if (endGame) {
explodeSplash();
}
//Piscar do Led
timeCalcVar = (millis() - iTime) % 1000;
if (timeCalcVar >= 0 && timeCalcVar <= 40)
{
digitalWrite(BLUELED, HIGH);
if (soundEnable)tone(tonepin, activeTone, largoTono);
}
if (timeCalcVar >= 180 && timeCalcVar <= 220) {
digitalWrite(BLUELED, LOW);
}
//Som
aTime = millis() - iTime;
timeCalcVar = (millis() - iTime) % 1000;
if (timeCalcVar >= 245 && timeCalcVar <= 255 && minutos - aTime / 60000 < 2 && soundEnable)tone(tonepin, activeTone, largoTono);
if (timeCalcVar >= 495 && timeCalcVar <= 510 && minutos - aTime / 60000 < 4 && soundEnable)tone(tonepin, activeTone, largoTono);
if (timeCalcVar >= 745 && timeCalcVar <= 760 && minutos - aTime / 60000 < 2 && soundEnable)tone(tonepin, activeTone, largoTono);
if ( minutos - aTime / 60000 == 0 && 59 - ((aTime / 1000) % 60) < 10)largoTono = 300;
lcd.setCursor(1, 0);
lcd.print(DETONATION_IN);
//Tempo Passado
lcd.setCursor(3, 1);
////////AQUI ESTÃO AS DUAS OPÇÕES QUE ENCERRAM O JOGO///////////
////O TEMPO ACABOU E A BOMBA EXPLODE
if (minutos - aTime / 60000 == 0 && 59 - ((aTime / 1000) % 60) == 0) // Verifica Se o Jogo Acabou
{
explodeSplash();
}
//imprime o tempo
printTime(minutos, aTime);
//// SEGUNDA OPÇÃO: VOCÊ PRESSIONA O BOTÃO DE DESARME
//SE FOR UM JOGO COM SENHA E O JOGADOR APERTOU 'D'
if ('d' == keypad.getKey() && passwordEnable) {
// --- VERIFICAÇÃO DO PENDRIVE ---
if (pendriveEnable && digitalRead(PINO_PENDRIVE) == HIGH) {
// Se o pendrive for exigido e NÃO estiver conectado (HIGH):
lcd.clear();
lcd.setCursor(0, 0);
lcd.print("INSIRA A CHAVE!"); // Mensagem de erro
if (soundEnable) tone(tonepin, errorTone, 200);
delay(1500);
cls();
}
else {
// Se estiver tudo certo, libera o desarme por senha normalmente:
lcd.clear();
lcd.setCursor(1, 0);
lcd.print(DISARMING);
delay(1000);//um pequeno atraso para pensar na senha
lcd.clear();
lcd.setCursor(0, 0);
lcd.print(ENTER_CODE);
setCodeTime();// precisamos definir a variável de comparação primeiro
//então compara :D
if (comparePassword()) {
disarmedSplash();
}
lcd.clear();
lcd.setCursor(3, 0);
lcd.print(CODE_ERROR);
if (soundEnable)tone(tonepin, errorTone, 200);
delay(500);
cls();
}
} // Fim da lógica de desarme COM senha
// SE FOR UM JOGO SEM SENHA (JOGADOR SEGURANDO O BOTÃO 'D')
if (defusing && !passwordEnable) // desarmando a bomba
{
// --- VERIFICAÇÃO DO PENDRIVE ---
if (pendriveEnable && digitalRead(PINO_PENDRIVE) == HIGH) {
// Se o pendrive for exigido e NÃO estiver conectado (HIGH):
lcd.clear();
lcd.setCursor(0, 0);
lcd.print("INSIRA A CHAVE!"); // Mensagem de erro
if (soundEnable) tone(tonepin, errorTone, 200);
delay(1500);
defusing = false; // Cancela a barra de progresso na hora
cls();
}
else {
// Se estiver tudo certo, libera a barra de progresso normalmente:
lcd.clear();
digitalWrite(BLUELED, LOW);
lcd.setCursor(3, 0);
lcd.print(DISARM);
lcd.setCursor(0, 1);
unsigned int percent = 0;
unsigned long xTime = millis();
while (defusing)
{
keypad.getKey();
//verifica se o tempo de jogo acaba durante a desativação
aTime = millis() - iTime;
if ((minutos - aTime / 60000 == 0 && 59 - ((aTime / 1000) % 60) == 0) || minutos - aTime / 60000 > 4000000000) {
endGame = true;
}
timeCalcVar = (millis() - xTime) % 1000;
if (timeCalcVar >= 0 && timeCalcVar <= 20)
{
digitalWrite(YELLOWLED, HIGH);
if (soundEnable)tone(tonepin, alarmTone1, 200);
}
if (timeCalcVar >= 480 && timeCalcVar <= 500)
{
if (soundEnable)tone(tonepin, alarmTone2, 200);
digitalWrite(YELLOWLED, LOW);
}
unsigned long seconds = (millis() - xTime);
percent = seconds / (ACTIVATESECONDS * 10);
drawBar(percent);
//BOMBA DESARMADA FIM DE JOGO
if (percent >= 100)disarmedSplash();
}
digitalWrite(BLUELED, LOW);
digitalWrite(YELLOWLED, LOW);
cls();
}
} // Fim da lógica de desarme SEM senha
}
}
//SABOTAGEM: O modo Sabotage é muito similar ao Buscar e Destruir.
// A principal diferença (visível na função destroySabotage abaixo) é que se alguém conseguir desarmar,
// a bomba não encerra o jogo! Ela volta para este status neutro para ser armada de novo até o tempo limite acabar.
void sabotage() {
endGame = false;
refresh = true;
cls();
digitalWrite(BLUELED, LOW);
digitalWrite(YELLOWLED, LOW);
//CONFIGURA O TEMPO INICIAL
int minutos = GAMEMINUTES - 1;
if (start) {
iTime = millis(); // tempo inicial do jogo, use isso porque o modo sabotagem pode retornar para sabotage()
start = false;
}
unsigned long aTime;
//Código de Início de Jogo
while (1) { // este é o código importante, é um pouco bagunçado mas funciona bem.
if (endGame) {
failSplash();
}
//Código para o piscar do led
timeCalcVar = (millis() - iTime) % 1000;
if (timeCalcVar >= 0 && timeCalcVar <= 50)digitalWrite(YELLOWLED, HIGH);
if (timeCalcVar >= 90 && timeCalcVar <= 130)digitalWrite(YELLOWLED, LOW);
lcd.setCursor(3, 0);
lcd.print(GAME_TIME);
aTime = millis() - iTime;
lcd.setCursor(3, 1);
//IMPRIME O TEMPO NO LCD
printTime(minutos, aTime);
//###########################VERIFICAÇÕES##################
//Verifica Se o Jogo Acabou
if (minutos - aTime / 60000 == 0 && 59 - ((aTime / 1000) % 60) == 0) {
failSplash();
}
//USADO NO JOGO COM SENHA
if ('d' == keypad.getKey() && passwordEnable) {
lcd.clear();
lcd.setCursor(2, 0);
lcd.print(ARMING_BOMB);
delay(1000);//um pequeno atraso para pensar na senha
lcd.clear();
lcd.setCursor(0, 0);
lcd.print(ENTER_CODE);
setCodeTime();// precisamos definir a variável de comparação primeiro
//então compara :D
if (comparePassword()) {
destroySabotage();
}
lcd.clear();
lcd.setCursor(2, 0);
lcd.print(CODE_ERROR);
if (soundEnable)tone(tonepin, errorTone, 200);
delay(500);
cls();
}
//Verifica Se Está Ativando
while (defusing && !passwordEnable)
{
keypad.getKey();
cls();
digitalWrite(YELLOWLED, LOW);
lcd.clear();
lcd.setCursor(2, 0);
lcd.print(ARMING_BOMB);
lcd.setCursor(0, 1);
unsigned int percent = 0;
unsigned long xTime = millis(); //inicia o tempo de desativação
while (defusing)
{
keypad.getKey();
//verifica se o tempo de jogo acaba durante a desativação
aTime = millis() - iTime;
if ((minutos - aTime / 60000 == 0 && 59 - ((aTime / 1000) % 60) == 0) || minutos - aTime / 60000 > 4000000000)endGame = true;
timeCalcVar = (millis() - xTime) % 1000;
if ( timeCalcVar >= 0 && timeCalcVar <= 40)
{
digitalWrite(BLUELED, HIGH);
if (soundEnable)tone(tonepin, alarmTone1, 200);
}
if (timeCalcVar >= 480 && timeCalcVar <= 520)
{
if (soundEnable)tone(tonepin, alarmTone2, 200);
digitalWrite(BLUELED, LOW);
}
unsigned long seconds = millis() - xTime;
percent = (seconds) / (ACTIVATESECONDS * 10);
drawBar(percent);
if (percent >= 100)
{
digitalWrite(YELLOWLED, LOW);
destroySabotage();// pula para o próximo modo de jogo
}
}
cls();
digitalWrite(BLUELED, LOW);
}
}
}
//DESTROY SABOTAGE: Mecanismo proprio do modo sabotagem.
void destroySabotage() {
endGame = false;
lcd.clear();
lcd.setCursor(3, 0);
lcd.print(BOMB_ARMED);
delay(1000);
int minutos = BOMBMINUTES - 1;
unsigned long iTime = millis();
unsigned long aTime;
int largoTono = 50;
//LOOP PRINCIPAL
while (1) {
//Se você falhar ao desarmar.
if (endGame) {
explodeSplash();
}
//Piscar do Led
timeCalcVar = (millis() - iTime) % 1000;
if (timeCalcVar >= 0 && timeCalcVar <= 40)
{
digitalWrite(BLUELED, HIGH);
if (soundEnable)tone(tonepin, activeTone, largoTono);
}
if (timeCalcVar >= 180 && timeCalcVar <= 220) {
digitalWrite(BLUELED, LOW);
}
//Som
timeCalcVar = (millis() - iTime) % 1000;
aTime = millis() - iTime;
if (timeCalcVar >= 245 && timeCalcVar <= 255 && minutos - aTime / 60000 < 2 && soundEnable)tone(tonepin, activeTone, largoTono);
if (timeCalcVar >= 495 && timeCalcVar <= 510 && minutos - aTime / 60000 < 4 && soundEnable)tone(tonepin, activeTone, largoTono);
if (timeCalcVar >= 745 && timeCalcVar <= 760 && minutos - aTime / 60000 < 2 && soundEnable)tone(tonepin, activeTone, largoTono);
if ( minutos - aTime / 60000 == 0 && 59 - ((aTime / 1000) % 60) < 10)largoTono = 300;
lcd.setCursor(1, 0);
lcd.print(DETONATION_IN);
//Tempo Passado
lcd.setCursor(3, 1);
////////AQUI ESTÃO AS DUAS OPÇÕES QUE ENCERRAM O JOGO///////////
////O TEMPO ACABOU E A BOMBA EXPLODE
if (minutos - aTime / 60000 == 0 && 59 - ((aTime / 1000) % 60) == 0) // Verifica Se o Jogo Acabou
{
explodeSplash();
}
//imprime o tempo
printTime(minutos, aTime);
//// SEGUNDA OPÇÃO: VOCÊ PRESSIONA O BOTÃO DE DESARME
//SE FOR UM JOGO COM SENHA E O JOGADOR APERTOU 'D'
if ('d' == keypad.getKey() && passwordEnable) {
// --- VERIFICAÇÃO DO PENDRIVE ---
if (pendriveEnable && digitalRead(PINO_PENDRIVE) == HIGH) {
// Se o pendrive for exigido e NÃO estiver conectado (HIGH):
lcd.clear();
lcd.setCursor(0, 0);
lcd.print("INSIRA A CHAVE!"); // Mensagem de erro
if (soundEnable) tone(tonepin, errorTone, 200);
delay(1500);
cls();
}
else {
// Se a chave estiver no lugar, libera o desarme por senha:
cls();
digitalWrite(BLUELED, LOW);
digitalWrite(YELLOWLED, HIGH);
lcd.print(DISARMING);
delay(1000);//um pequeno atraso para pensar na senha
lcd.clear();
lcd.setCursor(0, 0);
lcd.print(ENTER_CODE);
setCodeTime();// precisamos definir a variável de comparação primeiro
//então compara :D
if (comparePassword()) {
sabotage(); // No modo sabotagem, volta pro estado neutro!
}
lcd.clear();
lcd.setCursor(2, 0);
lcd.print(CODE_ERROR);
if (soundEnable)tone(tonepin, errorTone, 200);
delay(500);
cls();
}
} // Fim da lógica de desarme COM senha
// SE FOR UM JOGO SEM SENHA (JOGADOR SEGURANDO O BOTÃO 'D')
if (defusing && !passwordEnable) // desarmando a bomba
{
// --- VERIFICAÇÃO DO PENDRIVE ---
if (pendriveEnable && digitalRead(PINO_PENDRIVE) == HIGH) {
// Se o pendrive for exigido e NÃO estiver conectado (HIGH):
lcd.clear();
lcd.setCursor(0, 0);
lcd.print("INSIRA A CHAVE!"); // Mensagem de erro
if (soundEnable) tone(tonepin, errorTone, 200);
delay(1500);
defusing = false; // Cancela a barra de progresso na hora
cls();
}
else {
// Se a chave estiver no lugar, libera a barra de progresso:
lcd.clear();
digitalWrite(BLUELED, LOW);
lcd.setCursor(3, 0);
lcd.print(DISARM);
lcd.setCursor(0, 1);
unsigned int percent = 0;
unsigned long xTime = millis();
while (defusing)
{
keypad.getKey();
//verifica se o tempo de jogo acaba durante a desativação
aTime = millis() - iTime;
if ((minutos - aTime / 60000 == 0 && 59 - ((aTime / 1000) % 60) == 0) || minutos - aTime / 60000 > 4000000000) {
endGame = true;
}
timeCalcVar = (millis() - xTime) % 1000;
if (timeCalcVar >= 0 && timeCalcVar <= 20)
{
digitalWrite(YELLOWLED, HIGH);
if (soundEnable)tone(tonepin, alarmTone1, 200);
}
if (timeCalcVar >= 480 && timeCalcVar <= 500)
{
if (soundEnable)tone(tonepin, alarmTone2, 200);
digitalWrite(YELLOWLED, LOW);
}
unsigned long seconds = (millis() - xTime);
percent = seconds / (ACTIVATESECONDS * 10);
drawBar(percent);
//BOMBA DESARMADA RETORNA PARA SABOTAGEM
if (percent >= 100)
{
cls();
lcd.print("BOMBA DESARMADA");
delay(1000);
sabotage(); // Volta pro modo neutro de sabotagem
}
}
digitalWrite(BLUELED, LOW);
digitalWrite(YELLOWLED, LOW);
cls();
}
} // Fim da lógica de desarme SEM senha
}
}
//DOMINAÇÃO: Mecnismo de configuração do modo dominação.
//Aqui o objetivo não é explodir nada.
// Se você segurar o botão, a bomba muda para a cor do seu time (Vermelho ou Verde) e começa a somar pontos.
// Quem somar mais tempo segurando o controle da bomba até o timer geral zerar, vence a partida.
void domination(){
//CONFIGURA O TEMPO INICIAL
int minutos = GAMEMINUTES-1;
boolean showGameTime=true;
unsigned long a;
unsigned long iTime=millis(); // tempo inicial em milissegundos
unsigned long aTime;
team=0;
iZoneTime=0;
aTime=0;
blueTime=0;
yellowTime=0;
int largoTono = 50;
// 0 = neutro, 1 = time verde, 2 = time vermelho
a=millis();
//Código de Início de Jogo
while(1) // este é o código importante, é um pouco bagunçado mas funciona bem.
{
if(endGame){
gameOver();
}
keypad.getKey();
aTime=millis()- iTime;
//Código para o piscar do led
timeCalcVar=(millis()- iTime)%1000;
if(timeCalcVar >= 0 && timeCalcVar <= 40)
{
if(team==1)digitalWrite(YELLOWLED, HIGH);
if(team==2)digitalWrite(BLUELED, HIGH);
}
if(timeCalcVar >= 50 && timeCalcVar <= 100)
{
if(team==1)digitalWrite(YELLOWLED, LOW);
if(team==2)digitalWrite(BLUELED, LOW);
}
// Som!!! mesmo do Destroy
if(timeCalcVar >= 0 && timeCalcVar <= 40 && soundEnable)tone(tonepin,activeTone,largoTono);
if(timeCalcVar >= 245 && timeCalcVar <= 255 && minutos-aTime/60000<2 && soundEnable)tone(tonepin,activeTone,largoTono);
if(timeCalcVar >= 495 && timeCalcVar <= 510 && minutos-aTime/60000<4 && soundEnable)tone(tonepin,activeTone,largoTono);
if(timeCalcVar >= 745 && timeCalcVar <= 760 && minutos-aTime/60000<2 && soundEnable)tone(tonepin,activeTone,largoTono);
//Ajuda a contar 3 segs
if(a+2000<millis()){
a=millis();
showGameTime=!showGameTime;
cls();
}
//OS PRÓXIMOS DOIS MÉTODOS MOSTRAM "TEMPO DE JOGO" E "TEMPO DA ZONA CONTROLADA" ELES MOSTRAM 2 E 2 SEG CADA
if(showGameTime){ //O SEGUNDO É /2
lcd.setCursor(0,0);
lcd.print("TEMPO DE JOGO");
lcd.setCursor(3,1);
printTime(minutos, aTime);
}
else if (!showGameTime){
lcd.setCursor(0,0);
if(team == 0)lcd.print("ZONA NEUTRA");
if(team == 1)lcd.print("ZONA AMARELA");
if(team == 2)lcd.print("ZONA AZUL");
if(team>0){
lcd.setCursor(3,1);
printTimeDom(millis()-iZoneTime,true);
}
}
//###########################VERIFICAÇÕES##################
//Verifica Se o Jogo Acabou
if(minutos-aTime/60000==0 && 59-((aTime/1000)%60)==0)
{
gameOver();
}
//Verifica Se ESTÁ neutro
while((defusing || cancelando) && team > 0)
{
cls();
if(team>0)lcd.print("NEUTRALIZANDO...");
lcd.setCursor(0,1);
unsigned int percent=0;
unsigned long xTime=millis(); //inicia o tempo de desativação
while(defusing || cancelando)
{
//verifica se o tempo de jogo acaba durante a desativação
aTime= millis()- iTime;
if((minutos-aTime/60000==0 && 59-((aTime/1000)%60)==0) || minutos-aTime/60000>4000000000){
endGame = true;
}
keypad.getKey();
timeCalcVar = (millis()- xTime)%1000;
if( timeCalcVar >= 0 && timeCalcVar <= 20)
{
if(soundEnable)tone(tonepin,alarmTone1,200);
}
if(timeCalcVar >= 480 && timeCalcVar <= 500)
{
if(soundEnable)tone(tonepin,alarmTone2,200);
digitalWrite(BLUELED, LOW);
}
unsigned long seconds= millis() - xTime;
percent = (seconds)/(ACTIVATESECONDS*10);
drawBar(percent);
if(percent >= 100)
{
delay(1000);
if(team==1){
yellowTime+=millis()-iZoneTime;
iZoneTime=0;
}
if(team==2){
blueTime+=millis()-iZoneTime;
iZoneTime=0;
}
team=0;
break;
}
}
cls();
}
//Capturando vermelho
while(defusing && team == 0 )
{
cls();
if(team==0)lcd.print(" CAPTURANDO ZONA");
lcd.setCursor(0,1);
unsigned int percent=0;
unsigned long xTime=millis(); //inicia o tempo de desativação
while(defusing)
{
keypad.getKey();
//verifica se o tempo de jogo acaba durante a desativação
aTime= millis()- iTime;
if((minutos-aTime/60000==0 && 59-((aTime/1000)%60)==0) || minutos-aTime/60000>4000000000){
endGame = true;
}
timeCalcVar = (millis()- xTime)%1000;
if( timeCalcVar >= 0 && timeCalcVar <= 20)
{
digitalWrite(BLUELED, HIGH);
if(soundEnable)tone(tonepin,alarmTone1,200);
}
if(timeCalcVar >= 480 && timeCalcVar <= 500)
{
if(soundEnable)tone(tonepin,alarmTone2,200);
digitalWrite(BLUELED, LOW);
}
unsigned long seconds= millis() - xTime;
percent = (seconds)/(ACTIVATESECONDS*10);
drawBar(percent);
if(percent >= 100)
{
digitalWrite(YELLOWLED, LOW);
team=2;
iZoneTime=millis();
delay(1000);
break;
}
}
cls();
digitalWrite(BLUELED, LOW);
}
//indo para a zona verde
while(cancelando && team == 0 )
{
cls();
if(team==0)lcd.print(" CAPTURANDO ZONA");
lcd.setCursor(0,1);
unsigned int percent=0;
unsigned long xTime=millis(); //inicia o tempo de desativação
while(cancelando)
{
keypad.getKey();
//verifica se o tempo de jogo acaba durante a desativação
aTime= millis()- iTime;
if((minutos-aTime/60000==0 && 59-((aTime/1000)%60)==0) || minutos-aTime/60000>4000000000){
endGame = true;
}
timeCalcVar = (millis()- xTime)%1000;
if( timeCalcVar >= 0 && timeCalcVar <= 20)
{
digitalWrite(YELLOWLED, HIGH);
if(soundEnable)tone(tonepin,alarmTone1,200);
}
if(timeCalcVar >= 480 && timeCalcVar <= 500)
{
if(soundEnable)tone(tonepin,alarmTone2,200);
digitalWrite(YELLOWLED, LOW);
}
unsigned long seconds= millis() - xTime;
percent = (seconds)/(ACTIVATESECONDS*10);
drawBar(percent);
if(percent >= 100)
{
digitalWrite(YELLOWLED, LOW);
team=1;
iZoneTime=millis();
delay(1000);
break;
}
}
cls();
digitalWrite(YELLOWLED, LOW);
}
}
}
//GAME OVER: Mecanismo de fim de jogo DOMINAÇÃO.
//Ajuste de textos, tempo, checagem...
void gameOver(){
if(team==1)yellowTime+=millis()-iZoneTime;
if(team==2)blueTime+=millis()-iZoneTime;
digitalWrite(YELLOWLED, LOW);
digitalWrite(BLUELED, LOW);
while(!defusing){
keypad.getKey();
if(defusing){
keypad.getKey();
break;
}
lcd.clear();
lcd.setCursor(0,0);
lcd.print("TEMPO ENCERRADO!");
lcd.setCursor(0,1);
///verifica qual time ganhou a base
if(yellowTime>blueTime){
//time verde ganha
lcd.print("AMARELO GANHOU");
digitalWrite(YELLOWLED, HIGH);
// DISPARA RELÉ 1 SE ESTIVER ATIVADO NO MENU
if (relayEnable) {
digitalWrite(RELAYPIN, LOW);
delay(RELAY_TIME);
digitalWrite(RELAYPIN, HIGH);
}
}
else{
//time vermelho ganha
lcd.print("AZUL GANHOU");
digitalWrite(BLUELED, HIGH);
// DISPARA RELÉ 2 SE ESTIVER ATIVADO NO MENU
if (relayEnable) {
digitalWrite(RELAY2PIN, LOW);
delay(RELAY_TIME);
digitalWrite(RELAY2PIN, HIGH);
}
}
delay(3000);
keypad.getKey();
if(defusing){
keypad.getKey();
break;
}
cls();
lcd.print("TIME AZUL:");
lcd.setCursor(5,1);
printTimeDom(blueTime,false);
delay(3000);
keypad.getKey();
if(defusing){
break;
}
cls();
lcd.print("TIME AMARELO:");
lcd.setCursor(5,1);
printTimeDom(yellowTime,false);
delay(3000);
keypad.getKey();
if(defusing){
keypad.getKey();
break;
}
}
cls();
delay(100);
lcd.print("JOGAR NOVAMENTE?");
lcd.setCursor(0,1);
lcd.print("A : Sim B : Nao");
while(1)
{
var = keypad.waitForKey();
if(var == 'a' ){
tone(tonepin,2400,30);
cls();
domination();
break;
}
if(var == 'b' ){
tone(tonepin,2400,30);
menuPrincipal();
break;
}
}
}
//AUTOTEST: Mecanismo de teste dos perifericos
//LEDS, buzzer, rele...
void autoTest() {
cls();
lcd.print("INICIANDO TESTE");
lcd.setCursor(0,1);
lcd.print("AGUARDE...");
delay(2000);
// 1. Teste do LED Vermelho
cls();
lcd.print("TESTE: BLUE LED");
digitalWrite(BLUELED, HIGH);
delay(1500);
digitalWrite(BLUELED, LOW);
lcd.setCursor(0,1);
lcd.print("[ OK ]");
delay(1000);
// 2. Teste do LED Verde
cls();
lcd.print("TESTE: YELL LED");
digitalWrite(YELLOWLED, HIGH);
delay(1500);
digitalWrite(YELLOWLED, LOW);
lcd.setCursor(0,1);
lcd.print("[ OK ]");
delay(1000);
// 3. Teste do Buzzer (Som)
cls();
lcd.print("TESTE: BUZZER");
tone(tonepin, 2600, 500); // Toca um bip agudo
delay(600);
tone(tonepin, 1330, 500); // Toca um bip mais grave
delay(1000);
lcd.setCursor(0,1);
lcd.print("[ OK ]");
delay(1000);
// 4. Teste do Relé
// 4. Teste dos Relés
cls();
lcd.print("TESTE: RELE 1/2");
digitalWrite(RELAYPIN, LOW); // Estala Relé 1 (Verde/Explosão)
delay(1000);
digitalWrite(RELAY2PIN, LOW); // Estala Relé 2 (Vermelho/Explosão)
delay(1000);
digitalWrite(RELAYPIN, HIGH); // Desliga Relé 1
digitalWrite(RELAY2PIN, HIGH); // Desliga Relé 2
lcd.setCursor(0,1);
lcd.print("[ OK ]");
delay(1000);
// Finalização
cls();
lcd.print("SISTEMA 100%");
lcd.setCursor(0,1);
lcd.print("PRONTO PRO JOGO!");
delay(3000);
// Se quiser testar outro pino no futuro ou prolongar os bipes, é só seguir este padrão de "digitalWrite HIGH/LOW"
}
//greentime