Skip to content

Commit 78c99b8

Browse files
authored
Merge pull request #21 from kbembedded/trade-features
Trade: Overhaul for features and fixes. Bump to 1.4
2 parents 788848a + 32493e4 commit 78c99b8

22 files changed

+864
-380
lines changed

README.md

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ It currently trades a Pokemon based on your choice of Pokemon, Level, Stats and
2222

2323
## Installation Directions
2424

25-
This project is intended to be overlayed on top of an existing firmware repo, in my case the **Release 0.79.1** version.
25+
This project is intended to be overlaid on top of an existing firmware repo, in my case the **Release 0.79.1** version.
2626

2727
- Clone the [Flipper Zero firmware repository](https://github.com/flipperdevices/flipperzero-firmware). Refer to [this tutorial](https://github.com/jamisonderek/flipper-zero-tutorials/tree/main/firmware/updating/README.md) for updating the firmware.
2828
- Copy the "pokemon" folder into the `/applications_user/pokemon` folder in your firmware.
@@ -87,7 +87,7 @@ These instructions assume that you are starting at the Flipper Zero desktop. Oth
8787
<br />
8888
</p>
8989

90-
- The `Select Moves` menu is used to pick the set the traded Pokemon's moves. They are pre-populated with the moveset that the Pokemon would know at level 1. Selecting a move slot will bring up an alphabetical index of moves. Additionally, `No Move` and `Default` can be quickliy selected. Note that any move after the first `No Move` is ignored.
90+
- The `Select Moves` menu is used to pick the set the traded Pokemon's moves. They are pre-populated with the moveset that the Pokemon would know at level 1. Selecting a move slot will bring up an alphabetical index of moves. Additionally, `No Move` and `Default` can be quickly selected. Note that any move after the first `No Move` is ignored.
9191

9292
<p align='center'>
9393
<br />
@@ -115,7 +115,7 @@ These instructions assume that you are starting at the Flipper Zero desktop. Oth
115115
- The Pokemon's stats can also be influenced. The current settings are:
116116
- `Random IV, Zero EV` Mimics stats of a caught wild Pokemon.
117117
- `Random IV, Max EV / Level` IV is randomized, but EV is set to the maximum a trained Pokemon could be for its current level.
118-
- `Randon IV, Max EV` IV is randomized, EV is set to the abosolute max for a perfectly trained Pokemon.
118+
- `Randon IV, Max EV` IV is randomized, EV is set to the absolute max for a perfectly trained Pokemon.
119119
- `Max IV, Zero EV` Mimics stats of a caught wild Pokemon, but with the maximum IV possible.
120120
- `Max IV, Max EV / Level` IV is max, EV is set to the maximum a trained Pokemon could be for its current level.
121121
- `Max IV, Max EV` Absolutely perfect and overly powerful Pokemon.
@@ -126,7 +126,7 @@ These instructions assume that you are starting at the Flipper Zero desktop. Oth
126126
<br />
127127
</p>
128128

129-
- The `OT ID#` and `OT Name` of the Pokemon can also be set. The `OT ID#` must be between `0` and `65535`. Setting the `OT ID#` and `OT Name` to the same as your current trainer's causes the game to believe it was a wild caught Pokemon and not one that was traded. This means high level Pokmon will still obey you without badges, but, will not get the experience boost of a traded Pokemon.
129+
- The `OT ID#` and `OT Name` of the Pokemon can also be set. The `OT ID#` must be between `0` and `65535`. Setting the `OT ID#` and `OT Name` to the same as your current trainer's causes the game to believe it was a wild caught Pokemon and not one that was traded. This means high level Pokemon will still obey you without badges, but, will not get the experience boost of a traded Pokemon.
130130

131131
<p align='center'>
132132
<br />
@@ -220,6 +220,8 @@ These instructions assume that you are starting at the Flipper Zero desktop. Oth
220220
<img src="./docs/images/flipper-zero-flat-12.png" width="400" /><br />
221221
</p>
222222

223+
- Once the trade is complete, both the **Game Boy** and the **Flipper Zero** will return to the `WAITING` state. If the **Game Boy** selects `CANCEL`, the **Flipper Zero** will return to the `READY` state. The <img src="./docs/images/back.png" /> BACK button can be pressed to return to the main menu. The traded Pokemon can be modified, or completely changed, if desired. Once the **Flipper Zero** Re-enters the Trade screen, and the **Game Boy** re-selects the trade table in-game, another trade can be completed. This allows for trading multiple Pokemon without having to reset the **Game Boy** each time.
224+
223225
If the Flipper Zero gets stuck at the end of the exchange, you must reboot it by pressing the <img src="./docs/images/left.png" /> LEFT + <img src="./docs/images/back.png" /> BACK key combination.
224226

225227
<p align='center'>
@@ -328,7 +330,7 @@ For each image, the color `#aaa` was transformed to `#fff` so that Flipper Zero
328330
## Links
329331

330332
- [Flipper Zero firmware source code](https://github.com/flipperdevices/flipperzero-firmware)
331-
- Adan Scotney's pokemon [trade protocol specification](http://www.adanscotney.com/2014/01/spoofing-pokemon-trades-with-stellaris.html) and implementation
333+
- Adan Scotney's Pokemon [trade protocol specification](http://www.adanscotney.com/2014/01/spoofing-pokemon-trades-with-stellaris.html) and implementation
332334
- Derek Jamison - [Youtube Channel](https://www.youtube.com/@MrDerekJamison)
333335
- Matt Penny - [GBPlay Blog](https://blog.gbplay.io/)
334336
- [Pokémon data structure (Generation I)](<https://bulbapedia.bulbagarden.net/wiki/Pok%C3%A9mon_data_structure_(Generation_I)>)

TODO.md

Lines changed: 10 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,8 @@
1111
- [x] Add view to allow the traded Pokemon's level to be chosen between 2 and 100
1212
- [x] Add view to allow the traded Pokemon's hidden stats to be chosen (IV and EV) from some options
1313
- [ ] Are there any better ways to present these options?
14-
- [ ] Debug traded Pokemon level issue where after a battle the Pokemon's level drops (doesn't affect all traded Pokemon)
15-
- [ ] Optimise the level selection screen to be a number slider input instead of the current slideshow style selector (Implemented as text input that only accepts numbers)
14+
- [x] Debug traded Pokemon level issue where after a battle the Pokemon's level drops (doesn't affect all traded Pokemon)
15+
- [x] Optimise the level selection screen to be a number slider input instead of the current slideshow style selector (Implemented as text input that only accepts numbers)
1616
- Moves
1717
- [x] Add view to allow the traded Pokemon's moveset to be chosen (all 4 moves) allowing no move as an option
1818
- [ ] Find a way to get faster scrolling through the move select submenu
@@ -24,23 +24,24 @@
2424
- [x] Support setting pokemon type(s)
2525
- [ ] Implement a save/revert to default workflow on the select types scene
2626
- Trade
27-
- [ ] Investigate Trade screens not always blinking
27+
- [x] Investigate Trade screens not always blinking
2828
- UI
2929
- [ ] Find a way to line up submenu items so the main menu looks cleaner
3030
- They currently _mostly_ line up thanks to some manual spacing, but tabs don't appear to be supported to force that alignment
3131
- Alternatively may need to implement our own view to make this pretty
3232
- Documentation
3333
- [x] Add images for the level selection screen, stats selection screen, and move selection screens as per the original README
3434
- Codebase
35-
- [ ] Reimplement Logging calls
35+
- [x] Reimplement Logging calls
3636
- [ ] Clean up the codebase as it is now, there are a lot of optimizations in speed and code complexity that can be made, especially in added code in pokemon_app and maybe some code reduction/reuse in scenes
3737
- [ ] Consider using a single View in main app struct and only allocate a view as needed to reduce memory footprint
3838

3939
- Future Wants
40-
- [ ] Trading to Gen II games with both Gen I and Gen II pokemon
40+
- [ ] Trading to Gen II games with both Gen I and Gen II Pokemon
4141
- [ ] Enable IR mystery gift usage in Gen II using Flipper
42-
- [ ] Be able to set up mutiple pokemon to be able to trade more than one per trip to trade center
43-
- [ ] Be able to trade back and forth for e.g. trading a pokemon that evolves only when traded
44-
- [ ] Would Separateing out link cable states result in a cleaner API?
42+
- [ ] Be able to set up multiple Pokemon to be able to trade more than one per trip to trade center
43+
- [x] Be able to trade back and forth for e.g. trading a Pokemon that evolves only when traded
44+
- [x] Would Separating out link cable states result in a cleaner API?
4545
- [ ] Implement some simple logic to be able to "battle" the Flipper?
46-
- [ ] There was a suggestion to be able to trade in a pokemon to harvest OT name and ID on the flipper and set it to that.
46+
- [ ] There was a suggestion to be able to trade in a Pokemon to harvest OT name and ID on the flipper and set it to that.
47+
- [ ] Ability to save Pokemon to SD card. Either created on, or traded to, the Flipper app.

application.fam

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ App(
55
entry_point="pokemon_app",
66
requires=["gui"],
77
stack_size=2 * 1024,
8-
fap_version=[1,3],
8+
fap_version=[1,4],
99
fap_category="GPIO",
1010
fap_icon="pokemon_10px.png",
1111
fap_icon_assets="assets",

changelog.md

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,21 @@
11
# Changelog - Patch Notes
22

3-
## Version 1.2.3
3+
## Version 1.4
4+
- **Bug Fixes:** More robust trade logic fixes issues with names, remove ability to use numbers in Pokemon/Trainer names as the game itself will not allow that, fix trade animation not always being animated, make FAP icon 1bpp.
5+
- **Add Features:** Implement trade patch list that Game Boy expects and uses, add ability to return to main menu to modify a Pokemon traded to the Flipper and re-enter trade without the Game Boy needing to power cycle and re-connect through the Link Club, add back debug logging.
6+
- **Trade Refactor:** Eliminate extraneous code, improve robustness of state tracking during trades, isolate Trade's scope to the compilation unit, add notes on exchanged bytes during a trade, improve timing of animation during trade, reduce time spent in interrupt context, follow same setup/hold times for data exchange that the Game Boy uses, reduce use of magic numbers, clean up and improve code tracking real world time
7+
8+
## Version 1.3
49
- **Refactor and UI cleanup:** Convert to Flipper Zero UI modules for simpler interface, reduce binary size.
5-
- **Add Features:** Add ability to set custom pokemon nickname or default, add ability to set OT name and ID, add ability to select pokemon type(s). Note that, an evolution as well as a couple of different attacks will cause this to be overwritten with the pokemon's default values.
6-
- **Bug Fixes:** Fix strange issue with exp gain causing traded pokemon to de-level and result in incorrect stats.
10+
- **Add Features:** Add ability to set custom Pokemon nickname or default, add ability to set OT name and ID, add ability to select Pokemon type(s). Note that, an evolution as well as a couple of different attacks will cause this to be overwritten with the Pokemon's default values.
11+
- **Bug Fixes:** Fix strange issue with exp gain causing traded Pokemon to de-level and result in incorrect stats.
712

813
## Version 1.2.2
9-
- **Extended Functionality:** Add support to set level, select moves, set up EV/IV to a few predefined configurations, set up stats based on level and EV/IV settings, set nickname to default pokemon name.
14+
- **Extended Functionality:** Add support to set level, select moves, set up EV/IV to a few predefined configurations, set up stats based on level and EV/IV settings, set nickname to default Pokemon name.
1015

1116
## Version 1.2.1
12-
- **Add github action to build**
17+
- **Add GitHub action to build**
1318

1419
## Version 1.2.0
15-
- **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 .
20+
- **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 .
1621
- **Bug Fixes:** Fix furi crash, Fixes #9 by @kbembedded .

pokemon_10px.png

7.19 KB
Loading

pokemon_app.c

Lines changed: 56 additions & 17 deletions
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,14 +1918,12 @@ 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]));
19211924
buf[i] = toupper(pokemon_fap->pokemon_table[pokemon_fap->curr_pokemon].name[i]);
19221925
}
1926+
FURI_LOG_D(TAG, "[app] Set default nickname");
19231927

