Skip to content

Commit 28b18b7

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 28b18b7

8 files changed

+768
-338
lines changed

pokemon_app.c

+25-3
Original file line numberDiff line numberDiff line change
@@ -1863,6 +1863,17 @@ 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

@@ -2133,6 +2144,7 @@ static void trade_block_free(TradeBlock* trade) {
21332144

21342145
PokemonFap* pokemon_alloc() {
21352146
PokemonFap* pokemon_fap = (PokemonFap*)malloc(sizeof(PokemonFap));
2147+
View* trade_view;
21362148

21372149
// View dispatcher
21382150
pokemon_fap->view_dispatcher = view_dispatcher_alloc();
@@ -2180,8 +2192,18 @@ PokemonFap* pokemon_alloc() {
21802192
pokemon_fap->view_dispatcher, AppViewSelectPokemon, pokemon_fap->select_view);
21812193

21822194
// 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);
2195+
/* Allocate a view and pass it to the trade routines. The trade API is
2196+
* responsible for freeing every resource, including the view itself. This
2197+
* is actually how it was already designed anyway, so we just create it first. The
2198+
* main FAP does not keep track of it. In theory, we could let the trade
2199+
* API handle all of it, however, it doesn't make sense to have the trade
2200+
* API add itself to the view dispatcher since that kind of management is
2201+
* outside the scope of the trade routines and is the responsibility of the
2202+
* main FAP.
2203+
*/
2204+
trade_view = view_alloc();
2205+
pokemon_fap->trade = trade_alloc(pokemon_fap->trade_block, pokemon_fap->pokemon_table, trade_view);
2206+
view_dispatcher_add_view(pokemon_fap->view_dispatcher, AppViewTrade, trade_view);
21852207

21862208
return pokemon_fap;
21872209
}
@@ -2194,7 +2216,7 @@ void free_app(PokemonFap* pokemon_fap) {
21942216
select_pokemon_free(pokemon_fap);
21952217

21962218
view_dispatcher_remove_view(pokemon_fap->view_dispatcher, AppViewTrade);
2197-
trade_free(pokemon_fap);
2219+
trade_free(pokemon_fap->trade);
21982220

21992221
view_dispatcher_remove_view(pokemon_fap->view_dispatcher, AppViewMainMenu);
22002222

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_data.h

+5
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
@@ -42,6 +43,8 @@ struct __attribute__((__packed__)) pokemon_structure {
4243
uint16_t special; // Calculated from level
4344
};
4445

46+
/* XXX: Stock gameboy seems to use one TERM_ byte and then 0x00 for remaining bytes */
47+
/* but only OT Name? nickname is TERM_ all the way */
4548
struct __attribute__((__packed__)) name {
4649
/* Reused a few times, but in Gen I, all name strings are 11 bytes in memory.
4750
* At most, 10 symbols and a TERM_ byte.
@@ -50,7 +53,9 @@ struct __attribute__((__packed__)) name {
5053
unsigned char str[11];
5154
};
5255

56+
/* This is 418 bytes in memory/transmitted */
5357
struct __attribute__((__packed__)) trade_data_block {
58+
/* XXX: Change this to use struct name above */
5459
unsigned char trainer_name[11];
5560
uint8_t party_cnt;
5661
/* Only the first pokemon is ever used even though there are 7 bytes here.

scenes/pokemon_menu.c

+7
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,13 @@ 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(pokemon_fap->pokemon_table, pokemon_fap->trade_block->party_members[0]);
47+
4148
submenu_reset(pokemon_fap->submenu);
4249

4350
snprintf(

0 commit comments

Comments
 (0)