Skip to content

Commit

Permalink
trade: Fixes, features, and improvements
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
kbembedded committed Nov 6, 2023
1 parent de8ef7d commit 28b18b7
Show file tree
Hide file tree
Showing 8 changed files with 768 additions and 338 deletions.
28 changes: 25 additions & 3 deletions pokemon_app.c
Original file line number Diff line number Diff line change
Expand Up @@ -1863,6 +1863,17 @@ const NamedList type_list[] = {
{},
};

int pokemon_table_get_num_from_index(const PokemonTable* table, uint8_t index) {
int i;

for (i = 0;; i++) {
if (table[i].index == index) return i;
if (table[i].name == NULL) break;
}

return 0;
}

int pokemon_named_list_get_num_elements(const NamedList* list) {
int i;

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

PokemonFap* pokemon_alloc() {
PokemonFap* pokemon_fap = (PokemonFap*)malloc(sizeof(PokemonFap));
View* trade_view;

// View dispatcher
pokemon_fap->view_dispatcher = view_dispatcher_alloc();
Expand Down Expand Up @@ -2180,8 +2192,18 @@ PokemonFap* pokemon_alloc() {
pokemon_fap->view_dispatcher, AppViewSelectPokemon, pokemon_fap->select_view);

// Trade View
pokemon_fap->trade_view = trade_alloc(pokemon_fap);
view_dispatcher_add_view(pokemon_fap->view_dispatcher, AppViewTrade, pokemon_fap->trade_view);
/* Allocate a view and pass it to the trade routines. The trade API is
* responsible for freeing every resource, including the view itself. This
* is actually how it was already designed anyway, so we just create it first. The
* main FAP does not keep track of it. In theory, we could let the trade
* API handle all of it, however, it doesn't make sense to have the trade
* API add itself to the view dispatcher since that kind of management is
* outside the scope of the trade routines and is the responsibility of the
* main FAP.
*/
trade_view = view_alloc();
pokemon_fap->trade = trade_alloc(pokemon_fap->trade_block, pokemon_fap->pokemon_table, trade_view);
view_dispatcher_add_view(pokemon_fap->view_dispatcher, AppViewTrade, trade_view);

return pokemon_fap;
}
Expand All @@ -2194,7 +2216,7 @@ void free_app(PokemonFap* pokemon_fap) {
select_pokemon_free(pokemon_fap);

view_dispatcher_remove_view(pokemon_fap->view_dispatcher, AppViewTrade);
trade_free(pokemon_fap);
trade_free(pokemon_fap->trade);

view_dispatcher_remove_view(pokemon_fap->view_dispatcher, AppViewMainMenu);

Expand Down
14 changes: 3 additions & 11 deletions pokemon_app.h
Original file line number Diff line number Diff line change
Expand Up @@ -44,22 +44,12 @@ struct named_list {

typedef struct named_list NamedList;

typedef enum {
GAMEBOY_INITIAL,
GAMEBOY_READY,
GAMEBOY_WAITING,
GAMEBOY_TRADE_READY,
GAMEBOY_SEND,
GAMEBOY_PENDING,
GAMEBOY_TRADING
} render_gameboy_state_t;

struct pokemon_fap {
ViewDispatcher* view_dispatcher;

/* View ports for each of the application's steps */
View* select_view;
View* trade_view;
void* trade;

/* Scene manager */
SceneManager* scene_manager;
Expand Down Expand Up @@ -105,6 +95,8 @@ typedef enum {
AppViewExitConfirm,
} AppView;

int pokemon_table_get_num_from_index(const PokemonTable* table, uint8_t index);

int pokemon_named_list_get_num_elements(const NamedList* list);

int pokemon_named_list_get_list_pos_from_index(const NamedList* list, uint8_t index);
Expand Down
5 changes: 5 additions & 0 deletions pokemon_data.h
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
* GB/Z80. e.g. a uint16_t value of 0x2c01 translates to 0x012c.
* Need to use __builtin_bswap16(val) to switch between Flipper and Pokemon.
*/
/* This is 44 bytes in memory */
struct __attribute__((__packed__)) pokemon_structure {
uint8_t index;
uint16_t hp; // Calculated from level
Expand Down Expand Up @@ -42,6 +43,8 @@ struct __attribute__((__packed__)) pokemon_structure {
uint16_t special; // Calculated from level
};

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

/* This is 418 bytes in memory/transmitted */
struct __attribute__((__packed__)) trade_data_block {
/* XXX: Change this to use struct name above */
unsigned char trainer_name[11];
uint8_t party_cnt;
/* Only the first pokemon is ever used even though there are 7 bytes here.
Expand Down
7 changes: 7 additions & 0 deletions scenes/pokemon_menu.c
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,13 @@ void main_menu_scene_on_enter(void* context) {
*/
scene_manager_set_scene_state(pokemon_fap->scene_manager, SelectMoveScene, 0);

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

submenu_reset(pokemon_fap->submenu);

snprintf(
Expand Down
Loading

0 comments on commit 28b18b7

Please sign in to comment.