Skip to content

Commit 66c6258

Browse files
committed
trade: Fixes, features, and improvements
This commit wraps up a number of changes that aim to improve the trade process, its robustness, add some features, and fix some bugs. Change list in rough order of importance: Better pattern matching of incoming data for each trade step, provides more robust logic during trading and more correctly sync's states between the Flipper and gameboy. Fixes #20 as a side effect. Implement trade patch list. This allows for the Flipper to both send and receive patch data. The patch list is used by Pokemon to patch what is considered to be a NO_DATA_BYTE (e.g. data from the follower was not yet ready) with another byte, and then track that byte's index. The other side then restores that byte to what it should be. Fixes #18 Ability for the Flipper to correctly receive the Pokemon traded to it and put that Pokemon in the Flipper's trade buffer. This allows for trading back and forth (e.g. in the case of a Pokemon that evolves with trade, trading to the Flipper and back will trigger an evolution on the Gameboy), as well as the following feature. Ability for the Flipper to back out of the trade menu, modify the current Pokemon, and re-enter the trade menu without having to completely reset the Gameboy and re-enter the Cable Club. Fixes #19 Completely isolates Trade's context and scope. The _alloc() function now returns an anonymous pointer that means nothing to the rest of the application. However, this does require a bit of juggling as the main application is responsible for setting up the View at the start. Adds a huge comment block in the trade source outlining the current understanding of the actual trade data protocol. Also adds specific comments for more context specific details through various trade states. Changes to how the draw callback is called. In order to correctly update the canvas during the actual trading with a graphic and LED changed every 250 ms, the timer to call the update was changed to only run every 250 ms. However, Flipper OS does not guarantee that this is the only draw update call, and may issue a draw update at any time. The view model now tracks what the LED state should be, and our timed update routine callback is the only place this LED state is toggled. This forces the trade animation to always be sync'ed correctly no matter how often the Flipper OS calls it. Clean up state tracking overall. There are now two states that are tracked, the Gameboy/link state, and the actual trade states. The Gameboy state still has a bit of overlap with the trade states, however, it combines what was the link state in to this. This also allows elimination of additional bools that were used for state tracking in parallel. State transitions and meanings should now be a bit more straightforward. CLK pin now implements an interrupt on either edge. The ISR was updated to shift data out on the falling edge, and read data in on the rising edge. This eliminates any need for delays while in an interrupt context as well as mimics Gameboy behavior by matching the setup/hold times. Remove use of magic numbers as much as possible. Bytes to/from the Pokemon game now use macros for most of the bytes. Many of these were pulled from https://github.com/pret/pokered defines. Clean up cycle counter to real-time maths. Copied general paradigms from Flipper onewire code. This also includes the bit counter timeout and now ensures correct timeouts are measured between transmitted bytes.
1 parent de8ef7d commit 66c6258

10 files changed

+766
-347
lines changed

pokemon_app.c

+26-17
Original file line numberDiff line numberDiff line change
@@ -1863,17 +1863,23 @@ const NamedList type_list[] = {
18631863
{},
18641864
};
18651865

1866+
int pokemon_table_get_num_from_index(const PokemonTable* table, uint8_t index) {
1867+
int i;
1868+
1869+
for(i = 0;; i++) {
1870+
if(table[i].index == index) return i;
1871+
if(table[i].name == NULL) break;
1872+
}
1873+
1874+
return 0;
1875+
}
1876+
18661877
int pokemon_named_list_get_num_elements(const NamedList* list) {
18671878
int i;
18681879

18691880
for(i = 0;; i++) {
18701881
if(list[i].name == NULL) return i;
18711882
}
1872-
1873-
/* XXX: Would be faster to do something like this, but, can't easily do
1874-
* that using the current pointers. Might be able to clean this up later?
1875-
*/
1876-
//return sizeof(type_list)/sizeof(type_list[0]);
18771883
}
18781884

