From 80e3bb4338ec5be5bb44d0dfc694f3b897ddb206 Mon Sep 17 00:00:00 2001 From: Darryn Cull Date: Fri, 1 Sep 2023 21:44:58 +0200 Subject: [PATCH] Added stats selection --- README_catalog.md | 38 ------ README_es.md | 249 ------------------------------------ changelog.md | 5 - docs/images/implemented.svg | 15 --- pokemon_app.cpp | 10 ++ pokemon_app.h | 5 + views/select_level.cpp | 2 +- views/select_move1.cpp | 3 +- views/select_move2.cpp | 1 + views/select_move3.cpp | 1 + views/select_move4.cpp | 1 + views/select_stats.cpp | 195 ++++++++++++++++++++++++++++ views/select_stats.hpp | 25 ++++ views/trade.cpp | 52 +++++--- 14 files changed, 276 insertions(+), 326 deletions(-) delete mode 100644 README_catalog.md delete mode 100644 README_es.md delete mode 100644 changelog.md delete mode 100644 docs/images/implemented.svg create mode 100644 views/select_stats.cpp create mode 100644 views/select_stats.hpp diff --git a/README_catalog.md b/README_catalog.md deleted file mode 100644 index a8a607be430..00000000000 --- a/README_catalog.md +++ /dev/null @@ -1,38 +0,0 @@ -# 🐬 Flipper Zero - Pokemon Trading in Game Boy - -## Introduction - -This is a Pokemon exchange application from Flipper Zero to Game Boy (Generación I). Flipper Zero emulates a "Slave" Game Boy connected to a Game Link Cable to be able to exchange any Pokemon from the First Generation (Red, Blue, Yellow) to a real Game Boy. - -It is a Proof of Concept (POC) for using views, GPIO, and FURI (Flipper Universal Registry Implementation). - - -## Connection: Flipper Zero GPIO - Game Boy - -The pins should be connected as follows: - -| Cable Game Link (Socket) | Flipper Zero GPIO | -| ------------------------ | ----------------- | -| 6 (GND) | 8 (GND) | -| 5 (CLK) | 6 (B2) | -| 3 (SI) | 7 (C3) | -| 2 (SO) | 5 (B3) | - -## How does it work? - -The method used to communicate 2 Game Boys is based on the SPI protocol, which is a very simple serial communication protocol in which a master device communicates with one or more slave devices. The protocol is bidirectional and synchronous, and uses three basic signals: - -- A clock signal (CLK). -- An output signal (Serial Out or SO). -- An input signal (Serial In or SI). - -In the Game Boy, games store data in an internal shift register that is used to send and receive information. The SPI protocol used by the Game Boy uses the clock signal to indicate when data is being transferred. - -The Game Boy link protocol is synchronous and requires the slave device to respond at the same rate as the master device. The master device supplies an 8KHz clock (data transfer rate of 1KB/s). The time window for responding is only **~120μs**. However, the slave device has no restrictions and can respond when it receives data. The clock can vary and there is no lower limit. - - -## Tested In -- Game Boy Color (GBC) -- Game Boy Advance (GBA) - - diff --git a/README_es.md b/README_es.md deleted file mode 100644 index 7107f093010..00000000000 --- a/README_es.md +++ /dev/null @@ -1,249 +0,0 @@ -# 🐬 Flipper Zero - Pokemon Trading in Game Boy - -

- - Flipper Zero - Pokemon Trading Game Boy -
-

