Skip to content

Commit

Permalink
Merge pull request #21 from kbembedded/trade-features
Browse files Browse the repository at this point in the history
Trade: Overhaul for features and fixes. Bump to 1.4
  • Loading branch information
kbembedded authored Nov 8, 2023
2 parents 788848a + 32493e4 commit 78c99b8
Show file tree
Hide file tree
Showing 22 changed files with 864 additions and 380 deletions.
12 changes: 7 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ It currently trades a Pokemon based on your choice of Pokemon, Level, Stats and

## Installation Directions

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

- 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.
- Copy the "pokemon" folder into the `/applications_user/pokemon` folder in your firmware.
Expand Down Expand Up @@ -87,7 +87,7 @@ These instructions assume that you are starting at the Flipper Zero desktop. Oth
<br />
</p>

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

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

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

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

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

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.

<p align='center'>
Expand Down Expand Up @@ -328,7 +330,7 @@ For each image, the color `#aaa` was transformed to `#fff` so that Flipper Zero
## 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
- 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)](<https://bulbapedia.bulbagarden.net/wiki/Pok%C3%A9mon_data_structure_(Generation_I)>)
Expand Down
19 changes: 10 additions & 9 deletions TODO.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,8 @@
- [x] Add view to allow the traded Pokemon's level to be chosen between 2 and 100
- [x] Add view to allow the traded Pokemon's hidden stats to be chosen (IV and EV) from some options
- [ ] Are there any better ways to present these options?
- [ ] Debug traded Pokemon level issue where after a battle the Pokemon's level drops (doesn't affect all traded Pokemon)
- [ ] 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)
- [x] Debug traded Pokemon level issue where after a battle the Pokemon's level drops (doesn't affect all traded Pokemon)
- [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)
- Moves
- [x] Add view to allow the traded Pokemon's moveset to be chosen (all 4 moves) allowing no move as an option
- [ ] Find a way to get faster scrolling through the move select submenu
Expand All @@ -24,23 +24,24 @@
- [x] Support setting pokemon type(s)
- [ ] Implement a save/revert to default workflow on the select types scene
- Trade
- [ ] Investigate Trade screens not always blinking
- [x] Investigate Trade screens not always blinking
- UI
- [ ] Find a way to line up submenu items so the main menu looks cleaner
- They currently _mostly_ line up thanks to some manual spacing, but tabs don't appear to be supported to force that alignment
- Alternatively may need to implement our own view to make this pretty
- Documentation
- [x] Add images for the level selection screen, stats selection screen, and move selection screens as per the original README
- Codebase
- [ ] Reimplement Logging calls
- [x] Reimplement Logging calls
- [ ] 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
- [ ] Consider using a single View in main app struct and only allocate a view as needed to reduce memory footprint

- Future Wants
- [ ] Trading to Gen II games with both Gen I and Gen II pokemon
- [ ] Trading to Gen II games with both Gen I and Gen II Pokemon
- [ ] Enable IR mystery gift usage in Gen II using Flipper
- [ ] Be able to set up mutiple pokemon to be able to trade more than one per trip to trade center
- [ ] Be able to trade back and forth for e.g. trading a pokemon that evolves only when traded
- [ ] Would Separateing out link cable states result in a cleaner API?
- [ ] Be able to set up multiple Pokemon to be able to trade more than one per trip to trade center
- [x] Be able to trade back and forth for e.g. trading a Pokemon that evolves only when traded
- [x] Would Separating out link cable states result in a cleaner API?
- [ ] Implement some simple logic to be able to "battle" the Flipper?
- [ ] 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.
- [ ] 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.
- [ ] Ability to save Pokemon to SD card. Either created on, or traded to, the Flipper app.
2 changes: 1 addition & 1 deletion application.fam
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ App(
entry_point="pokemon_app",
requires=["gui"],
stack_size=2 * 1024,
fap_version=[1,3],
fap_version=[1,4],
fap_category="GPIO",
fap_icon="pokemon_10px.png",
fap_icon_assets="assets",
Expand Down
17 changes: 11 additions & 6 deletions changelog.md
Original file line number Diff line number Diff line change
@@ -1,16 +1,21 @@
# Changelog - Patch Notes

## Version 1.2.3
## Version 1.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.
- **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.
- **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

## Version 1.3
- **Refactor and UI cleanup:** Convert to Flipper Zero UI modules for simpler interface, reduce binary size.
- **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.
- **Bug Fixes:** Fix strange issue with exp gain causing traded pokemon to de-level and result in incorrect stats.
- **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.
- **Bug Fixes:** Fix strange issue with exp gain causing traded Pokemon to de-level and result in incorrect stats.

## Version 1.2.2
- **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.
- **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.

## Version 1.2.1
- **Add github action to build**
- **Add GitHub action to build**

## 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 .
- **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 .
Binary file modified pokemon_10px.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
73 changes: 56 additions & 17 deletions pokemon_app.c
Original file line number Diff line number Diff line change
Expand Up @@ -1863,17 +1863,23 @@ 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;

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

/* XXX: Would be faster to do something like this, but, can't easily do
* that using the current pointers. Might be able to clean this up later?
*/
//return sizeof(type_list)/sizeof(type_list[0]);
}

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

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