18791885
int pokemon_named_list_get_list_pos_from_index(const NamedList* list, uint8_t index) {
@@ -1884,7 +1890,7 @@ int pokemon_named_list_get_list_pos_from_index(const NamedList* list, uint8_t in
18841890
if(index == list[i].index) return i;
18851891
}
18861892

1887-
/* XXX: This will return the first entry in case index is not matched.
1893+
/* This will return the first entry in case index is not matched.
18881894
* Could be surprising at runtime.
18891895
*/
18901896
return 0;
@@ -1898,7 +1904,7 @@ const char* pokemon_named_list_get_name_from_index(const NamedList* list, uint8_
18981904
if(index == list[i].index) return list[i].name;
18991905
}
19001906

1901-
/* XXX: This will return the first entry in the case index is not matched,
1907+
/* This will return the first entry in the case index is not matched,
19021908
* this could be confusing/problematic at runtime.
19031909
*/
19041910
return list[0].name;
@@ -1912,9 +1918,6 @@ void pokemon_trade_block_set_default_name(char* dest, PokemonFap* pokemon_fap, s
19121918
/* Walk through the default name, toupper() each character, encode it, and
19131919
* then write that to the same position in the trade_block.
19141920
*/
1915-
/* XXX: The limit of this is hard-coded to a length of 11 at most. This may
1916-
* be a problem down the road!
1917-
*/
19181921
for(i = 0; i < 11; i++) {
19191922
pokemon_fap->trade_block->nickname[0].str[i] = pokemon_char_to_encoded(
19201923
toupper(pokemon_fap->pokemon_table[pokemon_fap->curr_pokemon].name[i]));
@@ -2117,9 +2120,11 @@ static TradeBlock* trade_block_alloc(void) {
21172120
/* OT trainer ID# */
21182121
trade->party[0].ot_id = __builtin_bswap16(42069);
21192122

2120-
/* XXX: move pp isn't explicitly set up, should be fine */
2121-
/* XXX: catch/held isn't explicitly set up, should be okay for only Gen I support now */
2122-
/* XXX: Status condition isn't explicity let up, would you ever want to? */
2123+
/* Notes:
2124+
* Move pp isn't explicitly set up, should be fine
2125+
* Catch/held isn't explicitly set up, should be okay for only Gen I support now
2126+
* Status condition isn't explicity let up, would you ever want to?
2127+
*/
21232128

21242129
/* Set up initial level */
21252130
trade->party[0].level = 2;
@@ -2180,8 +2185,12 @@ PokemonFap* pokemon_alloc() {
21802185
pokemon_fap->view_dispatcher, AppViewSelectPokemon, pokemon_fap->select_view);
21812186

21822187
// Trade View
2183-
pokemon_fap->trade_view = trade_alloc(pokemon_fap);
2184-
view_dispatcher_add_view(pokemon_fap->view_dispatcher, AppViewTrade, pokemon_fap->trade_view);
2188+
/* Allocates its own view and adds it to the main view_dispatcher */
2189+
pokemon_fap->trade = trade_alloc(
2190+
pokemon_fap->trade_block,
2191+
pokemon_fap->pokemon_table,
2192+
pokemon_fap->view_dispatcher,
2193+
AppViewTrade);
21852194

21862195
return pokemon_fap;
21872196
}
@@ -2193,8 +2202,8 @@ void free_app(PokemonFap* pokemon_fap) {
21932202
view_dispatcher_remove_view(pokemon_fap->view_dispatcher, AppViewSelectPokemon);
21942203
select_pokemon_free(pokemon_fap);
21952204

2196-
view_dispatcher_remove_view(pokemon_fap->view_dispatcher, AppViewTrade);
2197-
trade_free(pokemon_fap);
2205+
/* Also removes itself from the view_dispatcher */
2206+
trade_free(pokemon_fap->view_dispatcher, AppViewTrade, pokemon_fap->trade);
21982207

21992208
view_dispatcher_remove_view(pokemon_fap->view_dispatcher, AppViewMainMenu);
22002209

pokemon_app.h

+3-11
Original file line numberDiff line numberDiff line change
@@ -44,22 +44,12 @@ struct named_list {
4444

4545
typedef struct named_list NamedList;
4646

47-
typedef enum {
48-
GAMEBOY_INITIAL,
49-
GAMEBOY_READY,
50-
GAMEBOY_WAITING,
51-
GAMEBOY_TRADE_READY,
52-
GAMEBOY_SEND,
53-
GAMEBOY_PENDING,
54-
GAMEBOY_TRADING
55-
} render_gameboy_state_t;
56-
5747
struct pokemon_fap {
5848
ViewDispatcher* view_dispatcher;
5949

6050
/* View ports for each of the application's steps */
6151
View* select_view;
62-
View* trade_view;
52+
void* trade;
6353

6454
/* Scene manager */
6555
SceneManager* scene_manager;
@@ -105,6 +95,8 @@ typedef enum {
10595
AppViewExitConfirm,
10696
} AppView;
10797

98+
int pokemon_table_get_num_from_index(const PokemonTable* table, uint8_t index);
99+
108100
int pokemon_named_list_get_num_elements(const NamedList* list);
109101

110102
int pokemon_named_list_get_list_pos_from_index(const NamedList* list, uint8_t index);

pokemon_char_encode.c

-1
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@
33

44
#include "pokemon_char_encode.h"
55

6-
/* XXX Current text_input module only offers alnum and space input */
76
char pokemon_char_to_encoded(int byte) {
87
switch(byte) {
98
case 'A':

pokemon_data.h

+3
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
* GB/Z80. e.g. a uint16_t value of 0x2c01 translates to 0x012c.
1414
* Need to use __builtin_bswap16(val) to switch between Flipper and Pokemon.
1515
*/
16+
/* This is 44 bytes in memory */
1617
struct __attribute__((__packed__)) pokemon_structure {
1718
uint8_t index;
1819
uint16_t hp; // Calculated from level
@@ -50,7 +51,9 @@ struct __attribute__((__packed__)) name {
5051
unsigned char str[11];
5152
};
5253

54+
/* This is 418 bytes in memory/transmitted */
5355
struct __attribute__((__packed__)) trade_data_block {
56+
/* TODO: Change this to use struct name above */
5457
unsigned char trainer_name[11];
5558
uint8_t party_cnt;
5659
/* Only the first pokemon is ever used even though there are 7 bytes here.

scenes/pokemon_level.c

+2-1
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ static bool select_level_input_validator(const char* text, FuriString* error, vo
1919
rc = false;
2020
} else {
2121
pokemon_fap->trade_block->party[0].level = level_val;
22-
/* XXX: Need to recalculate other stats with level updated! */
22+
pokemon_fap->trade_block->party[0].level_again = level_val;
2323
}
2424

2525
return rc;
@@ -28,6 +28,7 @@ static bool select_level_input_validator(const char* text, FuriString* error, vo
2828
static void select_level_input_callback(void* context) {
2929
PokemonFap* pokemon_fap = (PokemonFap*)context;
3030

31+
/* Recalculate all stats from updated level */
3132
pokemon_trade_block_recalculate_stats_from_level(pokemon_fap);
3233
scene_manager_previous_scene(pokemon_fap->scene_manager);
3334
}

scenes/pokemon_menu.c

+8
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,14 @@ void main_menu_scene_on_enter(void* context) {
3838
*/
3939
scene_manager_set_scene_state(pokemon_fap->scene_manager, SelectMoveScene, 0);
4040

41+
/* HACK: Since we may have come from trade view, we cannot assume that
42+
* pokemon_fap->curr_pokemon is correct.
43+
* The proper way to do this would be to instead of tracking curr_pokemon
44+
* separately, have it always be derived fro the current trade_block.
45+
*/
46+
pokemon_fap->curr_pokemon = pokemon_table_get_num_from_index(
47+
pokemon_fap->pokemon_table, pokemon_fap->trade_block->party_members[0]);
48+
4149
submenu_reset(pokemon_fap->submenu);
4250

4351
snprintf(

0 commit comments

Comments
 (0)