-
- -**FW Official** | **FW Unleashed** | **FW RogueMaster** -:- | :- | :- -[![FlipC.org](https://flipc.org/EstebanFuentealba/Flipper-Zero-Game-Boy-Pokemon-Trading/badge?branch=main)](https://flipc.org/EstebanFuentealba/Flipper-Zero-Game-Boy-Pokemon-Trading?branch=main)|[![FlipC.org](https://flipc.org/EstebanFuentealba/Flipper-Zero-Game-Boy-Pokemon-Trading/badge?branch=main&firmware=unleashed)](https://flipc.org/EstebanFuentealba/Flipper-Zero-Game-Boy-Pokemon-Trading?branch=main&firmware=unleashed)|[![FlipC.org](https://flipc.org/EstebanFuentealba/Flipper-Zero-Game-Boy-Pokemon-Trading/badge?branch=main&firmware=roguemaster)](https://flipc.org/EstebanFuentealba/Flipper-Zero-Game-Boy-Pokemon-Trading?branch=main&firmware=roguemaster) -
- -## Introducción - -Esta es una aplicación de intercambio de Pokemon's desde de Flipper Zero a Game Boy [(Generación I)](https://bulbapedia.bulbagarden.net/wiki/Generation_I). Flipper Zero emula un Game Boy "Esclavo" conectado a **Cable Game Link** para poder intercambiar cualquier Pokemon de la Primera Generación (Red, Blue, Yellow) a un Game Boy Real. - -Es una Prueba de concepto (POC) para utilizar vistas, GPIO y FURI (Flipper Universal Registry Implementation). - -## Instrucciones de instalación - -Este proyecto está destinado a ser superpuesto encima de un repositorio de firmware existente, en mi caso la versión **Release 0.79.1**. - -- Clona el [Repositorio del firmware de Flipper Zero](https://github.com/flipperdevices/flipperzero-firmware). Consulta este [tutorial](https://github.com/jamisonderek/flipper-zero-tutorials/tree/main/firmware/updating/README.md) para actualizar el firmware. -- Copia la [carpeta "pokemon"](..) en la carpeta `/applications_user/pokemon` del firmware que clonaste. -- Corre el comando `fbt launch_app` para correr en tu Flipper Zero. - -```bash -./fbt launch_app APPSRC=pokemon -``` - -- NOTA: Si sólo quieres generar el archivo `fap` debes correr el siguiente comando. - -```bash -./fbt fap_pokemon -``` - -y usa [**qFlipper**](https://flipperzero.one/update) para copiar el archivo **pokemon.fap** generado a la carpeta `SD Card/apps/Game Boy`. - -

-
-

- -## Instrucciones de Uso - -Estas instrucciones asumen que está comenzando en el escritorio de Flipper Zero. De lo contrario, presione el botón Atrás hasta que esté en el escritorio. - -- Presione el botón `OK` en el flipper para abrir el menú principal. -- Elija `Aplicaciones` en el menú. -- Elija `Game Boy` en el submenú. -- Elija `Pokemon Trading` -- El Flipper Zero debe mostrar la selección de Pokemon que se desea intercambiar y por defecto parece bulbasaur. - -

-
- -
-

- -- Pulse los botones `IZQUIERDA`/`DERECHA` para paginar de 1 en 1 la selección de Pokemon. -- Pulse los botones `ARRIBA`/`ABAJO` para paginar de 10 en 10 la selección de Pokemon. -- Pulse el botón `OK` para seleccionar el Pokemon a Intercambiar. -

-
-
-

-- En el Flipper Zero se muestra la vista para conectar el Game Boy. -

-
-
-

-- En tu Game Boy debes conectar el **Cable Game Link** al Game Boy, en el juego dirigirte a un **Centro Pokémon** que tengas más cercano. -

-
-
-

-- Habla con la chica que está en el mostrador de la derecha. La chica nos dirá que para poder jugar antes tendremos que salvar el juego, le contestaremos que _SI_ pulsando el botón _A_. -

-
-
-

-- El Flipper Zero nos mostrará que estámos conectados. -

-
-
-

-- En el Game Boy nos preguntará que opción queremos y Seleccionamos **CENT. CAMBIO**. -

-
-
-

-- Entrarás a la sala de Intercambio donde debes presionar el botón A del Game Boy en el lado de tu mesa. -

-
-
-

-- Flipper Zero quedará en una pantalla de espera con el Pokemon que seleccionaste. -

-
-
-

-- Se te mostrarán tus Pokemon y el Pokemon que seleccionaste en el Flipper Zero, en este Caso **Mew**. Debes seleccionar el pokemon que quieres intercambiar y presionar **TRATO**. -

-
-
-

-- Debes confirmar el intercambio seleccionado **TRATO**. -

-
-
-

-- Flipper Zero quedará en una pantalla de espera con el Pokemon que seleccionaste. -

-
-
-

-- Finalmente comenzará el intercambio de Pokemon desde **Flipper Zero** al **Game Boy**. -

-
-
-

-- **NOTA**: Si al final del Intercambio se te bloquea el Flipper Zero debes Rebootear presionando la combinación de teclas IZQUIERDA + ATRAS -

-
-
-

- -## ¿Cómo trabaja? - -El método utilizado para comunicar 2 Game Boy se basa en el protocolo SPI, que es un protocolo de comunicación serial muy simple en el que un dispositivo maestro se comunica con uno o más dispositivos esclavos. El protocolo es bidireccional y sincrónico, y utiliza tres señales básicas: - -- Una señal de reloj (CLK). -- Una señal de salida (Serial Out o SO). -- Una señal de entrada (Serial In o SI). - -En el Game Boy, los juegos almacenan los datos en un registro de cambio interno que se utiliza para enviar y recibir información. El protocolo SPI utilizado por el Game Boy utiliza la señal de reloj para indicar cuándo se transfieren los datos. - -El protocolo de enlace de Game Boy es síncrono y requiere que el dispositivo esclavo responda al mismo ritmo que el dispositivo maestro. El dispositivo maestro suministra un reloj de 8KHz (velocidad de transferencia de datos de 1KB/s). La ventana de tiempo para responder es de solo **~120μs**. Sin embargo, el dispositivo esclavo no tiene restricciones y puede responder cuando recibe los datos. El reloj puede variar y no hay un límite inferior. - -

-
-
-

- -_Una transferencia de ejemplo de GB SPI. Aquí, el maestro envía 0xD9 (217) y el esclavo envía 0x45 (69)._ - -
- -Se puede conocer mas al respecto en el siguiente Video [**Analyzing the Different Versions of the Link Cable**](https://youtu.be/h1KKkCfzOws?t=151). - -## Placa para Flipper Zero con Socket PortData EXT Link - -Para la placa del Fipper Zero se utilizó un [PortData EXT Link](https://es.aliexpress.com/item/1005004116983895.html) y una [place de prototipo](https://es.aliexpress.com/item/32478242317.html) de 2x8. - -

-
-
-

- -_PortData EXT Link para Game Boy Color, Game Boy Pocket, GBC, GBP, GBL._ - -

-
-
-

-

-
-
-

-Usé una resistencia de 33kΩ en CLK, pero es opcional, se puede conectar directamente. - -## Conexión: Flipper Zero GPIO - Game Boy - -Se deben conectar los Pines de la siguiente manera - -

-
-
-

- - - - - Connect Flipper Zero GPIO to Game Boy Pins - - -| Cable Game Link (Socket) | Flipper Zero GPIO | -| ------------------------ | ----------------- | -| 6 (GND) | 8 (GND) | -| 5 (CLK) | 6 (B2) | -| 3 (SI) | 7 (C3) | -| 2 (SO) | 5 (B3) | - - -## Conectar a Flipper Zero sin Socket PortData EXT Link - -Pudes cortar un cable directamente sin usar el socket pero debes tener en cuenta que el es un cable cruzado SI-SO. - -

-
-
-

- -*"Cable Game Link" cortado y conectado directamente a los pines de Flipper Zero.* - - -**NOTA**: No guiarse por los colores porque dependiendo del fabricante estos pueden cambiar, con un multímetro medir continuidad e identificar que cable es de que pin - - -## GUI - -Para generar la Interfaz gráfica se utilizó la herramienta [**FUI-Editor**](https://ilin.pt/stuff/fui-editor/). -Además se utilizaron los sprites originales del juego _Pokemon Yellow_ que se encuentran en el repositorio [**Disassembly of Pokemon Yellow**](https://github.com/pret/pokeyellow/tree/master/gfx/pokemon/front). - -De cada imagen se transformó el color `#aaa` a `#fff` para que Flipper Zero la renderizara bien. Para eso se utilizó un **Batch** para [Photopea](https://www.photopea.com/), el editor de imagenes online. - -## Implementado en -- Game Boy Color (GBC) -- Game Boy Advance (GBA) - -## Implementado por -
- -## TODO - -- [ ] Refactorizar el código -- [ ] Al salir de la app el botón `OK` deja de funcionar por lo que hay que reiniciarlo 🤔 -- [ ] Setear a cada pokemon sus características, ataques, niveles por defecto -- [ ] Mejorar animaciones - -## Links - -- [Flipper Zero firmware source code](https://github.com/flipperdevices/flipperzero-firmware) -- Adan Scotney's pokemon [trade protocol specification](http://www.adanscotney.com/2014/01/spoofing-pokemon-trades-with-stellaris.html) and implementation -- Derek Jamison - [Youtube Channel](https://www.youtube.com/@MrDerekJamison) -- Matt Penny - [GBPlay Blog](https://blog.gbplay.io/) -- [Pokémon data structure (Generation I)]() -- [Disassembly of Pokemon Yellow](https://github.com/pret/pokeyellow) -- [Arduino-Spoofing-Gameboy-Pokemon-Trades](https://github.com/EstebanFuentealba/Arduino-Spoofing-Gameboy-Pokemon-Trades) -- [🎮 Gameboy link cable breakout PCB](https://github.com/Palmr/gb-link-cable) - -

-
-
-Desde Talcahuano 🇨🇱 con ❤ -

diff --git a/changelog.md b/changelog.md deleted file mode 100644 index eef7b7f499b..00000000000 --- a/changelog.md +++ /dev/null @@ -1,5 +0,0 @@ -# Changelog - Patch Notes - -## Version 1.2.0 -- **Cleanup data structs:** This refactors the main data blocks for defining pokemon, the icon, their species/hex value, as well as the large trade array in to more human friendly structs. Laying some groundwork to be able to adjust pokemon details pre-trade by @kbembedded . -- **Bug Fixes:** Fix furi crash, Fixes #9 by @kbembedded . diff --git a/docs/images/implemented.svg b/docs/images/implemented.svg deleted file mode 100644 index 79b2b8a9ea8..00000000000 --- a/docs/images/implemented.svg +++ /dev/null @@ -1,15 +0,0 @@ - - - - - - - - - - \ No newline at end of file diff --git a/pokemon_app.cpp b/pokemon_app.cpp index 8eb55aff430..e422d50ec1c 100644 --- a/pokemon_app.cpp +++ b/pokemon_app.cpp @@ -356,6 +356,14 @@ App* pokemon_alloc() { view_dispatcher_add_view( app->view_dispatcher, AppViewSelectLevel, select_level_get_view(app)); + // Start Index first stat + app->current_stats = 0; + // Select Level View + app->select_stats = select_stats_alloc(app); + view_set_previous_callback(select_stats_get_view(app), pokemon_exit_confirm_view); + view_dispatcher_add_view( + app->view_dispatcher, AppViewSelectStats, select_stats_get_view(app)); + // Start Index first move app->current_move = 0; // Select Move View @@ -406,6 +414,8 @@ void free_app(App* app) { select_pokemon_free(app); view_dispatcher_remove_view(app->view_dispatcher, AppViewSelectLevel); select_level_free(app); + view_dispatcher_remove_view(app->view_dispatcher, AppViewSelectStats); + select_stats_free(app); view_dispatcher_remove_view(app->view_dispatcher, AppViewSelectMove1); select_move1_free(app); view_dispatcher_remove_view(app->view_dispatcher, AppViewSelectMove2); diff --git a/pokemon_app.h b/pokemon_app.h index df54abf590e..92524aa30e1 100644 --- a/pokemon_app.h +++ b/pokemon_app.h @@ -12,6 +12,7 @@ #include "views/select_pokemon.hpp" #include "views/select_level.hpp" +#include "views/select_stats.hpp" #include "views/select_move1.hpp" #include "views/select_move2.hpp" #include "views/select_move3.hpp" @@ -55,6 +56,7 @@ struct App { ViewDispatcher* view_dispatcher; SelectPokemon* select_pokemon; SelectLevel* select_level; + SelectStats* select_stats; SelectMove1* select_move1; SelectMove2* select_move2; SelectMove3* select_move3; @@ -64,6 +66,7 @@ struct App { int current_pokemon = 0; int current_level = 3; + int current_stats = 0; int current_move = 0; char pokemon_hex_code = ' '; char move1_hex_code = ' '; @@ -75,6 +78,7 @@ struct App { typedef enum { AppViewSelectPokemon, AppViewSelectLevel, + AppViewSelectStats, AppViewSelectMove1, AppViewSelectMove2, AppViewSelectMove3, @@ -87,6 +91,7 @@ typedef void (*SelectPokemonCallback)(void* context, uint32_t index); typedef struct SelectPokemonModel { int current_pokemon = 0; int current_level = 3; + int current_stats = 0; int current_move = 0; char pokemon_hex_code = ' '; char move1_hex_code = ' '; diff --git a/views/select_level.cpp b/views/select_level.cpp index 19077724b5b..52e817900af 100644 --- a/views/select_level.cpp +++ b/views/select_level.cpp @@ -39,7 +39,7 @@ static bool select_level_input_callback(InputEvent* event, void* context) { }, false); - view_dispatcher_switch_to_view(select_level->app->view_dispatcher, AppViewSelectMove1); + view_dispatcher_switch_to_view(select_level->app->view_dispatcher, AppViewSelectStats); consumed = true; } else if(event->type == InputTypePress && event->key == InputKeyBack) { view_dispatcher_switch_to_view(select_level->app->view_dispatcher, AppViewSelectPokemon); diff --git a/views/select_move1.cpp b/views/select_move1.cpp index 8281ca01209..8a779c01404 100644 --- a/views/select_move1.cpp +++ b/views/select_move1.cpp @@ -41,7 +41,7 @@ static bool select_move1_input_callback(InputEvent* event, void* context) { view_dispatcher_switch_to_view(select_move1->app->view_dispatcher, AppViewSelectMove2); consumed = true; } else if(event->type == InputTypePress && event->key == InputKeyBack) { - view_dispatcher_switch_to_view(select_move1->app->view_dispatcher, AppViewSelectLevel); + view_dispatcher_switch_to_view(select_move1->app->view_dispatcher, AppViewSelectStats); consumed = true; } else if(event->type == InputTypePress && event->key == InputKeyLeft) { with_view_model_cpp( @@ -116,6 +116,7 @@ void select_move1_enter_callback(void* context) { model->current_pokemon = select_move1->app->current_pokemon; model->pokemon_hex_code = select_move1->app->pokemon_hex_code; model->current_level = select_move1->app->current_level; + model->current_stats = select_move1->app->current_stats; model->move1_hex_code = select_move1->app->move1_hex_code; }, true); diff --git a/views/select_move2.cpp b/views/select_move2.cpp index b487eeb757e..390985f773f 100644 --- a/views/select_move2.cpp +++ b/views/select_move2.cpp @@ -116,6 +116,7 @@ void select_move2_enter_callback(void* context) { model->current_pokemon = select_move2->app->current_pokemon; model->pokemon_hex_code = select_move2->app->pokemon_hex_code; model->current_level = select_move2->app->current_level; + model->current_stats = select_move2->app->current_stats; model->move1_hex_code = select_move2->app->move1_hex_code; model->move2_hex_code = select_move2->app->move2_hex_code; }, diff --git a/views/select_move3.cpp b/views/select_move3.cpp index 7fb4cb7342c..c5646c732b4 100644 --- a/views/select_move3.cpp +++ b/views/select_move3.cpp @@ -116,6 +116,7 @@ void select_move3_enter_callback(void* context) { model->current_pokemon = select_move3->app->current_pokemon; model->pokemon_hex_code = select_move3->app->pokemon_hex_code; model->current_level = select_move3->app->current_level; + model->current_stats = select_move3->app->current_stats; model->move1_hex_code = select_move3->app->move1_hex_code; model->move2_hex_code = select_move3->app->move2_hex_code; model->move3_hex_code = select_move3->app->move3_hex_code; diff --git a/views/select_move4.cpp b/views/select_move4.cpp index 62c5429b9c1..a48e7fe61ca 100644 --- a/views/select_move4.cpp +++ b/views/select_move4.cpp @@ -116,6 +116,7 @@ void select_move4_enter_callback(void* context) { model->current_pokemon = select_move4->app->current_pokemon; model->pokemon_hex_code = select_move4->app->pokemon_hex_code; model->current_level = select_move4->app->current_level; + model->current_stats = select_move4->app->current_stats; model->move1_hex_code = select_move4->app->move1_hex_code; model->move2_hex_code = select_move4->app->move2_hex_code; model->move3_hex_code = select_move4->app->move3_hex_code; diff --git a/views/select_stats.cpp b/views/select_stats.cpp new file mode 100644 index 00000000000..dcdb630fe56 --- /dev/null +++ b/views/select_stats.cpp @@ -0,0 +1,195 @@ +#include "../pokemon_app.h" +#include "select_stats.hpp" + +static void select_stats_render_callback(Canvas* canvas, void* context) { + canvas_clear(canvas); + + SelectPokemonModel* model = (SelectPokemonModel*)context; + const uint8_t current_index = model->current_stats; + + canvas_set_font(canvas, FontPrimary); + if(current_index == 0) { + canvas_draw_str_aligned( + canvas, 55, 54 / 2, AlignLeft, AlignTop, "Random IV"); + canvas_draw_str_aligned( + canvas, 55, 38, AlignLeft, AlignTop, "Zero EV"); + } else if(current_index == 1) { + canvas_draw_str_aligned( + canvas, 55, 54 / 2, AlignLeft, AlignTop, "Random IV"); + canvas_draw_str_aligned( + canvas, 55, 38, AlignLeft, AlignTop, "Max EV / Lvl"); + } else if(current_index == 2) { + canvas_draw_str_aligned( + canvas, 55, 54 / 2, AlignLeft, AlignTop, "Random IV"); + canvas_draw_str_aligned( + canvas, 55, 38, AlignLeft, AlignTop, "Max EV"); + } else if(current_index == 3) { + canvas_draw_str_aligned( + canvas, 55, 54 / 2, AlignLeft, AlignTop, "Max IV"); + canvas_draw_str_aligned( + canvas, 55, 38, AlignLeft, AlignTop, "Zero EV"); + } else if(current_index == 4) { + canvas_draw_str_aligned( + canvas, 55, 54 / 2, AlignLeft, AlignTop, "Max IV"); + canvas_draw_str_aligned( + canvas, 55, 38, AlignLeft, AlignTop, "Max EV / Lvl"); + } else if(current_index == 5) { + canvas_draw_str_aligned( + canvas, 55, 54 / 2, AlignLeft, AlignTop, "Max IV"); + canvas_draw_str_aligned( + canvas, 55, 38, AlignLeft, AlignTop, "Max EV"); + } + + canvas_set_font(canvas, FontSecondary); + canvas_draw_icon(canvas, 0, 0, pokemon_table[model->current_pokemon].icon); + canvas_draw_icon(canvas, 128 - 80, 0, &I_Space_80x18); + canvas_draw_str_aligned(canvas, (128 - 40), 5, AlignCenter, AlignTop, "Select Stats"); + + canvas_set_font(canvas, FontPrimary); + elements_button_center(canvas, "OK"); +} + +static bool select_stats_input_callback(InputEvent* event, void* context) { + furi_assert(context); + SelectStats* select_stats = (SelectStats*)context; + bool consumed = false; + + if(event->type == InputTypePress && event->key == InputKeyOk) { + with_view_model_cpp( + select_stats->view, + SelectPokemonModel*, + model, + { + select_stats->app->current_stats = model->current_stats; + }, + false); + + view_dispatcher_switch_to_view(select_stats->app->view_dispatcher, AppViewSelectMove1); + consumed = true; + } else if(event->type == InputTypePress && event->key == InputKeyBack) { + view_dispatcher_switch_to_view(select_stats->app->view_dispatcher, AppViewSelectLevel); + consumed = true; + } else if(event->type == InputTypePress && event->key == InputKeyLeft) { + with_view_model_cpp( + select_stats->view, + SelectPokemonModel*, + model, + { + if(model->current_stats == 0) { + model->current_stats = 5; + } else { + model->current_stats--; + } + }, + true); + consumed = true; + } else if(event->type == InputTypePress && event->key == InputKeyDown) { + with_view_model_cpp( + select_stats->view, + SelectPokemonModel*, + model, + { + if(model->current_stats == 0) { + model->current_stats = 5; + } else { + model->current_stats--; + } + }, + true); + consumed = true; + } else if(event->type == InputTypePress && event->key == InputKeyRight) { + with_view_model_cpp( + select_stats->view, + SelectPokemonModel*, + model, + { + if(model->current_stats == 5) { + model->current_stats = 0; + } else { + model->current_stats++; + } + }, + true); + consumed = true; + } else if(event->type == InputTypePress && event->key == InputKeyUp) { + with_view_model_cpp( + select_stats->view, + SelectPokemonModel*, + model, + { + if(model->current_stats == 5) { + model->current_stats = 0; + } else { + model->current_stats++; + } + }, + true); + consumed = true; + } + + return consumed; +} + +void select_stats_enter_callback(void* context) { + furi_assert(context); + SelectStats* select_stats = (SelectStats*)context; + with_view_model_cpp( + select_stats->view, + SelectPokemonModel*, + model, + { + model->current_pokemon = select_stats->app->current_pokemon; + model->pokemon_hex_code = select_stats->app->pokemon_hex_code; + model->current_level = select_stats->app->current_level; + model->current_stats = select_stats->app->current_stats; + }, + true); +} + +bool select_stats_custom_callback(uint32_t event, void* context) { + UNUSED(event); + furi_assert(context); + SelectStats* select_stats = (SelectStats*)context; + view_dispatcher_send_custom_event(select_stats->app->view_dispatcher, 0); + return true; +} + +void select_stats_exit_callback(void* context) { + furi_assert(context); + UNUSED(context); +} + +SelectStats* select_stats_alloc(App* app) { + SelectStats* select_stats = (SelectStats*)malloc(sizeof(SelectStats)); + select_stats->app = app; + select_stats->view = view_alloc(); + view_set_context(select_stats->view, select_stats); + view_allocate_model(select_stats->view, ViewModelTypeLockFree, sizeof(SelectPokemonModel)); + with_view_model_cpp( + select_stats->view, + SelectPokemonModel*, + model, + { + model->current_stats = app->current_stats; + }, + true); + + view_set_draw_callback(select_stats->view, select_stats_render_callback); + view_set_input_callback(select_stats->view, select_stats_input_callback); + view_set_enter_callback(select_stats->view, select_stats_enter_callback); + view_set_custom_callback(select_stats->view, select_stats_custom_callback); + + view_set_exit_callback(select_stats->view, select_stats_exit_callback); + return select_stats; +} + +void select_stats_free(App* app) { + furi_assert(app->select_stats); + view_free(app->select_stats->view); + free(app->select_stats); +} + +View* select_stats_get_view(App* app) { + furi_assert(app->select_stats); + return app->select_stats->view; +} diff --git a/views/select_stats.hpp b/views/select_stats.hpp new file mode 100644 index 00000000000..27290dde2b5 --- /dev/null +++ b/views/select_stats.hpp @@ -0,0 +1,25 @@ +#ifndef SELECCT_STATS_HPP +#define SELECCT_LEVEL_HPP + +#pragma once +#include +#include +#include + +#include +#include + +typedef struct App App; + +typedef struct { + View* view; + App* app; +} SelectStats; + +SelectStats* select_stats_alloc(App* app); + +void select_stats_free(App* app); + +View* select_stats_get_view(App* app); + +#endif /* SELECCT_STATS_HPP */ \ No newline at end of file diff --git a/views/trade.cpp b/views/trade.cpp index 7876e9f4131..d8cbff20147 100644 --- a/views/trade.cpp +++ b/views/trade.cpp @@ -373,6 +373,7 @@ void trade_enter_callback(void* context) { model->current_pokemon = trade->app->current_pokemon; model->pokemon_hex_code = trade->app->pokemon_hex_code; model->current_level = trade->app->current_level; + model->current_stats = trade->app->current_stats; model->current_move = trade->app->current_move; model->move1_hex_code = trade->app->move1_hex_code; model->move2_hex_code = trade->app->move2_hex_code; @@ -445,7 +446,12 @@ void trade_enter_callback(void* context) { // Set the Pokemon stat experience uint16_t statexp = 0x0000; - // uint16_t statexp = (65535 / 100) * level; + if(trade->app->current_stats == 1 || trade->app->current_stats == 4) { + statexp = (65535 / 100) * level; + } else if(trade->app->current_stats == 2 || trade->app->current_stats == 5) { + statexp = 65535; + } + DATA_BLOCK2.party[0].hp_ev = ((statexp >> 8) & 0x00FF) | ((statexp << 8) & 0xFF00); DATA_BLOCK2.party[0].atk_ev = ((statexp >> 8) & 0x00FF) | ((statexp << 8) & 0xFF00); DATA_BLOCK2.party[0].def_ev = ((statexp >> 8) & 0x00FF) | ((statexp << 8) & 0xFF00); @@ -459,30 +465,42 @@ void trade_enter_callback(void* context) { FURI_LOG_D(TAG, "[Trade] Pokemon Speed EV: %x", DATA_BLOCK2.party[0].spd_ev); FURI_LOG_D(TAG, "[Trade] Pokemon Special EV: %x", DATA_BLOCK2.party[0].special_ev); - // Set the Pokemon max hp - - // uint16_t max_hp = (0.01 * (2 * pokemon_table[trade->app->current_pokemon].base_hp + 15 + (0.25 * statexp)) * level) + level + 10; - uint16_t max_hp = floor((((2 * (pokemon_table[trade->app->current_pokemon].base_hp + 15)) + floor(sqrt(statexp) / 4)) * level) / 100) + (level + 10); - DATA_BLOCK2.party[0].max_hp = ((max_hp >> 8) & 0x00FF) | ((max_hp << 8) & 0xFF00); - DATA_BLOCK2.party[0].hp = ((max_hp >> 8) & 0x00FF) | ((max_hp << 8) & 0xFF00); + // Set the Pokemon stats - FURI_LOG_D(TAG, "[Trade] Pokemon Max HP: %x", DATA_BLOCK2.party[0].max_hp); + uint8_t atk_iv = 15; + uint8_t def_iv = 15; + uint8_t spd_iv = 15; + uint8_t special_iv = 15; + uint8_t hp_iv = 15; + if(trade->app->current_stats <= 2) { + atk_iv = rand() % 15; + def_iv = rand() % 15; + spd_iv = rand() % 15; + special_iv = rand() % 15; + DATA_BLOCK2.party[0].iv = ((atk_iv & 0b00001111) << 12) | ((def_iv & 0b00001111) << 8) | ((spd_iv & 0b00001111) << 4) | ((special_iv & 0b00001111)); + hp_iv = (DATA_BLOCK2.party[0].iv & 0xAA) >> 4; + } - // Set the Pokemon stats + FURI_LOG_D(TAG, "[Trade] Pokemon IV: %x", DATA_BLOCK2.party[0].iv); + FURI_LOG_D(TAG, "[Trade] Pokemon HP IV: %x", hp_iv); + FURI_LOG_D(TAG, "[Trade] Pokemon Attack IV: %x", atk_iv); + FURI_LOG_D(TAG, "[Trade] Pokemon Defence IV: %x", def_iv); + FURI_LOG_D(TAG, "[Trade] Pokemon Speed IV: %x", spd_iv); + FURI_LOG_D(TAG, "[Trade] Pokemon Special IV: %x", special_iv); - // uint8_t attack = (0.01 * (2 * pokemon_table[trade->app->current_pokemon].base_atk + 15 + (0.25 * statexp)) * level) + 5; - uint16_t attack = floor((((2 * (pokemon_table[trade->app->current_pokemon].base_atk + 15)) + floor(sqrt(statexp) / 4)) * level) / 100) + 5; + uint16_t max_hp = floor((((2 * (pokemon_table[trade->app->current_pokemon].base_hp + hp_iv)) + floor(sqrt(statexp) / 4)) * level) / 100) + (level + 10); + DATA_BLOCK2.party[0].max_hp = ((max_hp >> 8) & 0x00FF) | ((max_hp << 8) & 0xFF00); + DATA_BLOCK2.party[0].hp = ((max_hp >> 8) & 0x00FF) | ((max_hp << 8) & 0xFF00); + uint16_t attack = floor((((2 * (pokemon_table[trade->app->current_pokemon].base_atk + atk_iv)) + floor(sqrt(statexp) / 4)) * level) / 100) + 5; DATA_BLOCK2.party[0].atk = ((attack >> 8) & 0x00FF) | ((attack << 8) & 0xFF00); - // uint8_t defence = (0.01 * (2 * pokemon_table[trade->app->current_pokemon].base_def + 15 + (0.25 * statexp)) * level) + 5; - uint16_t defence = floor((((2 * (pokemon_table[trade->app->current_pokemon].base_def + 15)) + floor(sqrt(statexp) / 4)) * level) / 100) + 5; + uint16_t defence = floor((((2 * (pokemon_table[trade->app->current_pokemon].base_def + def_iv)) + floor(sqrt(statexp) / 4)) * level) / 100) + 5; DATA_BLOCK2.party[0].def = ((defence >> 8) & 0x00FF) | ((defence << 8) & 0xFF00); - // uint8_t speed = (0.01 * (2 * pokemon_table[trade->app->current_pokemon].base_spd + 15 + (0.25 * statexp)) * level) + 5; - uint16_t speed = floor((((2 * (pokemon_table[trade->app->current_pokemon].base_spd + 15)) + floor(sqrt(statexp) / 4)) * level) / 100) + 5; + uint16_t speed = floor((((2 * (pokemon_table[trade->app->current_pokemon].base_spd + spd_iv)) + floor(sqrt(statexp) / 4)) * level) / 100) + 5; DATA_BLOCK2.party[0].spd = ((speed >> 8) & 0x00FF) | ((speed << 8) & 0xFF00); - // uint8_t special = (0.01 * (2 * pokemon_table[trade->app->current_pokemon].base_special + 15 + (0.25 * statexp)) * level) + 5; - uint16_t special = floor((((2 * (pokemon_table[trade->app->current_pokemon].base_special + 15)) + floor(sqrt(statexp) / 4)) * level) / 100) + 5; + uint16_t special = floor((((2 * (pokemon_table[trade->app->current_pokemon].base_special + special_iv)) + floor(sqrt(statexp) / 4)) * level) / 100) + 5; DATA_BLOCK2.party[0].special = ((special >> 8) & 0x00FF) | ((special << 8) & 0xFF00); + FURI_LOG_D(TAG, "[Trade] Pokemon Max HP: %x", DATA_BLOCK2.party[0].max_hp); FURI_LOG_D(TAG, "[Trade] Pokemon Attack: %x", DATA_BLOCK2.party[0].atk); FURI_LOG_D(TAG, "[Trade] Pokemon Defence: %x", DATA_BLOCK2.party[0].def); FURI_LOG_D(TAG, "[Trade] Pokemon Speed: %x", DATA_BLOCK2.party[0].spd);