19241928
if(dest != NULL) {
19251929
strncpy(dest, buf, n);
@@ -1972,6 +1976,8 @@ void pokemon_trade_block_recalculate_stats_from_level(PokemonFap* pokemon_fap) {
19721976

19731977
pkmn->level_again = level;
19741978
UINT32_TO_EXP(experience, pkmn->exp);
1979+
FURI_LOG_D(TAG, "[app] Set pkmn level %d", level);
1980+
FURI_LOG_D(TAG, "[app] Set pkmn exp %d", (int)experience);
19751981

19761982
/* Generate STATEXP */
19771983
switch(curr_stats) {
@@ -1988,6 +1994,7 @@ void pokemon_trade_block_recalculate_stats_from_level(PokemonFap* pokemon_fap) {
19881994
break;
19891995
}
19901996

1997+
FURI_LOG_D(TAG, "[app] EVs set to %d", stat);
19911998
stat = __builtin_bswap16(stat);
19921999

19932000
pkmn->hp_ev = stat;
@@ -2006,33 +2013,50 @@ void pokemon_trade_block_recalculate_stats_from_level(PokemonFap* pokemon_fap) {
20062013
((special_iv & 0x0f));
20072014
hp_iv = (pkmn->iv & 0xAA) >> 4;
20082015
}
2016+
FURI_LOG_D(
2017+
TAG,
2018+
"[app] atk_iv %d, def_iv %d, spd_iv %d, spc_iv %d, hp_iv %d",
2019+
atk_iv,
2020+
def_iv,
2021+
spd_iv,
2022+
special_iv,
2023+
hp_iv);
20092024

20102025
/* Calculate HP */
20112026
// https://bulbapedia.bulbagarden.net/wiki/Stat#Generations_I_and_II
20122027
stat = floor((((2 * (table->base_hp + hp_iv)) + floor(sqrt(pkmn->hp_ev) / 4)) * level) / 100) +
20132028
(level + 10);
2029+
FURI_LOG_D(TAG, "[app] HP set to %d", stat);
20142030
pkmn->hp = __builtin_bswap16(stat);
20152031
pkmn->max_hp = pkmn->hp;
20162032

20172033
/* Calculate ATK, DEF, SPD, SP */
2034+
/* TODO: these all use the same calculations, could put the stats in a sub-array and iterate
2035+
* through each element in order rather than having to repeat the code. IVs would also need
2036+
* to be in a similar array.
2037+
**/
20182038
// https://bulbapedia.bulbagarden.net/wiki/Stat#Generations_I_and_II
20192039
stat =
20202040
floor((((2 * (table->base_atk + atk_iv)) + floor(sqrt(pkmn->atk_ev) / 4)) * level) / 100) +
20212041
5;
2042+
FURI_LOG_D(TAG, "[app] ATK set to %d", stat);
20222043
pkmn->atk = __builtin_bswap16(stat);
20232044
stat =
20242045
floor((((2 * (table->base_def + def_iv)) + floor(sqrt(pkmn->def_ev) / 4)) * level) / 100) +
20252046
5;
2047+
FURI_LOG_D(TAG, "[app] DEF set to %d", stat);
20262048
pkmn->def = __builtin_bswap16(stat);
20272049
stat =
20282050
floor((((2 * (table->base_spd + spd_iv)) + floor(sqrt(pkmn->spd_ev) / 4)) * level) / 100) +
20292051
5;
2052+
FURI_LOG_D(TAG, "[app] SPD set to %d", stat);
20302053
pkmn->spd = __builtin_bswap16(stat);
20312054
stat = floor(
20322055
(((2 * (table->base_special + special_iv)) + floor(sqrt(pkmn->special_ev) / 4)) *
20332056
level) /
20342057
100) +
20352058
5;
2059+
FURI_LOG_D(TAG, "[app] SPC set to %d", stat);
20362060
pkmn->special = __builtin_bswap16(stat);
20372061
}
20382062

@@ -2045,15 +2069,24 @@ void pokemon_trade_block_recalculate(PokemonFap* pokemon_fap) {
20452069
/* Set current pokemon to the trade structure */
20462070
pkmn->index = table->index;
20472071
pokemon_fap->trade_block->party_members[0] = table->index;
2072+
FURI_LOG_D(TAG, "[app] Set %s in trade block", table->name);
20482073

20492074
/* Set current pokemon's moves to the trade structure */
20502075
for(i = 0; i < 4; i++) {
20512076
pkmn->move[i] = table->move[i];
2077+
FURI_LOG_D(
2078+
TAG,
2079+
"[app] Set %s in trade block",
2080+
pokemon_named_list_get_name_from_index(pokemon_fap->move_list, pkmn->move[i]));
20522081
}
20532082

20542083
/* Set current pokemon's types to the trade structure */
20552084
for(i = 0; i < 2; i++) {
20562085
pkmn->type[i] = table->type[i];
2086+
FURI_LOG_D(
2087+
TAG,
2088+
"[app] Set %s in trade block",
2089+
pokemon_named_list_get_name_from_index(pokemon_fap->type_list, pkmn->type[i]));
20572090
}
20582091

20592092
pokemon_trade_block_recalculate_stats_from_level(pokemon_fap);
@@ -2087,9 +2120,11 @@ static TradeBlock* trade_block_alloc(void) {
20872120
/* OT trainer ID# */
20882121
trade->party[0].ot_id = __builtin_bswap16(42069);
20892122

2090-
/* XXX: move pp isn't explicitly set up, should be fine */
2091-
/* XXX: catch/held isn't explicitly set up, should be okay for only Gen I support now */
2092-
/* 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+
*/
20932128

20942129
/* Set up initial level */
20952130
trade->party[0].level = 2;
@@ -2150,8 +2185,12 @@ PokemonFap* pokemon_alloc() {
21502185
pokemon_fap->view_dispatcher, AppViewSelectPokemon, pokemon_fap->select_view);
21512186

21522187
// Trade View
2153-
pokemon_fap->trade_view = trade_alloc(pokemon_fap);
2154-
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);
21552194

21562195
return pokemon_fap;
21572196
}
@@ -2163,8 +2202,8 @@ void free_app(PokemonFap* pokemon_fap) {
21632202
view_dispatcher_remove_view(pokemon_fap->view_dispatcher, AppViewSelectPokemon);
21642203
select_pokemon_free(pokemon_fap);
21652204

2166-
view_dispatcher_remove_view(pokemon_fap->view_dispatcher, AppViewTrade);
2167-
trade_free(pokemon_fap);
2205+
/* Also removes itself from the view_dispatcher */
2206+
trade_free(pokemon_fap->view_dispatcher, AppViewTrade, pokemon_fap->trade);
21682207

21692208
view_dispatcher_remove_view(pokemon_fap->view_dispatcher, AppViewMainMenu);
21702209

0 commit comments

Comments
 (0)