Pokémon con NFCs
Introducción
El objetivo de este proyecto es crear una máquina que permita jugar a un juego como Pokémon. Las partidas son de un jugador contra otro jugador. La principal innovación de este sistema es que utiliza códigos NFC, de manera que dependiendo de la tarjeta que se pase por el lector se juega con un Pokémon u otro, siendo cada tarjeta una especie de Pokeball.
Problemas durante el desarrollo
Durante el transcurso de este proyecto nos hemos encontrado diversos problemas a la hora de hacer funcionar todos los componentes en conjunto.
El primer problema con el que dimos fue al montar la pantalla en la placa UNO, que al ser de tipo “shield” y montarse justo encima de esta, ocupaba casi la totalidad de sus puertos, por tanto no era posible conectar también el lector NFC.
Nuestra solución fue adquirir la placa MEGA 2560, con la que obtuvimos más puertos libres con los que trabajar.
Y aquí llegó nuestro segundo problema: vimos que el lector NFC necesitaba los mismos pines que el lector de tarjetas microSD de la pantalla, estos son los pines del bus SPI, que no se pueden cambiar, así que añadimos al sistema una prototype board con la que duplicamos estos pines y pudimos utilizarlos para los dos componentes sin problema.
Cabe destacar que otra solución a estos dos problemas también podría haber sido usar dos placas UNO en conjunto, pero decidimos usar la MEGA con la prototype board por simplicidad y para conseguir un resultado bastante más compacto a la hora de introducirlo en una carcasa estilo GameBoy.
En cuanto al lector NFC, desconocíamos que su correcto uso requería soldarlo, por lo que estuvimos bastante tiempo intentando averiguar por qué no hacía contacto correctamente hasta que dimos con la solución. Una vez soldado dejó de dar problemas.
Componentes
Nombre | Unidades | Precio (31€) |
ELEGOO Pantalla Táctil TFT de 2,8 Pulgadas | 1 | 15€ |
Pila 9V | 1 | 4€ |
AZDelivery RFID (lector NFC) | 1 | 7€ |
AZDelivery RFID Chips | 4 | 5€ |
Arduino Mega 2560 | 1 | – |
MicroSD Sandisk 16GB | 1 | – |
Cableado | – | – |
Hardware
Placa Arduino Mega 2560
Lector de tarjetas
Pantalla táctil TFT
Sistema completo conectado
Software
En cuanto a la base de datos, para crear los distintos pokémon hemos usado unas funciones que nos permiten escribir y guardarlos en la tarjeta SD. Para el proyecto solo tenemos 4 códigos NFC por lo que solo hemos creado 4 pokémon, cada uno con sus atributos. También hemos usado una función para crear fácilmente los movimientos y los pokémon. De esta forma, para incluir un nuevo pokémon solamente habría que aplicar los distintos atributos en la funciones y crear la imagen .bmp para el combate. La nueva información de pokémon o de movimiento se envía a unos documentos en la SD que guardan toda la información, funcionando de forma similar a una base de datos. Las funciones de escritura para guardar en la base reciben todos los campos y los escriben en sus documentos de text. Las funciones de lectura reciben un pokemon o un movimiento y su identificador, buscan en el documento de texto la información solicitada y rellenan los atributos.
Al inicio del juego, se pide por pantalla que el jugador 1 acerque su tarjeta NFC al lector. Una vez leído, pide lo mismo para el Jugador 2 y crea una imagen de fondo con los dos pokémon leídos. Estas tarjetas tienen un código que se utiliza como identificador para los pokémon. Al leer las tarjetas se accede a la base de datos para obtener los demás campos conociendo el ID.
Sobre la pantalla, es importante destacar que es bastante nueva y emplea sus propias librerías por lo que hay pocos ejemplos en internet. Sin embargo, viene con un par de programas de prueba que pueden resultar muy útiles. Lo primero es inicializar la pantalla con los pines correctos. Programar el texto y las pantallas fue muy sencillo, bastaba con recurrir a las funciones de las librerías. Con cada combinación de pokémon se creó un fondo en formato .bmp de 24 bits para que así la pantalla representase fehacientemente el combate. Con los botones pudimos volver a utilizar las librerías. Resulta interesante destacar que permiten bastante personalización, pudiendo variar el color del interior, de la línea exterior e incluyendo un texto dentro del botón.
El diagrama de flujo de una partida completa es el siguiente:
Vídeo explicativo
Enlace al vídeo con la explicación y una prueba del funcionamiento del sistema:
https://drive.google.com/file/d/1ntK_a7MnPx4Yf6eTb42QroKLgJClUcXY/view?usp=sharing
Código completo
Aquí el código del proyecto con algunos comentarios
#include <Elegoo_GFX.h> // Core graphics library
#include <Elegoo_TFTLCD.h> // Hardware-specific library
#include <TouchScreen.h> // Touch screen library
#include <SD.h>
#include <SPI.h>
#include <MFRC522.h> // Lector NFC
#define MINPRESSURE 10 // presión del tactil
#define MAXPRESSURE 1000
#define YP A3 //pines para el tactil
#define XM A2
#define YM 9
#define XP 8
#define TS_MINX 120 //tamaño del tactil
#define TS_MAXX 900
#define TS_MINY 70
#define TS_MAXY 920
#define LCD_CS A3 // Chip Select goes to Analog 3
#define LCD_CD A2 // Command/Data goes to Analog 2
#define LCD_WR A1 // LCD Write goes to Analog 1
#define LCD_RD A0 // LCD Read goes to Analog 0
#define PIN_SD_CS 10 // Elegoo SD shields and modules: pin 10
#define RST_PIN 19 // NFC
#define SS_PIN 20 // NFC
#define LCD_RESET A4 // Arduino's reset pin
// colores
#define BLACK 0x0000
#define BLUE 0x001F
#define RED 0xF800
#define GREEN 0x07E0
#define CYAN 0x07FF
#define MAGENTA 0xF81F
#define YELLOW 0xFFE0
#define WHITE 0xFFFF
// // Color definitions
#define ILI9341_DARKGREEN 0x03E0 /* 0, 128, 0 */
#define ILI9341_DARKGREY 0x7BEF /* 128, 128, 128 */
#define ILI9341_BLUE 0x001F /* 0, 0, 255 */
#define ILI9341_RED 0xF800 /* 255, 0, 0 */
#define ILI9341_ORANGE 0xFD20 /* 255, 165, 0 */
// Inicializar la pantalla TFT y el lector NFC
Elegoo_TFTLCD tft(LCD_CS, LCD_CD, LCD_WR, LCD_RD, LCD_RESET);
TouchScreen ts = TouchScreen(XP, YP, XM, YM, 300);
MFRC522 mfrc522(SS_PIN, RST_PIN);
#define MAX_BMP 10 // bmp file num
//#define FILE_OVERWRITE (O_READ | O_WRITE | O_CREAT)
#define FILENAME_LEN 30 // max file name length
#define BUFFPIXEL 60 // buffers
#define BUFFPIXEL_X3 180
////////////////////// VARIABLES DE IMAGEN ////////////////////////////
const int __Gnbmp_height = 320; // bmp hight
const int __Gnbmp_width = 240; // bmp width
unsigned char __Gnbmp_image_offset = 0; // offset
File bmpFile;
Elegoo_GFX_Button button[4]; //botones y etiquetas para ellos
char mov1, mov2, mov3, mov4;
////////////////////// VARIABLES DE GAMEPLAY ////////////////////////////
struct movimiento {
String nombre;
short tipo; // 0 normal, 1 agua, 2 fuego, 3 planta
short efecto; // 0 daño, 1 defensa, 2 subir ataque, 3 subir salud, 4 resurreccion
float valorEfecto; //valor inicial del efecto al que se le aplican las stats del pokemon para obtener el valor final del ataque
float precision;
};
struct pokemon {
String id; //codigo nfc
String nombre;
short tipo; //0 normal, 1 agua, 2 fuego, 3 planta
struct movimiento movimientos[4] = {{"", 0, 0, 0, 0},{"", 0, 0, 0, 0},{"", 0, 0, 0, 0},{"", 0, 0, 0, 0}};
float vida;
short velocidad;
float ataque; //ataque y defensa son valores float entre 1 y 3 por ejemplo, que se multiplican por el daño o defensa del movimiento
float defensa;
bool defensaActiva = false;
};
//variables globales
struct pokemon p1;
struct pokemon p2;
bool turno_p1; //indica de que jugador es el turno
struct movimiento mov; //para el movimiento a usar en cada turno
bool escaneados= false; // true tras escanear ambos pokemons
bool iniciaCombate = true; //asignar ciertos valores al inicio del combate
bool jugada=false; // true tras pulsar un boton para que se realice una jugada
File dataFile1; //para los archivos de la SD
File dataFile2;
////////////////////////////////////////////////////////////////////
void writePokemon(String id, String nombre, short tipo, String movimientos1, String movimientos2, String movimientos3,
String movimientos4, float vida, short velocidad, int ataque, int defensa) {
dataFile1 = SD.open("pokemon.txt", FILE_WRITE); // Abre o crea el archivo "pokemon.txt" en modo de escritura
if (dataFile1) {
// Escribir un registro en el formato deseado
dataFile1.print(id);
dataFile1.print(" ");
dataFile1.print(nombre);
dataFile1.print(" ");
dataFile1.print(tipo);
dataFile1.print(" ");
dataFile1.print(movimientos1);
dataFile1.print(" ");
dataFile1.print(movimientos2);
dataFile1.print(" ");
dataFile1.print(movimientos3);
dataFile1.print(" ");
dataFile1.print(movimientos4);
dataFile1.print(" ");
dataFile1.print(vida);
dataFile1.print(" ");
dataFile1.print(velocidad);
dataFile1.print(" ");
dataFile1.print(ataque);
dataFile1.print(" ");
dataFile1.println(defensa);
}
dataFile1.close();
}
void writeMovimientos (String nombre, short tipo, short efecto, int valor, float precision) {
dataFile1 = SD.open("movs.txt", FILE_WRITE); // Abre o crea el archivo "pokemon.txt" en modo de escritura
if (dataFile1) {
// Escribir un registro en el formato deseado
dataFile1.print(nombre);
dataFile1.print(" ");
dataFile1.print(tipo);
dataFile1.print(" ");
dataFile1.print(efecto);
dataFile1.print(" ");
dataFile1.print(valor);
dataFile1.print(" ");
dataFile1.println(precision);
}
dataFile1.close();
}
void bmpdraw(File f, int x, int y){ //dibujar la imagen de fondo
bmpFile.seek(__Gnbmp_image_offset);
uint32_t time = millis();
uint8_t sdbuffer[BUFFPIXEL_X3]; // 3 * pixels to buffer
for (int i=0; i< __Gnbmp_height; i++) {
for(int j=0; j<(240/BUFFPIXEL); j++) {
bmpFile.read(sdbuffer, BUFFPIXEL_X3);
uint8_t buffidx = 0;
int offset_x = j*BUFFPIXEL;
unsigned int __color[BUFFPIXEL];
for(int k=0; k<BUFFPIXEL; k++) {
__color[k] = sdbuffer[buffidx+2]>>3; // read
__color[k] = __color[k]<<6 | (sdbuffer[buffidx+1]>>2); // green
__color[k] = __color[k]<<5 | (sdbuffer[buffidx+0]>>3); // blue
buffidx += 3;
}
for (int m = 0; m < BUFFPIXEL; m ++) {
tft.drawPixel(m+offset_x, i,__color[m]);
}
}
}
Serial.print(millis() - time, DEC);
Serial.println(" ms");
}
boolean bmpReadHeader(File f){ //leer el archivo bmp de la imagen
// read header
uint32_t tmp;
uint8_t bmpDepth;
if (read16(f) != 0x4D42) {
// magic bytes missing
return false;
}
// read file size
tmp = read32(f);
Serial.print("size 0x");
Serial.println(tmp, HEX);
// read and ignore creator bytes
read32(f);
__Gnbmp_image_offset = read32(f);
Serial.print("offset ");
Serial.println(__Gnbmp_image_offset, DEC);
// read DIB header
tmp = read32(f);
Serial.print("header size ");
Serial.println(tmp, DEC);
int bmp_width = read32(f);
int bmp_height = read32(f);
if(bmp_width != __Gnbmp_width || bmp_height != __Gnbmp_height) { // if image is not 320x240, return false
return false;
}
if (read16(f) != 1)
return false;
bmpDepth = read16(f);
Serial.print("bitdepth ");
Serial.println(bmpDepth, DEC);
if (read32(f) != 0) {
// compression not supported!
return false;
}
Serial.print("compression ");
Serial.println(tmp, DEC);
return true;
}
// read data from the SD card file and convert them to big endian
uint16_t read16(File f){
uint16_t d;
uint8_t b;
b = f.read();
d = f.read();
d <<= 8;
d |= b;
return d;
}
uint32_t read32(File f){
uint32_t d;
uint16_t b;
b = read16(f);
d = read16(f);
d <<= 16;
d |= b;
return d;
}
///////////////////////////////////////////////////
///////////// FUNCIONES AUXILIARES ////////////////
///////////////////////////////////////////////////
//funciones para leer la SD y sacar los atributos
struct movimiento readMovimiento(struct movimiento mov, String nombre) {
dataFile2 = SD.open("movs.txt", FILE_READ);
int en_proceso = 1;
String token;
if(dataFile2){
while (dataFile2.available() && en_proceso==1) {
while(en_proceso == 1){
token = dataFile2.readStringUntil(' ');
if ((token==nombre)||(token=="")){
en_proceso = 0;
}else{
dataFile2.readStringUntil('\n');
}
}
if (token == nombre){
// if(dataFile2.readStringUntil(' ') == nombre) {
//lee cada campo de la linea, separados por un espacio
mov.nombre = nombre;
mov.tipo = dataFile2.readStringUntil(' ').toInt();
mov.efecto = dataFile2.readStringUntil(' ').toInt();
mov.valorEfecto = dataFile2.readStringUntil(' ').toInt();
mov.precision = dataFile2.readStringUntil(' ').toFloat();
//}
// Imprimir los valores (prueba)
Serial.print(mov.nombre);
Serial.print(" ");
Serial.print(mov.tipo);
Serial.print(" ");
Serial.print(mov.efecto);
Serial.print(" ");
Serial.print(mov.valorEfecto);
Serial.print(" ");
Serial.println(mov.precision);
}
}
dataFile2.close();
}
return mov;
}
// void readPokemon(struct pokemon p, String id) {
struct pokemon readPokemon(struct pokemon p, String id) {
Serial.print("Buscando ID ");
Serial.println(id);
dataFile1 = SD.open("pokemon.txt", FILE_READ);
int en_proceso = 1;
String token;
if(dataFile1){
while (dataFile1.available() && en_proceso==1) {
while(en_proceso == 1){
token = dataFile1.readStringUntil(' ');
if ((token==id)||(token=="")){
en_proceso = 0;
}else{
dataFile1.readStringUntil('\n');
}
}
if (token == id){
Serial.println("ID encontrado");
// if(dataFile1.readStringUntil(' ') == id) {
//lee cada campo de la linea, separados por un espacio
p.id = id;
p.nombre = dataFile1.readStringUntil(' ');
Serial.println("Leyendo nombre");
Serial.println(p.nombre);
//p.tipo = dataFile1.readStringUntil('').toInt();
p.tipo = dataFile1.readStringUntil(' ').toInt();
p.movimientos[0].nombre = dataFile1.readStringUntil(' ');
Serial.println("Leyendo movimientos");
p.movimientos[0] = readMovimiento(p.movimientos[0], p.movimientos[0].nombre);
p.movimientos[1].nombre = dataFile1.readStringUntil(' ');
p.movimientos[1] = readMovimiento(p.movimientos[1], p.movimientos[1].nombre);
p.movimientos[2].nombre = dataFile1.readStringUntil(' ');
p.movimientos[2] = readMovimiento(p.movimientos[2], p.movimientos[2].nombre);
p.movimientos[3].nombre = dataFile1.readStringUntil(' ');
p.movimientos[3] = readMovimiento(p.movimientos[3], p.movimientos[3].nombre);
p.vida = dataFile1.readStringUntil(' ').toFloat();
p.velocidad = dataFile1.readStringUntil(' ').toInt();
p.ataque = dataFile1.readStringUntil(' ').toInt();
p.defensa = dataFile1.readStringUntil(' ').toInt();
// Imprimir los valores (prueba)
Serial.print(p.id);
Serial.print(" ");
Serial.print(p.nombre);
Serial.print(" ");
Serial.print(p.tipo);
Serial.print(" ");
Serial.print(p.movimientos[0].nombre);
Serial.print(" ");
Serial.print(p.movimientos[1].nombre);
Serial.print(" ");
Serial.print(p.movimientos[2].nombre);
Serial.print(" ");
Serial.print(p.movimientos[3].nombre);
Serial.print(" ");
Serial.print(p.vida);
Serial.print(" ");
Serial.print(p.velocidad);
Serial.print(" ");
Serial.print(p.ataque);
Serial.print(" ");
Serial.println(p.defensa);
}
}
dataFile1.close();
}
return p;
}
void DrawBotones(){ //imprime los botones para el manejo de los pokes durante el combate
char buttonLabel1[6];
char buttonLabel2[6];
char buttonLabel3[6];
char buttonLabel4[6];
if(turno_p1){ //dependiendo de quien sea el turno imprime unos movimientos u otros
for(int i=0; i<5; i++){
buttonLabel1[i] = p1.movimientos[0].nombre[i];
buttonLabel2[i] = p1.movimientos[1].nombre[i];
buttonLabel3[i] = p1.movimientos[2].nombre[i];
buttonLabel4[i] = p1.movimientos[3].nombre[i];
}
//mov1= p1.movimientos[0].nombre[0];
//mov2= p1.movimientos[1].nombre[0];
//mov3= p1.movimientos[2].nombre[0];
//mov4= p1.movimientos[3].nombre[0];
}
else{
for(int i=0; i<5; i++){
buttonLabel1[i] = p2.movimientos[0].nombre[i];
buttonLabel2[i] = p2.movimientos[1].nombre[i];
buttonLabel3[i] = p2.movimientos[2].nombre[i];
buttonLabel4[i] = p2.movimientos[3].nombre[i];
}
}
buttonLabel1[5] = '\0';
buttonLabel2[5] = '\0';
buttonLabel3[5] = '\0';
buttonLabel4[5] = '\0';
delay(1000);
button[0].initButton(&tft, 60, 205, 80, 30, BLACK, WHITE, BLACK, buttonLabel1, 1);
button[0].drawButton();
button[1].initButton(&tft, 60, 265, 80, 30, BLACK, WHITE, BLACK, buttonLabel2, 1);
button[1].drawButton();
button[2].initButton(&tft, 180, 205, 80, 30, BLACK, WHITE, BLACK, buttonLabel3, 1);
button[2].drawButton();
button[3].initButton(&tft, 180, 265, 80, 30, BLACK, WHITE, BLACK, buttonLabel4, 1);
button[3].drawButton();
}
//FUNCIONES DE GAMEPLAY
void leerTarjeta(struct pokemon *p){
Serial.println(" Lectura de tarjeta");
int lectura_ok = 0;
while (lectura_ok == 0){
//leer una tarjeta NFC
if (mfrc522.PICC_IsNewCardPresent() && mfrc522.PICC_ReadCardSerial()) {
String idLeido = "";
int tam = mfrc522.uid.size;
if (tam > 0){
for (byte i = 0; i < tam; i++) {
idLeido += String(mfrc522.uid.uidByte[i] < 0x10 ? "0" : "") + String(mfrc522.uid.uidByte[i], HEX);
}
idLeido.toUpperCase();
p->id = idLeido; // Asigna el ID leído al pokemon
lectura_ok = 1;
}else{
Serial.print("Intentalo de nuevo");
}
}
}
// Aquí puedes añadir más lógica para asignar otros valores al pokemon si es necesario
mfrc522.PICC_HaltA(); // Halt PICC
}
void leerTarjetaOld(struct pokemon *p){
Serial.println(" Entrando en leer tarjeta");
//leer una tarjeta NFC
if (!mfrc522.PICC_IsNewCardPresent() || !mfrc522.PICC_ReadCardSerial()) {
return;
}
String idLeido = "";
for (byte i = 0; i < mfrc522.uid.size; i++) {
idLeido += String(mfrc522.uid.uidByte[i] < 0x10 ? "0" : "") + String(mfrc522.uid.uidByte[i], HEX);
}
idLeido.toUpperCase();
p->id = idLeido; // Asigna el ID leído al pokemon
// Aquí puedes añadir más lógica para asignar otros valores al pokemon si es necesario
mfrc522.PICC_HaltA(); // Halt PICC
}
int devolverEfectividadTipo(int tipo1, int tipo2) { //IN GAME calcular la efectividad del movimiento en funcion del tipo del pokemon
if ((tipo1 == 0 || tipo2 == 0)||tipo1==tipo2) {
//si alguno es de tipo normal o son iguales se devuelve 1
return 1;
}
//hay que comprobar cual es cada una
if(tipo1==1){
if (tipo2 == 2)
return 2;
else
return 0.5;
}
if (tipo1 == 2) {
if (tipo2 == 3)
return 2;
else
return 0.5;
}
if (tipo1 == 3) {
if (tipo2 == 1)
return 2;
else
return 0.5;
}
//si no ha saltado ninguna se devuelve -1 (ha habido algun error)
return -1;
}
int calculoAtaque(struct pokemon atacante, struct movimiento mov, int tipoAtacado){ //pokemon que ataca para conocer sus habilidades (velocidad, nivel,...), el movimiento que usa y el pokemon atacado para quitarle mas o menos vida dependiendo de su tipo
float aleatorio;
aleatorio = random(0, 1);
Serial.print("--------- Aleatorio ");
Serial.println(aleatorio);
Serial.print("--------- Efecto ");
Serial.println(mov.efecto);
Serial.print("--------- Precisión ");
Serial.println(mov.precision);
Serial.print("--------- Valor efecto ");
Serial.println(mov.valorEfecto);
if(mov.efecto != 1) { // efectos: 0 daño, 1 defensa
if(mov.precision >= aleatorio)
return (mov.valorEfecto);
else
return 0;
}
else{
atacante.defensaActiva = true; //si efecto = 1 es un movimiento de defensa y activa la defensa
return 0; //se defiende, no hace daño
}
}
// void ataque(struct pokemon atacante, struct movimiento mov, struct pokemon atacado) { //realizar el movimiento del pokemon
// int valorAtaque = calculoAtaque(atacante, mov, atacado.tipo);
// int potenciador = devolverEfectividadTipo(atacante.tipo, atacado.tipo);
// Serial.print("Daño del ataque = ");
// Serial.println(valorAtaque);
// if(mov.efecto == 0){
// if (atacado.defensaActiva){
// if (atacado.defensa <= valorAtaque)
// atacado.vida -= ((valorAtaque * potenciador + atacante.ataque) - atacado.defensa); //si la defensa es menor o igual que el daño, se causa el daño restando la defensa al valor del ataque
// else //si la defensa es mayor no se causa daño
// atacado.vida -= 0;
// atacado.defensaActiva = false; //se desactiva este campo ya que no hay mas defensa
// }
// }
// else if(mov.efecto == 1) //defensa
// atacante.defensaActiva = true;
// else if (mov.efecto == 2) //ataque
// atacante.ataque += mov.valorEfecto;
// else if (mov.efecto == 2) //salud
// atacante.vida += mov.valorEfecto;
// else if (mov.efecto == 4) //resurreccion
// atacante.vida += mov.valorEfecto;
// }
void funcionBotones(){ //control de la funcionalidad de los botones
digitalWrite(13, HIGH);
TSPoint p = ts.getPoint();
digitalWrite(13, LOW);
pinMode(XM, OUTPUT);
pinMode(YP, OUTPUT);
Serial.println("---- Paso 1");
Serial.println(p.z);
if (p.z > MINPRESSURE && p.z < MAXPRESSURE) {
// scale from 0->1023 to tft.width
Serial.println("Paso 2");
p.x = map(p.x, TS_MINX, TS_MAXX, tft.width(), 0);
p.y = (tft.height()-map(p.y, TS_MINY, TS_MAXY, tft.height(), 0));
}
Serial.println("primer bucle");
// go thru all the button, checking if they were pressed
for (uint8_t b=0; b<4; b++) {
if (button[b].contains(p.x, p.y)) {
Serial.print("Pressing: "); Serial.println(b);
button[b].press(true); // tell the button it is pressed
} else {
button[b].press(false); // tell the button it is NOT pressed
}
}
// now we can ask the button if their state has changed
for (uint8_t b=0; b<4; b++) {
if (button[b].justReleased()) {
Serial.print("Released: "); Serial.println(b);
button[b].drawButton(); // draw normal
}
if (button[b].justPressed()) {
jugada=true;
Serial.println("Pressed");
button[b].drawButton(true); // draw invert
if(turno_p1) // selecciona el movimiento del pokemon a hacer y activa la jugada
mov = p1.movimientos[b];
}
else{
mov = p2.movimientos[b];
}
}
}
char* elegirFondo(char* filename){ //seleccionar la imagen de fondo en funcion de los pokes escaneados
//char filename[11];
for(int i=0; i<=2; i++) //3 primeras letras de un poke
{filename[i] = p1.nombre[i];
Serial.println(p1.nombre[i]);
}
for(int i=3; i<=5; i++) //3 primeras letras del otro poke
{filename[i] = p2.nombre[i-3];}
filename[6] = '.';
filename[7] = 'b';
filename[8] = 'm';
filename[9] = 'p';
filename[10] = '\0';
Serial.print("Filename: ");
Serial.println(filename);
return filename;
}
Elegoo_GFX_Button buttons[15];
/* create 15 buttons, in classic candybar phone style */
char buttonlabels[15][5] = {"Send", "Clr", "End", "1", "2", "3", "4", "5", "6", "7", "8", "9", "*", "0", "#" };
uint16_t buttoncolors[15] = {ILI9341_DARKGREEN, ILI9341_DARKGREY, ILI9341_RED,
ILI9341_BLUE, ILI9341_BLUE, ILI9341_BLUE,
ILI9341_BLUE, ILI9341_BLUE, ILI9341_BLUE,
ILI9341_BLUE, ILI9341_BLUE, ILI9341_BLUE,
ILI9341_ORANGE, ILI9341_BLUE, ILI9341_ORANGE};
void setup(void) {
Serial.begin(9600); // Initialize serial communications with the PC
Serial.println("======================");
Serial.println("======================");
tft.reset();
tft.begin(0x9341);
#define MEGA2560_SD_CS_PIN 53
//Init SD_Card
pinMode(MEGA2560_SD_CS_PIN, OUTPUT);
if (!SD.begin(10)) {
Serial.println("initialization failed!");
tft.setCursor(0, 0);
tft.setTextColor(WHITE);
tft.setTextSize(1);
tft.println("SD Card Init fail.");
}else
Serial.println("initialization done.");
SD.remove("pokemon.txt");
SD.remove("movs.txt");
delay(1000);
//para pokemon
dataFile1 = SD.open("pokemon.txt", FILE_WRITE); // Abre o crea el archivo "pokemon.txt" en modo de escritura
if (dataFile1) {
//codigo, nombre, tipo, movimientos, vida, velocidad, ataque, defensa
writePokemon("537554A6", "Ratatta", 0, "Golpe", "Placaje", "Furia", "GranGolpe", 100, 72, 56, 35);
writePokemon("A343D2A6", "Squirtle", 1, "PistolaAgua", "Tsunami", "FuerteEspiritu", "Defensa", 50, 43, 48, 65);
writePokemon("63B176A6", "Charmander", 2, "Lanzallamas", "Abrasar", "Furia", "Defensa", 100, 65, 52, 43);
writePokemon("932977B7", "Bulbasur", 3, "HojaAfilada", "FlorAfilada", "Resurreccion", "Defensa", 85, 45, 49, 49);
dataFile1.close(); // Cierra el archivo después de escribir los datos
Serial.println("Datos escritos en la tarjeta SD");
}
else {
Serial.println("Error al abrir el archivo");
}
//para movimientos
dataFile1 = SD.open("movs.txt", FILE_WRITE); // Abre o crea el archivo "movimientos.txt" en modo de escritura
if (dataFile1) {
// String nombre, short tipo (normal, agua, fuego, planta), short efecto (daño, defensa, subir ataque, subir salud, resurreccion), int valor, float precision
//de daño
writeMovimientos("Golpe", 0, 0, 80, 0.7); //normal
writeMovimientos("Placaje", 0, 0, 60, 1.0);
writeMovimientos("GranGolpe", 0, 0, 90, 0.4);
writeMovimientos("PistolaAgua", 1, 0, 80, 0.7); //agua
writeMovimientos("Tsunami", 1, 0, 80, 0.6);
writeMovimientos("Lanzallamas", 2, 0, 90, 0.5); //fuego
writeMovimientos("Abrasar", 2, 0, 70, 0.7);
writeMovimientos("HojaAfilada", 3, 0, 85, 0.7); //planta
writeMovimientos("FlorAfilada", 3, 0, 65, 0.8);
//de efecto
writeMovimientos("Defensa", 0, 1, 1, 0.75); //Defensa: 75 de precisión de defenderte del ataque del rival
writeMovimientos("Furia", 0, 2, 25, 1); //Furia: aumenta el ataque
writeMovimientos("FuerteEspiritu", 0, 3, 50, 1); //Fuerte espíritu: aumenta la vida actual
writeMovimientos("Resurreccion", 0, 4, 145, 0.3); //Resurrección: 30 de precisión de que te cure toda la vida
dataFile1.close(); // Cierra el archivo después de escribir los datos
Serial.println("Datos escritos en la tarjeta SD");
}
else {
Serial.println("Error al abrir el archivo");
}
//////////// Pantalla de inicio /////////////////////////////
tft.setRotation(2); //Vertical.
tft.fillScreen(BLACK); //Fondo negro
tft.setTextColor(WHITE); //Texto blanco
tft.setTextSize(1);
tft.setCursor(30,60);
tft.print("Listo para un combate?");
tft.setTextSize(3);
tft.setTextColor(CYAN);
tft.setCursor(20,150);
tft.print("ESCANEA TU");
tft.setCursor(40,200);
tft.print("POKEMON");
pinMode(SS_PIN, OUTPUT);
while (!Serial); // Do nothing if no serial port is opened (added for Arduinos based on ATMEGA32U4)
SPI.begin(); // Init SPI bus
mfrc522.PCD_Init(); // Init MFRC522
Serial.println(F("Acerca el PICC para leer el UID..."));
delay(3000);
}
void loop(void) {
// ///////////////////////////////////////////////////////
// INSERTANDO LOOP
delay(10); // OJO! delay reducido!!
if(iniciaCombate){
Serial.println(" Leyendo tarjeta 1");
delay(1000); // OJO! delay añadidoido!!
leerTarjeta(&p1); //escanear pokemon 1
tft.setRotation(2); //esperando que escanee al otro
tft.fillScreen(MAGENTA);
tft.setTextSize(3);
tft.setTextColor(BLACK);
tft.setCursor(15,100);
tft.print("ESCANEA OTRO");
tft.setCursor(40,160);
tft.print("POKEMON");
Serial.println(" Leyendo tarjeta 2");
delay(4000); //espera para no leer dos veces el mismo
leerTarjeta(&p2); //escanear pokemon 2
tft.fillScreen(BLACK);
// imprimir datos de poke
if (p1.id != "" && p2.id != "") {
Serial.println("ID del Pokemon: " + p1.id);
Serial.println("ID del Pokemon: " + p2.id);
// Imprimir más información del pokemon si es necesario
}
// Leer datos de los pokemons
p1 = readPokemon(p1, p1.id);
p2 = readPokemon(p2, p2.id);
tft.setRotation(0);
// Serial.println(elegirfFondo());
char filename[11];
elegirFondo(filename);
bmpFile = SD.open(filename);
if (! bmpFile) {
Serial.println("didnt find image");
tft.setTextColor(WHITE); tft.setTextSize(1);
tft.println("didnt find BMPimage");
while (1);
}
if(! bmpReadHeader(bmpFile)) {
Serial.println("bad bmp");
tft.setTextColor(WHITE); tft.setTextSize(1);
tft.println("bad bmp");
return;
}
bmpdraw(bmpFile, 0, 0);
bmpFile.close();
iniciaCombate = false;
Serial.println("Comparar velocidad");
if(p1.velocidad >= p2.velocidad)
turno_p1 = true;
else
turno_p1 = false;
tft.setRotation(2);
DrawBotones();
tft.fillRect(40, 30, 75, 20, WHITE);
tft.fillRect(160, 30, 75, 20, WHITE);
}
// ///////////////////////////////////////////////////////
// FUNCION BOTONES
digitalWrite(13, HIGH);
TSPoint p = ts.getPoint();
digitalWrite(13, LOW);
pinMode(XM, OUTPUT);
pinMode(YP, OUTPUT);
// Serial.print("---- Presión ");
// Serial.println(p.z);
if (p.z > MINPRESSURE && p.z < MAXPRESSURE) {
// scale from 0->1023 to tft.width
Serial.print("---- Presión detectada ");
Serial.println(p.z);
p.x = map(p.x, TS_MINX, TS_MAXX, tft.width(), 0);
p.y = (tft.height()-map(p.y, TS_MINY, TS_MAXY, tft.height(), 0));
// Serial.print("---- Coordenadas ");
Serial.print(p.x);
Serial.print(" , ");
Serial.println(p.y);
}
// Serial.println("primer bucle");
// // go thru all the button, checking if they were pressed
for (uint8_t b=0; b<4; b++) {
if (button[b].contains(p.x, p.y)) {
Serial.print("---- Pulsación capturada boton ");
Serial.println(b);
button[b].press(true); // tell the button it is pressed
} else {
button[b].press(false); // tell the button it is NOT pressed
}
}
// now we can ask the button if their state has changed
for (uint8_t b=0; b<4; b++) {
if (button[b].justReleased()) {
// Serial.print("Released: "); Serial.println(b);
button[b].drawButton(); // draw normal
}
if (button[b].justPressed()) {
jugada=true;
Serial.println("Pressed. ");
button[b].drawButton(true); // draw invert
if(turno_p1) // selecciona el movimiento del pokemon a hacer y activa la jugada
mov = p1.movimientos[b];
else
mov = p2.movimientos[b];
delay(100); // UI debouncing
}
tft.setTextColor(BLACK); //imprimir la vida de cada Poke
tft.setTextSize(2);
tft.setCursor(40,30);
tft.print(p1.vida);
tft.setCursor(160,30);
tft.print(p2.vida);
///////////////// CÓDIGO DEL COMBATE //////////////////
if((p1.vida < 0) || (p2.vida < 0)){ //si un poke muere mostrar pantalla final y salir
tft.fillScreen(MAGENTA);
tft.setTextColor(WHITE);
tft.setTextSize(1);
tft.setCursor(40,60);
tft.print("Fin del combate");
tft.setTextSize(3);
tft.setCursor(20,180);
tft.print("Ganador:");
tft.setCursor(20,220);
if(p1.vida <= 0)
tft.print(p2.nombre);
else
tft.print(p1.nombre);
delay(1500);
while (true) {}
}
if(jugada){ //si se pulsa un botón
Serial.println("------- Atencion, jugada");
Serial.print("IMPRIMIENDO VIDA J1 ");
Serial.print(p1.nombre);
Serial.println(p1.vida);
Serial.print("IMPRIMIENDO VIDA J2 ");
Serial.print(p2.nombre);
Serial.println(p2.vida);
if(turno_p1){
Serial.println("Turno de jugador 1");
//////////////////////////
////////////////////////// ataque(p1, mov, p2);
////////////// ataque(atacante, mov, atacado)
int valorAtaque = calculoAtaque(p1, mov, p2.tipo);
int potenciador = devolverEfectividadTipo(p1.tipo, p2.tipo);
Serial.print("Daño del ataque = ");
Serial.println(valorAtaque);
Serial.print(" Efecto ");
Serial.println(mov.efecto);
if(mov.efecto == 0){
if (p2.defensaActiva){
if (p2.defensa <= valorAtaque)
p2.vida -= ((valorAtaque * potenciador + p1.ataque) - p2.defensa + 35); //si la defensa es menor o igual que el daño, se causa el daño restando la defensa al valor del ataque
else //si la defensa es mayor no se causa daño
p2.vida -= 25;
p2.defensaActiva = false; //se desactiva este campo ya que no hay mas defensa
}else{
p2.vida -= 10;
}
}
else if(mov.efecto == 1) //defensa
p1.defensaActiva = true;
else if (mov.efecto == 2) //ataque
p1.ataque += mov.valorEfecto;
else if (mov.efecto == 3) //salud
p1.vida += mov.valorEfecto;
else if (mov.efecto == 4) //resurreccion
p1.vida += mov.valorEfecto;
Serial.println("El jugador 1 ha hecho su movimiento");
turno_p1 = !turno_p1;
}
else {
Serial.println("Turno de jugador 2");
//////////////////////////
////////////////////////// ataque(p2, mov, p1);
////////////// ataque(atacante, mov, atacado)
int valorAtaque = calculoAtaque(p2, mov, p1.tipo);
int potenciador = devolverEfectividadTipo(p2.tipo, p1.tipo);
Serial.print("Daño del ataque = ");
Serial.println(valorAtaque);
Serial.print(" Efecto ");
Serial.println(mov.efecto);
if(mov.efecto == 0){
if (p1.defensaActiva){
if (p1.defensa <= valorAtaque){
Serial.print("-- Potenciador");
p1.vida -= ((valorAtaque * potenciador + p2.ataque) - p1.defensa + 35); //si la defensa es menor o igual que el daño, se causa el daño restando la defensa al valor del ataque
}else{ //si la defensa es mayor no se causa daño
Serial.print("-- No potenciador");
p1.vida -= 25;
}
p1.defensaActiva = false; //se desactiva este campo ya que no hay mas defensa
}else{
p1.vida -= 10;
}
}
else if(mov.efecto == 1) //defensa
p2.defensaActiva = true;
else if (mov.efecto == 2) //ataque
p2.ataque += mov.valorEfecto;
else if (mov.efecto == 3) //salud
p2.vida += mov.valorEfecto;
else if (mov.efecto == 4) //resurreccion
p2.vida += mov.valorEfecto;
Serial.println("El jugador 2 ha hecho su movimiento");
turno_p1 = !turno_p1;
}
Serial.println(" Ataque completado");
Serial.print(" Vida p1 ");
Serial.println(p1.vida);
Serial.print(" Vida p2 ");
Serial.println(p2.vida);
tft.fillRect(40, 30, 75, 20, WHITE);
tft.fillRect(160, 30, 75, 20, WHITE);
jugada = 0;
}
delay(10);
}
}