/* XXX: This will return the first entry in the case index is not matched,
/* This will return the first entry in the case index is not matched,
* this could be confusing/problematic at runtime.
*/
return list[0].name;
Expand All @@ -1912,14 +1918,12 @@ void pokemon_trade_block_set_default_name(char* dest, PokemonFap* pokemon_fap, s
/* Walk through the default name, toupper() each character, encode it, and
* then write that to the same position in the trade_block.
*/
/* XXX: The limit of this is hard-coded to a length of 11 at most. This may
* be a problem down the road!
*/
for(i = 0; i < 11; i++) {
pokemon_fap->trade_block->nickname[0].str[i] = pokemon_char_to_encoded(
toupper(pokemon_fap->pokemon_table[pokemon_fap->curr_pokemon].name[i]));
buf[i] = toupper(pokemon_fap->pokemon_table[pokemon_fap->curr_pokemon].name[i]);
}
FURI_LOG_D(TAG, "[app] Set default nickname");

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

pkmn->level_again = level;
UINT32_TO_EXP(experience, pkmn->exp);
FURI_LOG_D(TAG, "[app] Set pkmn level %d", level);
FURI_LOG_D(TAG, "[app] Set pkmn exp %d", (int)experience);

/* Generate STATEXP */
switch(curr_stats) {
Expand All @@ -1988,6 +1994,7 @@ void pokemon_trade_block_recalculate_stats_from_level(PokemonFap* pokemon_fap) {
break;
}

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

pkmn->hp_ev = stat;
Expand All @@ -2006,33 +2013,50 @@ void pokemon_trade_block_recalculate_stats_from_level(PokemonFap* pokemon_fap) {
((special_iv & 0x0f));
hp_iv = (pkmn->iv & 0xAA) >> 4;
}
FURI_LOG_D(
TAG,
"[app] atk_iv %d, def_iv %d, spd_iv %d, spc_iv %d, hp_iv %d",
atk_iv,
def_iv,
spd_iv,
special_iv,
hp_iv);

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

/* Calculate ATK, DEF, SPD, SP */
/* TODO: these all use the same calculations, could put the stats in a sub-array and iterate
* through each element in order rather than having to repeat the code. IVs would also need
* to be in a similar array.
**/
// https://bulbapedia.bulbagarden.net/wiki/Stat#Generations_I_and_II
stat =
floor((((2 * (table->base_atk + atk_iv)) + floor(sqrt(pkmn->atk_ev) / 4)) * level) / 100) +
5;
FURI_LOG_D(TAG, "[app] ATK set to %d", stat);
pkmn->atk = __builtin_bswap16(stat);
stat =
floor((((2 * (table->base_def + def_iv)) + floor(sqrt(pkmn->def_ev) / 4)) * level) / 100) +
5;
FURI_LOG_D(TAG, "[app] DEF set to %d", stat);
pkmn->def = __builtin_bswap16(stat);
stat =
floor((((2 * (table->base_spd + spd_iv)) + floor(sqrt(pkmn->spd_ev) / 4)) * level) / 100) +
5;
FURI_LOG_D(TAG, "[app] SPD set to %d", stat);
pkmn->spd = __builtin_bswap16(stat);
stat = floor(
(((2 * (table->base_special + special_iv)) + floor(sqrt(pkmn->special_ev) / 4)) *
level) /
100) +
5;
FURI_LOG_D(TAG, "[app] SPC set to %d", stat);
pkmn->special = __builtin_bswap16(stat);
}

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

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

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

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

/* XXX: move pp isn't explicitly set up, should be fine */
/* XXX: catch/held isn't explicitly set up, should be okay for only Gen I support now */
/* XXX: Status condition isn't explicity let up, would you ever want to? */
/* Notes:
* Move pp isn't explicitly set up, should be fine
* Catch/held isn't explicitly set up, should be okay for only Gen I support now
* Status condition isn't explicity let up, would you ever want to?
*/

/* Set up initial level */
trade->party[0].level = 2;
Expand Down Expand Up @@ -2150,8 +2185,12 @@ 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);
/* Allocates its own view and adds it to the main view_dispatcher */
pokemon_fap->trade = trade_alloc(
pokemon_fap->trade_block,
pokemon_fap->pokemon_table,
pokemon_fap->view_dispatcher,
AppViewTrade);

return pokemon_fap;
}
Expand All @@ -2163,8 +2202,8 @@ void free_app(PokemonFap* pokemon_fap) {
view_dispatcher_remove_view(pokemon_fap->view_dispatcher, AppViewSelectPokemon);
select_pokemon_free(pokemon_fap);

view_dispatcher_remove_view(pokemon_fap->view_dispatcher, AppViewTrade);
trade_free(pokemon_fap);
/* Also removes itself from the view_dispatcher */
trade_free(pokemon_fap->view_dispatcher, AppViewTrade, pokemon_fap->trade);

view_dispatcher_remove_view(pokemon_fap->view_dispatcher, AppViewMainMenu);

Expand Down
Loading

0 comments on commit 78c99b8

Please sign in to comment.