diff --git a/asm/macros/event.inc b/asm/macros/event.inc index e806a7d54f..ca937e0c0d 100644 --- a/asm/macros/event.inc +++ b/asm/macros/event.inc @@ -2689,3 +2689,21 @@ callnative NativeVsSeekerRematchId, requests_effects=1 .2byte \rematchId .endm + + @ Sets the move relearner state + .macro setmoverelearnerstate state:req + callnative ScrCmd_setmoverelearnerstate, requests_effects=1 + .2byte \state + .endm + + @ Retrieves the move relearner state and stores it in the specified var + .macro getmoverelearnerstate varId:req + callnative ScrCmd_getmoverelearnerstate, requests_effects=1 + .4byte \varId + .endm + + @ Execute script if bag has TMs and/or HMs + .macro istmrelearneractive destination:req + callnative ScrCmd_istmrelearneractive, requests_effects=1 + .4byte \destination + .endm diff --git a/data/event_scripts.s b/data/event_scripts.s index 3603764450..cf37f6730c 100644 --- a/data/event_scripts.s +++ b/data/event_scripts.s @@ -43,6 +43,7 @@ #include "constants/maps.h" #include "constants/mauville_old_man.h" #include "constants/metatile_labels.h" +#include "constants/move_relearner.h" #include "constants/moves.h" #include "constants/party_menu.h" #include "constants/pokedex.h" @@ -696,6 +697,7 @@ EventScript_SetBrineyLocation_Route109:: .include "data/scripts/obtain_item.inc" .include "data/scripts/record_mix.inc" .include "data/scripts/pc.inc" + .include "data/scripts/move_relearner.inc" @ scripts/notices.inc? signs.inc? See comment about text/notices.inc Common_EventScript_ShowPokemartSign:: @@ -881,6 +883,7 @@ Common_EventScript_PlayerHandedOverTheItem:: .include "data/text/pkmn_center_nurse.inc" .include "data/text/mart_clerk.inc" .include "data/text/obtain_item.inc" + .include "data/text/move_relearner.inc" @ The below and surf.inc could be split into some text/notices.inc gText_PokemartSign:: diff --git a/data/maps/FallarborTown_MoveRelearnersHouse/scripts.inc b/data/maps/FallarborTown_MoveRelearnersHouse/scripts.inc index 8dd70b9b04..ea58d275df 100644 --- a/data/maps/FallarborTown_MoveRelearnersHouse/scripts.inc +++ b/data/maps/FallarborTown_MoveRelearnersHouse/scripts.inc @@ -22,6 +22,7 @@ FallarborTown_MoveRelearnersHouse_EventScript_AskTeachMove:: FallarborTown_MoveRelearnersHouse_EventScript_ChooseMon:: msgbox FallarborTown_MoveRelearnersHouse_Text_TutorWhichMon, MSGBOX_DEFAULT + setmoverelearnerstate MOVE_RELEARNER_LEVEL_UP_MOVES @ Specifically supposed to teach level up moves special ChooseMonForMoveRelearner waitstate goto_if_eq VAR_0x8004, PARTY_NOTHING_CHOSEN, FallarborTown_MoveRelearnersHouse_EventScript_ComeBackWithHeartScale diff --git a/data/scripts/move_relearner.inc b/data/scripts/move_relearner.inc new file mode 100644 index 0000000000..15c80733df --- /dev/null +++ b/data/scripts/move_relearner.inc @@ -0,0 +1,105 @@ +Common_EventScript_MoveRelearner:: + lockall + faceplayer + message MoveRelearner_Text_WouldLearnNewMoves + waitmessage + goto Common_EventScript_MoveRelearnerDynMultiChoice + end + +Common_EventScript_MoveRelearnerDynMultiChoice:: + dynmultipush MoveRelearner_Text_LevelUpMoves, 0 +.if P_ENABLE_MOVE_RELEARNERS == TRUE + dynmultipush MoveRelearner_Text_EggMoves, 1 + dynmultipush MoveRelearner_Text_TMMoves, 2 + dynmultipush MoveRelearner_Text_TutormoveMoves, 3 +.else + call_if_set P_FLAG_EGG_MOVES, MoveRelearner_EventScript_PushEggMoves + istmrelearneractive MoveRelearner_EventScript_PushTMMoves + call_if_set P_FLAG_TUTOR_MOVES, MoveRelearner_EventScript_PushTutorMoves +.endif @ P_ENABLE_MOVE_RELEARNERS + dynmultipush MoveRelearner_Text_SeeYa, 4 + dynmultistack 0, 0, FALSE, 5, 0, 0, DYN_MULTICHOICE_CB_NONE + closemessage + switch VAR_RESULT + case 0, MoveRelearner_EventScript_TeachLevelUpMoves + case 1, MoveRelearner_EventScript_TeachEggMoves + case 2, MoveRelearner_EventScript_TeachTMMoves + case 3, MoveRelearner_EventScript_TeachTutorMoves + case 4, MoveRelearner_EventScript_PleaseComeAgain +MoveRelearner_EventScript_PleaseComeAgain: + msgbox MoveRelearner_Text_ThankYouComeAgain, MSGBOX_DEFAULT + releaseall + end + +MoveRelearner_EventScript_PushEggMoves: + dynmultipush MoveRelearner_Text_EggMoves, 1 + return + +MoveRelearner_EventScript_PushTMMoves: + dynmultipush MoveRelearner_Text_TMMoves, 2 + return + +MoveRelearner_EventScript_PushTutorMoves: + dynmultipush MoveRelearner_Text_TutormoveMoves, 3 + return + +MoveRelearner_EventScript_TeachLevelUpMoves: + setmoverelearnerstate MOVE_RELEARNER_LEVEL_UP_MOVES + bufferstring STR_VAR_3, MoveRelearner_Text_LevelUpMoveLWR + goto MoveRelearner_EventScript_TeachMove + end + +MoveRelearner_EventScript_TeachEggMoves: + setmoverelearnerstate MOVE_RELEARNER_EGG_MOVES + bufferstring STR_VAR_3, MoveRelearner_Text_EggMoveLWR + goto MoveRelearner_EventScript_TeachMove + end + +MoveRelearner_EventScript_TeachTMMoves: + setmoverelearnerstate MOVE_RELEARNER_TM_MOVES + bufferstring STR_VAR_3, MoveRelearner_Text_TMMoveLWR + goto MoveRelearner_EventScript_TeachMove + end + +MoveRelearner_EventScript_TeachTutorMoves: + setmoverelearnerstate MOVE_RELEARNER_TUTOR_MOVES + bufferstring STR_VAR_3, MoveRelearner_Text_TutorMoveLWR + goto MoveRelearner_EventScript_TeachMove + end + +MoveRelearner_EventScript_TeachMove:: + getpartysize + goto_if_eq VAR_RESULT, 0, MoveRelearner_EventScript_NoPkmn + msgbox MoveRelearner_Text_ChoosePkmn, MSGBOX_DEFAULT + special ChooseMonForMoveRelearner + waitstate + call_if_eq VAR_0x8004, PARTY_NOTHING_CHOSEN, MoveRelearner_EventScript_AnythingElse + special IsSelectedMonEgg + call_if_eq VAR_RESULT, YES, MoveRelearner_EventScript_CantTeachMoveToEgg + call_if_eq VAR_0x8005, NO, MoveRelearner_EventScript_CantTeachMoveToPkmn + msgbox MoveRelearner_Text_WhichXmoveShouldTeach, MSGBOX_DEFAULT + special TeachMoveRelearnerMove + waitstate + goto MoveRelearner_EventScript_AnythingElse + end + +MoveRelearner_EventScript_NoPkmn: + msgbox MoveRelearner_Text_HaveNoPkmn, MSGBOX_AUTOCLOSE + releaseall + end + +MoveRelearner_EventScript_CantTeachMoveToEgg: + msgbox MoveRelearner_Text_CantTeachMoveToEgg, MSGBOX_AUTOCLOSE + goto MoveRelearner_EventScript_AnythingElse + end + +MoveRelearner_EventScript_CantTeachMoveToPkmn: + msgbox MoveRelearner_Text_CantTeachMoveToPkmn, MSGBOX_AUTOCLOSE + goto MoveRelearner_EventScript_AnythingElse + end + +MoveRelearner_EventScript_AnythingElse:: + message MoveRelearner_Text_AnythingElse + waitmessage + goto Common_EventScript_MoveRelearnerDynMultiChoice + end diff --git a/data/text/move_relearner.inc b/data/text/move_relearner.inc new file mode 100644 index 0000000000..b9400fd62b --- /dev/null +++ b/data/text/move_relearner.inc @@ -0,0 +1,55 @@ +MoveRelearner_Text_WouldLearnNewMoves: + .string "Hi, I'm the Move Relearner.\n" + .string "Would you like to learn new moves?$" + +MoveRelearner_Text_LevelUpMoves: + .string "Level Up Moves$" + +MoveRelearner_Text_EggMoves: + .string "Egg Moves$" + +MoveRelearner_Text_TMMoves: + .string "TM Moves$" + +MoveRelearner_Text_TutormoveMoves: + .string "Tutor Moves$" + +MoveRelearner_Text_SeeYa: + .string "See ya!$" + +MoveRelearner_Text_AnythingElse: + .string "Is there anything else I may do for you?$" + +MoveRelearner_Text_ChoosePkmn: + .string "Please choose your Pokémon.$" + +MoveRelearner_Text_HaveNoPkmn: + .string "You have no Pokémon.$" + +MoveRelearner_Text_CantTeachMoveToEgg: + .string "Sorry…\n" + .string "But an Egg can't learn moves.$" + +MoveRelearner_Text_CantTeachMoveToPkmn: + .string "Sorry…\p" + .string "It doesn't appear as if I have any move\n" + .string "I can teach that Pokémon.$" + +MoveRelearner_Text_LevelUpMoveLWR:: + .string "level up move$" + +MoveRelearner_Text_EggMoveLWR:: + .string "egg move$" + +MoveRelearner_Text_TMMoveLWR:: + .string "TM move$" + +MoveRelearner_Text_TutorMoveLWR:: + .string "tutor move$" + +MoveRelearner_Text_WhichXmoveShouldTeach: + .string "Which {STR_VAR_3} should I teach?$" + +MoveRelearner_Text_ThankYouComeAgain: + .string "Thank you for using our services.\n" + .string "Please come again!$" diff --git a/include/config/summary_screen.h b/include/config/summary_screen.h index 07bae63533..e76cd478b6 100644 --- a/include/config/summary_screen.h +++ b/include/config/summary_screen.h @@ -2,16 +2,16 @@ #define GUARD_CONFIG_SUMMARY_SCREEN_H // Settings -#define P_SUMMARY_SCREEN_MOVE_RELEARNER TRUE // If TRUE, shows an option for Pokémon to relearn moves on the summary screen moves page. -#define P_SUMMARY_SCREEN_NATURE_COLORS TRUE // If TRUE, nature-based stat boosts and reductions will be red and blue in the summary screen. -#define P_SUMMARY_MOVE_RELEARNER_FULL_PP TRUE // If TRUE, the move relearner in the summary screen restores relearned moves' PP to full. +#define P_SUMMARY_SCREEN_NATURE_COLORS TRUE // If TRUE, nature-based stat boosts and reductions will be red and blue in the summary screen. #define P_SUMMARY_SCREEN_RENAME TRUE // If TRUE, an option to change Pokémon nicknames replaces the cancel prompt on the summary screen info page. + +// IV/EV settings #define P_SUMMARY_SCREEN_IV_EV_INFO FALSE // If TRUE, will allow player to cycle through the Stats, IVs, and EVs in the summary screen skills page. #define P_SUMMARY_SCREEN_IV_EV_BOX_ONLY FALSE // If TRUE, will allow player to cycle through the Stats, IVs, and EVs in the summary screen skills page, but only in the PC storage box. #define P_SUMMARY_SCREEN_IV_HYPERTRAIN TRUE // If TRUE, stats that have been hyper trained will show as 31/S when viewing them in the summary screen #define P_SUMMARY_SCREEN_IV_EV_TILESET FALSE // If TRUE, loads an alternate tileset to allow changing the "STATS" label in the summary screen skills page. Note: if it's still loading the alternate tileset after changing this and recompiling, you may need a `make clean` before compilation. #define P_SUMMARY_SCREEN_IV_EV_VALUES FALSE // If TRUE, will show the actual IV value instead of the letter grade. -/* +/* LETTER GRADE GUIDE: F = 0 @@ -26,7 +26,32 @@ Info taken from https://bulbapedia.bulbagarden.net/wiki/Stats_judge. #define P_SUMMARY_SCREEN_IV_ONLY FALSE // If TRUE, will only show IV info in the summary screen. #define P_SUMMARY_SCREEN_EV_ONLY FALSE // If TRUE, will only show EV info in the summary screen. -// Flags +// IV/EV flags #define P_FLAG_SUMMARY_SCREEN_IV_EV_INFO 0 // If this flag is set, will allow player to cycle through the Stats, IVs, and EVs in the summary screen skills page. Note: if P_SUMMARY_SCREEN_IV_EV_INFO is TRUE, this flag does nothing. +// Move Relearner settings +#define P_ENABLE_MOVE_RELEARNERS FALSE // If TRUE, it enables move relearners for egg, TM and tutor. (see below for specific configs /flags) +#define P_SORT_MOVES FALSE // If TRUE, sorts all moves alphabetically in the relearner's list. + +// Level up Relearner +#define P_PRE_EVO_MOVES FALSE // If TRUE, it enables the Pokémon to learn moves from it's pre evolution. +#define P_ENABLE_ALL_LEVEL_UP_MOVES FALSE // If TRUE, it enables the Pokémon to learn all level up moves, regardless of its level. + +// TM Relearner +#define P_TM_MOVES_RELEARNER TRUE // If TRUE, enables machine move relearner. +#define P_ENABLE_ALL_TM_MOVES FALSE // If TRUE, it enables the Pokémon to learn all TMs its compatible with, regardless of it being in the bag. + +// Relearner flags - Redundant if P_ENABLE_MOVE_RELEARNERS is TRUE, but still added here incase you don't want all relearners unlocked at the same time. +// To use the following features in scripting, replace the 0s with the flag ID you're assigning it to. +// Eg: Replace with FLAG_UNUSED_0x264 so you can use that flag to toggle the feature. +#define P_FLAG_EGG_MOVES 0 // If this flag is set, enables egg move relearner. +#define P_FLAG_TUTOR_MOVES 0 // If this flag is set, enables tutor move relearner. + +// Move Relearner summary screen +#define P_SUMMARY_SCREEN_MOVE_RELEARNER TRUE // If TRUE, shows an option for Pokémon to relearn moves on the summary screen moves page. +#define P_SUMMARY_MOVE_RELEARNER_FULL_PP TRUE // If TRUE, the move relearner in the summary screen restores relearned moves' PP to full. + +// Move Relearner party menu +#define P_PARTY_MOVE_RELEARNER FALSE // If TRUE, it enables the move relearner in the party menu. + #endif // GUARD_CONFIG_SUMMARY_SCREEN_H diff --git a/include/constants/move_relearner.h b/include/constants/move_relearner.h new file mode 100644 index 0000000000..0229cb7f17 --- /dev/null +++ b/include/constants/move_relearner.h @@ -0,0 +1,29 @@ +#ifndef GUARD_CONSTANTS_MOVE_RELEARNER_H +#define GUARD_CONSTANTS_MOVE_RELEARNER_H + +// Max number of moves shown by the move relearner. +// Increased from 25 to 60 so Mew can display all TMs/HMs. +// If you plan on adding more TMs, increase this number too. +#define MAX_RELEARNER_MOVES 60 + +// Move Relearner menu change constants +enum MoveRelearnerStates +{ + MOVE_RELEARNER_LEVEL_UP_MOVES, + MOVE_RELEARNER_EGG_MOVES, + MOVE_RELEARNER_TM_MOVES, + MOVE_RELEARNER_TUTOR_MOVES, + MOVE_RELEARNER_COUNT, +}; + +enum RelearnMode +{ + RELEARN_MODE_NONE = 0, + RELEARN_MODE_SCRIPT = 1, // Relearning moves through an event script + // These two must stay 2 and 3, they are tied to the summary screen pages + RELEARN_MODE_PSS_PAGE_BATTLE_MOVES = 2, // Relearning moves through the summary screen's battle moves page + RELEARN_MODE_PSS_PAGE_CONTEST_MOVES = 3, // Relearning moves through the summary screen's contest moves page (defaults to contest page on relearner screen) + RELEARN_MODE_PARTY_MENU = 4, // Relearning moves through the party menu's moves submenu +}; + +#endif // GUARD_CONSTANTS_MOVE_RELEARNER_H diff --git a/include/constants/pokemon.h b/include/constants/pokemon.h index b3ceb884cf..59bf27d823 100644 --- a/include/constants/pokemon.h +++ b/include/constants/pokemon.h @@ -174,7 +174,6 @@ enum __attribute__((packed)) Stat #define LEVEL_UP_MOVE_END 0xFFFF #define MAX_LEVEL_UP_MOVES 20 -#define MAX_RELEARNER_MOVES max(MAX_LEVEL_UP_MOVES, 25) #define MON_MALE 0x00 #define MON_FEMALE 0xFE diff --git a/include/item.h b/include/item.h index fa52b4adb5..1e0728d38e 100644 --- a/include/item.h +++ b/include/item.h @@ -180,6 +180,8 @@ static inline u16 GetTMHMMoveId(enum TMHMIndex index) return gTMHMItemMoveIds[index].moveId; } +enum TMHMItemId GetTMHMItemIdFromMoveId(u16 move); + void BagPocket_SetSlotData(struct BagPocket *pocket, u32 pocketPos, struct ItemSlot newSlot); struct ItemSlot BagPocket_GetSlotData(struct BagPocket *pocket, u32 pocketPos); diff --git a/include/move_relearner.h b/include/move_relearner.h index 9aab3974df..1d22d82a79 100644 --- a/include/move_relearner.h +++ b/include/move_relearner.h @@ -1,11 +1,14 @@ #ifndef GUARD_MOVE_RELEARNER_H #define GUARD_MOVE_RELEARNER_H +#include "constants/move_relearner.h" + void TeachMoveRelearnerMove(void); void MoveRelearnerShowHideHearts(s32 move); void MoveRelearnerShowHideCategoryIcon(s32); void CB2_InitLearnMove(void); -extern u8 gOriginSummaryScreenPage; +extern enum MoveRelearnerStates gMoveRelearnerState; +extern enum RelearnMode gRelearnMode; #endif //GUARD_MOVE_RELEARNER_H diff --git a/include/party_menu.h b/include/party_menu.h index 38ba5202b5..acae4e0c0b 100644 --- a/include/party_menu.h +++ b/include/party_menu.h @@ -96,6 +96,7 @@ u8 GetPartyIdFromBattlePartyId(u8 battlePartyId); void ShowPartyMenuToShowcaseMultiBattleParty(void); void ChooseMonForDaycare(void); bool8 CB2_FadeFromPartyMenu(void); +void CB2_ReturnToPartyMenuFromSummaryScreen(void); void ChooseContestMon(void); void ChoosePartyMon(void); void ChooseMonForMoveRelearner(void); diff --git a/include/pokemon.h b/include/pokemon.h index 215600be64..9293e9b26a 100644 --- a/include/pokemon.h +++ b/include/pokemon.h @@ -838,9 +838,15 @@ void UpdatePartyPokerusTime(u16 days); void PartySpreadPokerus(struct Pokemon *party); bool8 TryIncrementMonLevel(struct Pokemon *mon); u8 CanLearnTeachableMove(u16 species, u16 move); -u8 GetMoveRelearnerMoves(struct Pokemon *mon, u16 *moves); +u8 GetRelearnerLevelUpMoves(struct Pokemon *mon, u16 *moves); +u8 GetRelearnerEggMoves(struct Pokemon *mon, u16 *moves); +u8 GetRelearnerTMMoves(struct Pokemon *mon, u16 *moves); +u8 GetRelearnerTutorMoves(struct Pokemon *mon, u16 *moves); +u8 GetNumberOfLevelUpMoves(struct Pokemon *mon); +u8 GetNumberOfEggMoves(struct Pokemon *mon); +u8 GetNumberOfTMMoves(struct Pokemon *mon); +u8 GetNumberOfTutorMoves(struct Pokemon *mon); u8 GetLevelUpMovesBySpecies(u16 species, u16 *moves); -u8 GetNumberOfRelearnableMoves(struct Pokemon *mon); u16 SpeciesToPokedexNum(u16 species); bool32 IsSpeciesInHoennDex(u16 species); u16 GetBattleBGM(void); diff --git a/include/pokemon_summary_screen.h b/include/pokemon_summary_screen.h index 3aa8460a17..db4c0e89ad 100755 --- a/include/pokemon_summary_screen.h +++ b/include/pokemon_summary_screen.h @@ -3,6 +3,7 @@ #include "main.h" #include "config/summary_screen.h" +#include "constants/move_relearner.h" extern u8 gLastViewedMonIndex; @@ -13,11 +14,12 @@ extern const struct SpritePalette gSpritePal_CategoryIcons; extern const struct SpriteTemplate gSpriteTemplate_CategoryIcons; extern MainCallback gInitialSummaryScreenCallback; -void ShowPokemonSummaryScreen(u8 mode, void *mons, u8 monIndex, u8 maxMonIndex, void (*callback)(void)); -void ShowSelectMovePokemonSummaryScreen(struct Pokemon *mons, u8 monIndex, u8 maxMonIndex, void (*callback)(void), u16 newMove); -void ShowPokemonSummaryScreenHandleDeoxys(u8 mode, struct BoxPokemon *mons, u8 monIndex, u8 maxMonIndex, void (*callback)(void)); -u8 GetMoveSlotToReplace(void); -void SummaryScreen_SetAnimDelayTaskId(u8 taskId); +enum IncrDecrUpdateValues +{ + TRY_SET_UPDATE, + TRY_INCREMENT, + TRY_DECREMENT, +}; // The Pokémon Summary Screen can operate in different modes. Certain features, // such as move re-ordering, are available in the different modes. @@ -48,4 +50,14 @@ enum PokemonSummarySkillsMode SUMMARY_SKILLS_MODE_EVS, }; +void ShowPokemonSummaryScreen(u8 mode, void *mons, u8 monIndex, u8 maxMonIndex, void (*callback)(void)); +void ShowSelectMovePokemonSummaryScreen(struct Pokemon *mons, u8 monIndex, u8 maxMonIndex, void (*callback)(void), u16 newMove); +void ShowPokemonSummaryScreenHandleDeoxys(u8 mode, struct BoxPokemon *mons, u8 monIndex, u8 maxMonIndex, void (*callback)(void)); +u8 GetMoveSlotToReplace(void); +void SummaryScreen_SetAnimDelayTaskId(u8 taskId); +void ShowRelearnPrompt(void); +void TryUpdateRelearnType(enum IncrDecrUpdateValues delta); +u32 GetCurrentRelearnMovesCount(void); +u32 GetRelearnMovesCount(enum MoveRelearnerStates state); + #endif // GUARD_POKEMON_SUMMARY_SCREEN_H diff --git a/include/strings.h b/include/strings.h index 7220214357..7a2a80bea4 100644 --- a/include/strings.h +++ b/include/strings.h @@ -2424,8 +2424,17 @@ extern const u8 gText_PlayerRegroupCenter[]; extern const u8 gText_PlayerRegroupHome[]; extern const u8 gText_Relearn[]; // move relearner from summary screen +extern const u8 gText_Relearn_LevelUp[]; +extern const u8 gText_Relearn_Egg[]; +extern const u8 gText_Relearn_TM[]; +extern const u8 gText_Relearn_Tutor[]; extern const u8 gText_Rename[]; // change nickname from summary screen +extern const u8 MoveRelearner_Text_LevelUpMoveLWR[]; +extern const u8 MoveRelearner_Text_EggMoveLWR[]; +extern const u8 MoveRelearner_Text_TMMoveLWR[]; +extern const u8 MoveRelearner_Text_TutorMoveLWR[]; + // Switch Caught Mon into Party extern const u8 gText_CannotSendMonToBoxHM[]; extern const u8 gText_CannotSendMonToBoxActive[]; diff --git a/src/data/party_menu.h b/src/data/party_menu.h index 655264f02d..cce9c7036c 100644 --- a/src/data/party_menu.h +++ b/src/data/party_menu.h @@ -118,6 +118,7 @@ static const u8 sFontColorTable[][3] = {TEXT_COLOR_WHITE, TEXT_COLOR_DARK_GRAY, TEXT_COLOR_LIGHT_GRAY}, // Selection actions {TEXT_COLOR_WHITE, TEXT_COLOR_BLUE, TEXT_COLOR_LIGHT_BLUE}, // Field moves {TEXT_COLOR_TRANSPARENT, TEXT_COLOR_WHITE, TEXT_COLOR_DARK_GRAY}, // Unused + {TEXT_COLOR_WHITE, TEXT_COLOR_RED, TEXT_COLOR_LIGHT_RED}, // Move relearner }; static const struct WindowTemplate sSinglePartyMenuWindowTemplate[] = @@ -717,6 +718,11 @@ struct [MENU_TRADE1] = {sText_Trade4, CursorCb_Trade1}, [MENU_TRADE2] = {sText_Trade4, CursorCb_Trade2}, [MENU_TOSS] = {gMenuText_Toss, CursorCb_Toss}, + [MENU_LEVEL_UP_MOVES] = {COMPOUND_STRING("LEVEL MOVES"), CursorCb_ChangeLevelUpMoves}, + [MENU_EGG_MOVES] = {COMPOUND_STRING("EGG MOVES"), CursorCb_ChangeEggMoves}, + [MENU_TM_MOVES] = {COMPOUND_STRING("TM MOVES"), CursorCb_ChangeTMMoves}, + [MENU_TUTOR_MOVES] = {COMPOUND_STRING("TUTOR MOVES"), CursorCb_ChangeTutorMoves}, + [MENU_SUB_MOVES] = {COMPOUND_STRING("LEARN MOVES"), CursorCb_LearnMovesSubMenu}, [MENU_CATALOG_BULB] = {COMPOUND_STRING("Light bulb"), CursorCb_CatalogBulb}, [MENU_CATALOG_OVEN] = {COMPOUND_STRING("Microwave oven"), CursorCb_CatalogOven}, [MENU_CATALOG_WASHING] = {COMPOUND_STRING("Washing machine"), CursorCb_CatalogWashing}, diff --git a/src/debug.c b/src/debug.c index 8fde81637f..4d3e5cd39f 100644 --- a/src/debug.c +++ b/src/debug.c @@ -386,7 +386,7 @@ extern const u8 Debug_EventScript_FakeRTCNotEnabled[]; extern const u8 Debug_BerryPestsDisabled[]; extern const u8 Debug_BerryWeedsDisabled[]; -extern const u8 FallarborTown_MoveRelearnersHouse_EventScript_ChooseMon[]; +extern const u8 Common_EventScript_MoveRelearner[]; #include "data/map_group_count.h" @@ -577,7 +577,7 @@ static const struct DebugMenuOption sDebugMenu_Actions_PCBag[] = static const struct DebugMenuOption sDebugMenu_Actions_Party[] = { - { COMPOUND_STRING("Move Reminder"), DebugAction_ExecuteScript, FallarborTown_MoveRelearnersHouse_EventScript_ChooseMon }, + { COMPOUND_STRING("Move Relearner"), DebugAction_ExecuteScript, Common_EventScript_MoveRelearner }, { COMPOUND_STRING("Hatch an Egg"), DebugAction_ExecuteScript, Debug_HatchAnEgg }, { COMPOUND_STRING("Heal party"), DebugAction_Party_HealParty }, { COMPOUND_STRING("Inflict Status1"), DebugAction_ExecuteScript, Debug_EventScript_InflictStatus1 }, diff --git a/src/item.c b/src/item.c index 155909e547..b0dc8a7b2e 100644 --- a/src/item.c +++ b/src/item.c @@ -88,6 +88,19 @@ static inline void NONNULL BagPocket_SetSlotDataPC(struct BagPocket *pocket, u32 pocket->itemSlots[pocketPos].quantity = newSlot.quantity; } +enum TMHMItemId GetTMHMItemIdFromMoveId(u16 move) +{ + if (move == MOVE_NONE) + return 0; + + for (u16 i = 0; i < NUM_ALL_MACHINES; i++) + { + if (GetTMHMMoveId(i + 1) == move) + return GetTMHMItemId(i + 1); + } + return 0; +} + struct ItemSlot NONNULL BagPocket_GetSlotData(struct BagPocket *pocket, u32 pocketPos) { switch (pocket->id) diff --git a/src/menu_specialized.c b/src/menu_specialized.c index df4233fd14..e9818b5a12 100644 --- a/src/menu_specialized.c +++ b/src/menu_specialized.c @@ -4,6 +4,7 @@ #include "contest_effect.h" #include "data.h" #include "decompress.h" +#include "event_data.h" #include "gpu_regs.h" #include "graphics.h" #include "menu.h" diff --git a/src/move_relearner.c b/src/move_relearner.c index ee700beb44..9aafbb4b59 100644 --- a/src/move_relearner.c +++ b/src/move_relearner.c @@ -9,6 +9,7 @@ #include "event_data.h" #include "field_screen_effect.h" #include "gpu_regs.h" +#include "item.h" #include "move_relearner.h" #include "list_menu.h" #include "malloc.h" @@ -134,7 +135,8 @@ #define MENU_STATE_CONFIRM_DELETE_OLD_MOVE 18 #define MENU_STATE_PRINT_WHICH_MOVE_PROMPT 19 #define MENU_STATE_SHOW_MOVE_SUMMARY_SCREEN 20 -// States 21, 22, and 23 are skipped. +#define MENU_STATE_RETURN_TO_PARTY_MENU 21 +// States 22 and 23 are skipped. #define MENU_STATE_PRINT_STOP_TEACHING 24 #define MENU_STATE_WAIT_FOR_STOP_TEACHING 25 #define MENU_STATE_CONFIRM_STOP_TEACHING 26 @@ -183,7 +185,8 @@ static EWRAM_DATA struct { bool8 showContestInfo; } sMoveRelearnerMenuState = {0}; -EWRAM_DATA u8 gOriginSummaryScreenPage = 0; // indicates summary screen page that the move relearner was opened from (if opened from PSS) +EWRAM_DATA enum MoveRelearnerStates gMoveRelearnerState = MOVE_RELEARNER_LEVEL_UP_MOVES; +EWRAM_DATA enum RelearnMode gRelearnMode = RELEARN_MODE_NONE; static const u16 sUI_Pal[] = INCBIN_U16("graphics/interface/ui_learn_move.gbapal"); @@ -402,11 +405,28 @@ void CB2_InitLearnMove(void) SetVBlankCallback(VBlankCB_MoveRelearner); InitMoveRelearnerBackgroundLayers(); - InitMoveRelearnerWindows(gOriginSummaryScreenPage == PSS_PAGE_CONTEST_MOVES); + InitMoveRelearnerWindows(gRelearnMode == RELEARN_MODE_PSS_PAGE_CONTEST_MOVES); sMoveRelearnerMenuState.listOffset = 0; sMoveRelearnerMenuState.listRow = 0; - sMoveRelearnerMenuState.showContestInfo = gOriginSummaryScreenPage == PSS_PAGE_CONTEST_MOVES; + sMoveRelearnerMenuState.showContestInfo = gRelearnMode == RELEARN_MODE_PSS_PAGE_CONTEST_MOVES; + + switch (gMoveRelearnerState) + { + case MOVE_RELEARNER_EGG_MOVES: + StringCopy(gStringVar3, MoveRelearner_Text_EggMoveLWR); + break; + case MOVE_RELEARNER_TM_MOVES: + StringCopy(gStringVar3, MoveRelearner_Text_TMMoveLWR); + break; + case MOVE_RELEARNER_TUTOR_MOVES: + StringCopy(gStringVar3, MoveRelearner_Text_TutorMoveLWR); + break; + case MOVE_RELEARNER_LEVEL_UP_MOVES: + default: + StringCopy(gStringVar3, MoveRelearner_Text_LevelUpMoveLWR); + break; + } CreateLearnableMovesList(); @@ -474,6 +494,18 @@ static void PrintMessageWithPlaceholders(const u8 *src) MoveRelearnerPrintMessage(gStringVar4); } +// If reusable TMs is off, remove the TM from the bag +static void RemoveRelearnerTMFromBag(u16 move) +{ + u16 item = GetTMHMItemIdFromMoveId(move); + + if (!I_REUSABLE_TMS && !P_ENABLE_ALL_TM_MOVES + && gMoveRelearnerState == MOVE_RELEARNER_TM_MOVES && GetItemTMHMIndex(item) <= NUM_TECHNICAL_MACHINES) + { + RemoveBagItem(item, 1); + } +} + // See the state machine doc at the top of the file. static void DoMoveRelearnerMain(void) { @@ -482,14 +514,14 @@ static void DoMoveRelearnerMain(void) case MENU_STATE_FADE_TO_BLACK: sMoveRelearnerStruct->state++; HideHeartSpritesAndShowTeachMoveText(FALSE); - if (gOriginSummaryScreenPage == PSS_PAGE_CONTEST_MOVES) + if (gRelearnMode == RELEARN_MODE_PSS_PAGE_CONTEST_MOVES) MoveRelearnerShowHideHearts(GetCurrentSelectedMove()); BeginNormalPaletteFade(PALETTES_ALL, 0, 16, 0, RGB_BLACK); break; case MENU_STATE_WAIT_FOR_FADE: if (!gPaletteFade.active) { - if (gOriginSummaryScreenPage == PSS_PAGE_CONTEST_MOVES) + if (gRelearnMode == RELEARN_MODE_PSS_PAGE_CONTEST_MOVES) sMoveRelearnerStruct->state = MENU_STATE_IDLE_CONTEST_MODE; else sMoveRelearnerStruct->state = MENU_STATE_IDLE_BATTLE_MODE; @@ -499,7 +531,6 @@ static void DoMoveRelearnerMain(void) sMoveRelearnerStruct->state++; break; case MENU_STATE_SETUP_BATTLE_MODE: - HideHeartSpritesAndShowTeachMoveText(FALSE); sMoveRelearnerStruct->state++; AddScrollArrows(); @@ -684,10 +715,11 @@ static void DoMoveRelearnerMain(void) FreeMoveRelearnerResources(); } break; - case 21: - if (!MoveRelearnerRunTextPrinters()) + case MENU_STATE_RETURN_TO_PARTY_MENU: + if (!gPaletteFade.active) { - sMoveRelearnerStruct->state = MENU_STATE_FADE_AND_RETURN; + FreeMoveRelearnerResources(); + SetMainCallback2(CB2_ReturnToPartyMenuFromSummaryScreen); } break; case 22: @@ -695,26 +727,28 @@ static void DoMoveRelearnerMain(void) break; case MENU_STATE_FADE_AND_RETURN: BeginNormalPaletteFade(PALETTES_ALL, 0, 0, 16, RGB_BLACK); - sMoveRelearnerStruct->state++; + if (gRelearnMode == RELEARN_MODE_PARTY_MENU) + sMoveRelearnerStruct->state = MENU_STATE_RETURN_TO_PARTY_MENU; + else + sMoveRelearnerStruct->state++; break; case MENU_STATE_RETURN_TO_FIELD: if (!gPaletteFade.active) { if (gInitialSummaryScreenCallback != NULL) { - switch (gOriginSummaryScreenPage) + switch (gRelearnMode) { - case PSS_PAGE_BATTLE_MOVES: + case RELEARN_MODE_PSS_PAGE_BATTLE_MOVES: ShowPokemonSummaryScreen(SUMMARY_MODE_RELEARNER_BATTLE, gPlayerParty, sMoveRelearnerStruct->partyMon, gPlayerPartyCount - 1, gInitialSummaryScreenCallback); break; - case PSS_PAGE_CONTEST_MOVES: + case RELEARN_MODE_PSS_PAGE_CONTEST_MOVES: ShowPokemonSummaryScreen(SUMMARY_MODE_RELEARNER_CONTEST, gPlayerParty, sMoveRelearnerStruct->partyMon, gPlayerPartyCount - 1, gInitialSummaryScreenCallback); break; default: ShowPokemonSummaryScreen(SUMMARY_MODE_NORMAL, gPlayerParty, sMoveRelearnerStruct->partyMon, gPlayerPartyCount - 1, gInitialSummaryScreenCallback); break; } - gOriginSummaryScreenPage = 0; } else { @@ -722,6 +756,7 @@ static void DoMoveRelearnerMain(void) } FreeMoveRelearnerResources(); + gRelearnMode = RELEARN_MODE_NONE; } break; case MENU_STATE_FADE_FROM_SUMMARY_SCREEN: @@ -754,7 +789,8 @@ static void DoMoveRelearnerMain(void) RemoveMonPPBonus(&gPlayerParty[sMoveRelearnerStruct->partyMon], sMoveRelearnerStruct->moveSlot); SetMonMoveSlot(&gPlayerParty[sMoveRelearnerStruct->partyMon], GetCurrentSelectedMove(), sMoveRelearnerStruct->moveSlot); u8 newPP = GetMonData(&gPlayerParty[sMoveRelearnerStruct->partyMon], MON_DATA_PP1 + sMoveRelearnerStruct->moveSlot); - if (!P_SUMMARY_MOVE_RELEARNER_FULL_PP && gOriginSummaryScreenPage != 0 && originalPP < newPP) + if (!P_SUMMARY_MOVE_RELEARNER_FULL_PP + && (gRelearnMode == RELEARN_MODE_PSS_PAGE_BATTLE_MOVES || gRelearnMode == RELEARN_MODE_PSS_PAGE_BATTLE_MOVES) && originalPP < newPP) SetMonData(&gPlayerParty[sMoveRelearnerStruct->partyMon], MON_DATA_PP1 + sMoveRelearnerStruct->moveSlot, &originalPP); StringCopy(gStringVar2, GetMoveName(GetCurrentSelectedMove())); PrintMessageWithPlaceholders(gText_MoveRelearnerAndPoof); @@ -775,6 +811,7 @@ static void DoMoveRelearnerMain(void) if (!MoveRelearnerRunTextPrinters()) { PlayFanfare(MUS_LEVEL_UP); + RemoveRelearnerTMFromBag(GetCurrentSelectedMove()); sMoveRelearnerStruct->state = MENU_STATE_WAIT_FOR_FANFARE; } break; @@ -953,7 +990,22 @@ static void CreateLearnableMovesList(void) s32 i; u8 nickname[POKEMON_NAME_LENGTH + 1]; - sMoveRelearnerStruct->numMenuChoices = GetMoveRelearnerMoves(&gPlayerParty[sMoveRelearnerStruct->partyMon], sMoveRelearnerStruct->movesToLearn); + switch (gMoveRelearnerState) + { + case MOVE_RELEARNER_EGG_MOVES: + sMoveRelearnerStruct->numMenuChoices = GetRelearnerEggMoves(&gPlayerParty[sMoveRelearnerStruct->partyMon], sMoveRelearnerStruct->movesToLearn); + break; + case MOVE_RELEARNER_TM_MOVES: + sMoveRelearnerStruct->numMenuChoices = GetRelearnerTMMoves(&gPlayerParty[sMoveRelearnerStruct->partyMon], sMoveRelearnerStruct->movesToLearn); + break; + case MOVE_RELEARNER_TUTOR_MOVES: + sMoveRelearnerStruct->numMenuChoices = GetRelearnerTutorMoves(&gPlayerParty[sMoveRelearnerStruct->partyMon], sMoveRelearnerStruct->movesToLearn); + break; + case MOVE_RELEARNER_LEVEL_UP_MOVES: + default: + sMoveRelearnerStruct->numMenuChoices = GetRelearnerLevelUpMoves(&gPlayerParty[sMoveRelearnerStruct->partyMon], sMoveRelearnerStruct->movesToLearn); + break; + } for (i = 0; i < sMoveRelearnerStruct->numMenuChoices; i++) { diff --git a/src/party_menu.c b/src/party_menu.c index 3964716afa..5094bfcb9f 100644 --- a/src/party_menu.c +++ b/src/party_menu.c @@ -43,6 +43,7 @@ #include "menu_helpers.h" #include "menu_specialized.h" #include "metatile_behavior.h" +#include "move_relearner.h" #include "overworld.h" #include "palette.h" #include "party_menu.h" @@ -99,6 +100,11 @@ enum { MENU_REGISTER, MENU_TRADE1, MENU_TRADE2, + MENU_LEVEL_UP_MOVES, + MENU_EGG_MOVES, + MENU_TM_MOVES, + MENU_TUTOR_MOVES, + MENU_SUB_MOVES, MENU_TOSS, MENU_CATALOG_BULB, MENU_CATALOG_OVEN, @@ -126,6 +132,7 @@ enum { ACTIONS_REGISTER, ACTIONS_TRADE, ACTIONS_SPIN_TRADE, + ACTIONS_MOVES_SUB, ACTIONS_TAKEITEM_TOSS, ACTIONS_ROTOM_CATALOG, ACTIONS_ZYGARDE_CUBE, @@ -335,13 +342,13 @@ static void Task_CancelParticipationYesNo(u8); static void Task_HandleCancelParticipationYesNoInput(u8); static bool8 ShouldUseChooseMonText(void); static void SetPartyMonFieldSelectionActions(struct Pokemon *, u8); +static void SetPartyMonLearnMoveSelectionActions(struct Pokemon*, u8); static u8 GetPartyMenuActionsTypeInBattle(struct Pokemon *); static u8 GetPartySlotEntryStatus(s8); static void Task_UpdateHeldItemSprite(u8); static void Task_HandleSelectionMenuInput(u8); static void CB2_ShowPokemonSummaryScreen(void); static void UpdatePartyToBattleOrder(void); -static void CB2_ReturnToPartyMenuFromSummaryScreen(void); static void SlidePartyMenuBoxOneStep(u8); static void Task_SlideSelectedSlotsOffscreen(u8); static void SwitchPartyMon(void); @@ -478,6 +485,11 @@ static void CursorCb_Trade1(u8); static void CursorCb_Trade2(u8); static void CursorCb_Toss(u8); static void CursorCb_FieldMove(u8); +static void CursorCb_ChangeLevelUpMoves(u8); +static void CursorCb_ChangeEggMoves(u8); +static void CursorCb_ChangeTMMoves(u8); +static void CursorCb_ChangeTutorMoves(u8); +static void CursorCb_LearnMovesSubMenu(u8); static void CursorCb_CatalogBulb(u8); static void CursorCb_CatalogOven(u8); static void CursorCb_CatalogWashing(u8); @@ -1081,10 +1093,27 @@ static void DisplayPartyPokemonDataForContest(u8 slot) static void DisplayPartyPokemonDataForRelearner(u8 slot) { - if (GetNumberOfRelearnableMoves(&gPlayerParty[slot]) == 0) - DisplayPartyPokemonDescriptionData(slot, PARTYBOX_DESC_NOT_ABLE_2); - else - DisplayPartyPokemonDescriptionData(slot, PARTYBOX_DESC_ABLE_2); + struct Pokemon *mon = &gPlayerParty[slot]; + bool32 hasMoves = FALSE; + + switch (gMoveRelearnerState) + { + case MOVE_RELEARNER_EGG_MOVES: + hasMoves = (GetNumberOfEggMoves(mon) > 0); + break; + case MOVE_RELEARNER_TM_MOVES: + hasMoves = (GetNumberOfTMMoves(mon) > 0); + break; + case MOVE_RELEARNER_TUTOR_MOVES: + hasMoves = (GetNumberOfTutorMoves(mon) > 0); + break; + default: + hasMoves = (GetNumberOfLevelUpMoves(mon) > 0); + break; + } + + u32 desc = (hasMoves ? PARTYBOX_DESC_ABLE_2 : PARTYBOX_DESC_NOT_ABLE_2); + DisplayPartyPokemonDescriptionData(slot, desc); } static void DisplayPartyPokemonDataForWirelessMinigame(u8 slot) @@ -2771,7 +2800,13 @@ static u8 DisplaySelectionWindow(u8 windowType) for (i = 0; i < sPartyMenuInternal->numActions; i++) { const u8 *text; - u8 fontColorsId = (sPartyMenuInternal->actions[i] >= MENU_FIELD_MOVES) ? 4 : 3; + u8 fontColorsId = 3; + + if (sPartyMenuInternal->actions[i] >= MENU_FIELD_MOVES) + fontColorsId = 4; + if (sPartyMenuInternal->actions[i] >= MENU_LEVEL_UP_MOVES && sPartyMenuInternal->actions[i] <= MENU_SUB_MOVES) + fontColorsId = 6; + if (sPartyMenuInternal->actions[i] >= MENU_FIELD_MOVES) text = GetMoveName(FieldMove_GetMoveId(sPartyMenuInternal->actions[i] - MENU_FIELD_MOVES)); else @@ -2819,6 +2854,11 @@ static void SetPartyMonSelectionActions(struct Pokemon *mons, u8 slotId, u8 acti { SetPartyMonFieldSelectionActions(mons, slotId); } + else if (action == ACTIONS_MOVES_SUB && P_PARTY_MOVE_RELEARNER) + { + sPartyMenuInternal->numActions = 0; + SetPartyMonLearnMoveSelectionActions(mons, slotId); + } else { sPartyMenuInternal->numActions = sPartyMenuActionCounts[action]; @@ -2834,6 +2874,12 @@ static void SetPartyMonFieldSelectionActions(struct Pokemon *mons, u8 slotId) sPartyMenuInternal->numActions = 0; AppendToList(sPartyMenuInternal->actions, &sPartyMenuInternal->numActions, MENU_SUMMARY); + if (P_PARTY_MOVE_RELEARNER + && (GetMonData(&mons[slotId], MON_DATA_SPECIES) + && (GetNumberOfLevelUpMoves(&mons[slotId]) || GetNumberOfEggMoves(&mons[slotId]) + || GetNumberOfTMMoves(&mons[slotId]) || GetNumberOfTutorMoves(&mons[slotId])))) + AppendToList(sPartyMenuInternal->actions, &sPartyMenuInternal->numActions, MENU_SUB_MOVES); + // Add field moves to action list for (i = 0; i < MAX_MON_MOVES; i++) { @@ -2859,6 +2905,32 @@ static void SetPartyMonFieldSelectionActions(struct Pokemon *mons, u8 slotId) AppendToList(sPartyMenuInternal->actions, &sPartyMenuInternal->numActions, MENU_CANCEL1); } +static void SetPartyMonLearnMoveSelectionActions(struct Pokemon *mons, u8 slotId) +{ + if (GetMonData(&mons[slotId], MON_DATA_SPECIES) != SPECIES_NONE && GetNumberOfLevelUpMoves(&mons[slotId]) > 0) + AppendToList(sPartyMenuInternal->actions, &sPartyMenuInternal->numActions, MENU_LEVEL_UP_MOVES); + + if (P_ENABLE_MOVE_RELEARNERS || (P_FLAG_EGG_MOVES != 0 && FlagGet(P_FLAG_EGG_MOVES))) + { + if (GetMonData(&mons[slotId], MON_DATA_SPECIES) != SPECIES_NONE && GetNumberOfEggMoves(&mons[slotId]) > 0) + AppendToList(sPartyMenuInternal->actions, &sPartyMenuInternal->numActions, MENU_EGG_MOVES); + } + + if (P_ENABLE_MOVE_RELEARNERS || P_TM_MOVES_RELEARNER) + { + if (GetMonData(&mons[slotId], MON_DATA_SPECIES) != SPECIES_NONE && GetNumberOfTMMoves(&mons[slotId]) > 0) + AppendToList(sPartyMenuInternal->actions, &sPartyMenuInternal->numActions, MENU_TM_MOVES); + } + + if (P_ENABLE_MOVE_RELEARNERS || (P_FLAG_TUTOR_MOVES != 0 && FlagGet(P_FLAG_TUTOR_MOVES))) + { + if (GetMonData(&mons[slotId], MON_DATA_SPECIES) != SPECIES_NONE && GetNumberOfTutorMoves(&mons[slotId]) > 0) + AppendToList(sPartyMenuInternal->actions, &sPartyMenuInternal->numActions, MENU_TUTOR_MOVES); + } + + AppendToList(sPartyMenuInternal->actions, &sPartyMenuInternal->numActions, MENU_CANCEL1); +} + static u8 GetPartyMenuActionsType(struct Pokemon *mon) { u32 actionType; @@ -3016,7 +3088,7 @@ static void CB2_ShowPokemonSummaryScreen(void) } } -static void CB2_ReturnToPartyMenuFromSummaryScreen(void) +void CB2_ReturnToPartyMenuFromSummaryScreen(void) { gPaletteFade.bufferTransferDisabled = TRUE; gPartyMenu.slotId = gLastViewedMonIndex; @@ -7857,6 +7929,7 @@ static void Task_ChoosePartyMon(u8 taskId) void ChooseMonForMoveRelearner(void) { + gRelearnMode = RELEARN_MODE_SCRIPT; LockPlayerFieldControls(); FadeScreen(FADE_TO_BLACK, 0); CreateTask(Task_ChooseMonForMoveRelearner, 10); @@ -7876,9 +7949,27 @@ static void CB2_ChooseMonForMoveRelearner(void) { gSpecialVar_0x8004 = GetCursorSelectionMonId(); if (gSpecialVar_0x8004 >= PARTY_SIZE) + { gSpecialVar_0x8004 = PARTY_NOTHING_CHOSEN; + } else - gSpecialVar_0x8005 = GetNumberOfRelearnableMoves(&gPlayerParty[gSpecialVar_0x8004]); + { + switch(gMoveRelearnerState) + { + case MOVE_RELEARNER_EGG_MOVES: + gSpecialVar_0x8005 = GetNumberOfEggMoves(&gPlayerParty[gSpecialVar_0x8004]); + break; + case MOVE_RELEARNER_TM_MOVES: + gSpecialVar_0x8005 = GetNumberOfTMMoves(&gPlayerParty[gSpecialVar_0x8004]); + break; + case MOVE_RELEARNER_TUTOR_MOVES: + gSpecialVar_0x8005 = GetNumberOfTutorMoves(&gPlayerParty[gSpecialVar_0x8004]); + break; + default: + gSpecialVar_0x8005 = GetNumberOfLevelUpMoves(&gPlayerParty[gSpecialVar_0x8004]); + break; + } + } gFieldCallback2 = CB2_FadeFromPartyMenu; SetMainCallback2(CB2_ReturnToField); } @@ -8008,6 +8099,62 @@ void IsLastMonThatKnowsSurf(void) } } +static void CursorCb_ChangeLevelUpMoves(u8 taskId) +{ + PlaySE(SE_SELECT); + gMoveRelearnerState = MOVE_RELEARNER_LEVEL_UP_MOVES; + gRelearnMode = RELEARN_MODE_PARTY_MENU; + gLastViewedMonIndex = gPartyMenu.slotId; + gSpecialVar_0x8004 = gLastViewedMonIndex; + TeachMoveRelearnerMove(); + Task_ClosePartyMenu(taskId); +} + +static void CursorCb_ChangeEggMoves(u8 taskId) +{ + PlaySE(SE_SELECT); + gMoveRelearnerState = MOVE_RELEARNER_EGG_MOVES; + gRelearnMode = RELEARN_MODE_PARTY_MENU; + gLastViewedMonIndex = gPartyMenu.slotId; + gSpecialVar_0x8004 = gLastViewedMonIndex; + TeachMoveRelearnerMove(); + Task_ClosePartyMenu(taskId); +} + +static void CursorCb_ChangeTMMoves(u8 taskId) +{ + PlaySE(SE_SELECT); + gMoveRelearnerState = MOVE_RELEARNER_TM_MOVES; + gRelearnMode = RELEARN_MODE_PARTY_MENU; + gLastViewedMonIndex = gPartyMenu.slotId; + gSpecialVar_0x8004 = gLastViewedMonIndex; + TeachMoveRelearnerMove(); + Task_ClosePartyMenu(taskId); +} + +static void CursorCb_ChangeTutorMoves(u8 taskId) +{ + PlaySE(SE_SELECT); + gMoveRelearnerState = MOVE_RELEARNER_TUTOR_MOVES; + gRelearnMode = RELEARN_MODE_PARTY_MENU; + gLastViewedMonIndex = gPartyMenu.slotId; + gSpecialVar_0x8004 = gLastViewedMonIndex; + TeachMoveRelearnerMove(); + Task_ClosePartyMenu(taskId); +} + +static void CursorCb_LearnMovesSubMenu(u8 taskId) +{ + PlaySE(SE_SELECT); + PartyMenuRemoveWindow(&sPartyMenuInternal->windowId[0]); + PartyMenuRemoveWindow(&sPartyMenuInternal->windowId[1]); + SetPartyMonSelectionActions(gPlayerParty, gPartyMenu.slotId, ACTIONS_MOVES_SUB); + DisplaySelectionWindow(SELECTWINDOW_ACTIONS); + DisplayPartyMenuStdMessage(PARTY_MSG_DO_WHAT_WITH_MON); + gTasks[taskId].data[0] = 0xFF; + gTasks[taskId].func = Task_HandleSelectionMenuInput; +} + void CursorCb_MoveItemCallback(u8 taskId) { u16 item1, item2; diff --git a/src/pokemon.c b/src/pokemon.c index e728a7dbe2..639e6adaed 100644 --- a/src/pokemon.c +++ b/src/pokemon.c @@ -12,6 +12,7 @@ #include "battle_tower.h" #include "battle_z_move.h" #include "data.h" +#include "daycare.h" #include "dexnav.h" #include "event_data.h" #include "event_object_movement.h" @@ -26,6 +27,7 @@ #include "caps.h" #include "link.h" #include "main.h" +#include "move_relearner.h" #include "overworld.h" #include "m4a.h" #include "party_menu.h" @@ -5709,46 +5711,282 @@ u8 CanLearnTeachableMove(u16 species, u16 move) } } -u8 GetMoveRelearnerMoves(struct Pokemon *mon, u16 *moves) +static void QuickSortMoves(u16 *moves, s32 left, s32 right) { - u16 learnedMoves[4]; - u8 numMoves = 0; - u16 species = GetMonData(mon, MON_DATA_SPECIES, 0); - u8 level = GetMonData(mon, MON_DATA_LEVEL, 0); - const struct LevelUpMove *learnset = GetSpeciesLevelUpLearnset(species); - int i, j, k; + if (left >= right) + return; - for (i = 0; i < MAX_MON_MOVES; i++) - learnedMoves[i] = GetMonData(mon, MON_DATA_MOVE1 + i, 0); + u16 pivot = moves[(left + right) / 2]; + s32 i = left, j = right; - for (i = 0; i < MAX_LEVEL_UP_MOVES; i++) + while (i <= j) { - u16 moveLevel; + while (moves[i] != MOVE_NONE && StringCompare(GetMoveName(moves[i]), GetMoveName(pivot)) < 0) + i++; + while (moves[j] != MOVE_NONE && StringCompare(GetMoveName(moves[j]), GetMoveName(pivot)) > 0) + j--; - if (learnset[i].move == LEVEL_UP_MOVE_END) - break; - - moveLevel = learnset[i].level; - - if (moveLevel <= level) + if (i <= j) { - for (j = 0; j < MAX_MON_MOVES && learnedMoves[j] != learnset[i].move; j++) - ; - - if (j == MAX_MON_MOVES) - { - for (k = 0; k < numMoves && moves[k] != learnset[i].move; k++) - ; - - if (k == numMoves) - moves[numMoves++] = learnset[i].move; - } + u16 temp = moves[i]; + moves[i] = moves[j]; + moves[j] = temp; + i++; + j--; } } + QuickSortMoves(moves, left, j); + QuickSortMoves(moves, i, right); +} + +static void SortMovesAlphabetically(u16 *moves, u8 numMoves) +{ + if (numMoves > 1) + QuickSortMoves(moves, 0, numMoves - 1); +} + +u8 GetRelearnerLevelUpMoves(struct Pokemon *mon, u16 *moves) +{ + u16 learnedMoves[MAX_MON_MOVES] = {0}; + u8 numMoves = 0; + u16 species = GetMonData(mon, MON_DATA_SPECIES, 0); + u8 level = (P_ENABLE_ALL_LEVEL_UP_MOVES ? MAX_LEVEL : GetMonData(mon, MON_DATA_LEVEL, 0)); + + for (u8 i = 0; i < MAX_MON_MOVES; i++) + learnedMoves[i] = GetMonData(mon, MON_DATA_MOVE1 + i, 0); + + do + { + const struct LevelUpMove *learnset = GetSpeciesLevelUpLearnset(species); + + for (u16 i = 0; i < MAX_LEVEL_UP_MOVES && learnset[i].move != LEVEL_UP_MOVE_END; i++) + { + if (learnset[i].level > level) + continue; + + u16 j; + for (j = 0; j < MAX_MON_MOVES; j++) + { + if (learnedMoves[j] == learnset[i].move) + break; + } + if (j < MAX_MON_MOVES) + continue; + + for (j = 0; j < numMoves; j++) + { + if (moves[j] == learnset[i].move) + break; + } + if (j < numMoves) + continue; + + moves[numMoves++] = learnset[i].move; + } + + species = (P_PRE_EVO_MOVES ? GetSpeciesPreEvolution(species) : SPECIES_NONE); + } while (species != SPECIES_NONE); + + if (P_SORT_MOVES) + SortMovesAlphabetically(moves, numMoves); + return numMoves; } +u8 GetRelearnerEggMoves(struct Pokemon *mon, u16 *moves) +{ + u16 learnedMoves[MAX_MON_MOVES] = {0}; + u8 numMoves = 0; + u16 species = GetMonData(mon, MON_DATA_SPECIES); + + while (GetSpeciesPreEvolution(species) != SPECIES_NONE) + species = GetSpeciesPreEvolution(species); + const u16 *eggMoves = GetSpeciesEggMoves(species); + + if (eggMoves == sNoneEggMoveLearnset) + return numMoves; + + for (u8 i = 0; i < MAX_MON_MOVES; i++) + learnedMoves[i] = GetMonData(mon, MON_DATA_MOVE1 + i, 0); + + for (u16 i = 0; eggMoves[i] != MOVE_UNAVAILABLE; i++) + { + u16 j; + for (j = 0; j < MAX_MON_MOVES; j++) + { + if (learnedMoves[j] == eggMoves[i]) + break; + } + if (j < MAX_MON_MOVES) + continue; + + for (j = 0; j < numMoves; j++) + { + if (moves[j] == eggMoves[i]) + break; + } + if (j < numMoves) + continue; + + moves[numMoves++] = eggMoves[i]; + } + + if (P_SORT_MOVES) + SortMovesAlphabetically(moves, numMoves); + + return numMoves; +} + +u8 GetRelearnerTMMoves(struct Pokemon *mon, u16 *moves) +{ + u16 learnedMoves[MAX_MON_MOVES] = {0}; + u8 numMoves = 0; + u16 species = GetMonData(mon, MON_DATA_SPECIES); + u16 allMoves[NUM_ALL_MACHINES]; + u16 totalMoveCount = 0; + + for (u16 i = 0; i < NUM_ALL_MACHINES; i++) + { + enum TMHMItemId item = GetTMHMItemId(i + 1); + u16 move = GetTMHMMoveId(i + 1); + if ((P_ENABLE_ALL_TM_MOVES || CheckBagHasItem(item, 1)) && CanLearnTeachableMove(species, move) && move != MOVE_NONE) + allMoves[totalMoveCount++] = move; + } + + for (u8 i = 0; i < MAX_MON_MOVES; i++) + learnedMoves[i] = GetMonData(mon, MON_DATA_MOVE1 + i, 0); + + for (u16 i = 0; i < totalMoveCount; i++) + { + u16 j; + for (j = 0; j < MAX_MON_MOVES; j++) + { + if (learnedMoves[j] == allMoves[i]) + break; + } + if (j < MAX_MON_MOVES) + continue; + + for (j = 0; j < numMoves; j++) + { + if (moves[j] == allMoves[i]) + break; + } + if (j < numMoves) + continue; + + moves[numMoves++] = allMoves[i]; + } + + if (P_SORT_MOVES) + SortMovesAlphabetically(moves, numMoves); + + return numMoves; +} + +u8 GetRelearnerTutorMoves(struct Pokemon *mon, u16 *moves) +{ +#if P_TUTOR_MOVES_ARRAY + u16 learnedMoves[MAX_MON_MOVES] = {0}; + u8 numMoves = 0; + u16 species = GetMonData(mon, MON_DATA_SPECIES, 0); + + for (u8 i = 0; i < MAX_MON_MOVES; i++) + learnedMoves[i] = GetMonData(mon, MON_DATA_MOVE1 + i, 0); + + for (u16 i = 0; gTutorMoves[i] != MOVE_UNAVAILABLE; i++) + { + u16 move = gTutorMoves[i]; + + if (!CanLearnTeachableMove(species, move)) + continue; + + u16 j; + for (j = 0; j < MAX_MON_MOVES; j++) + { + if (learnedMoves[j] == move) + break; + } + if (j < MAX_MON_MOVES) + continue; + + for (j = 0; j < numMoves; j++) + { + if (moves[j] == move) + break; + } + if (j < numMoves) + continue; + + moves[numMoves++] = move; + } + + if (P_SORT_MOVES) + SortMovesAlphabetically(moves, numMoves); + + return numMoves; +#else + return 0; +#endif // P_TUTOR_MOVES_ARRAY +} + +u8 GetNumberOfLevelUpMoves(struct Pokemon *mon) +{ + u16 moves[MAX_RELEARNER_MOVES] = {0}; + u16 species = GetMonData(mon, MON_DATA_SPECIES_OR_EGG, 0); + + if (species == SPECIES_EGG) + return 0; + + return GetRelearnerLevelUpMoves(mon, moves); +} + +u8 GetNumberOfEggMoves(struct Pokemon *mon) +{ + if (!FlagGet(P_FLAG_EGG_MOVES) && !P_ENABLE_MOVE_RELEARNERS) + return 0; + + u16 moves[EGG_MOVES_ARRAY_COUNT] = {0}; + u16 species = GetMonData(mon, MON_DATA_SPECIES_OR_EGG, 0); + + if (species == SPECIES_EGG) + return 0; + + return GetRelearnerEggMoves(mon, moves); +} + +u8 GetNumberOfTMMoves(struct Pokemon *mon) +{ + if (!P_TM_MOVES_RELEARNER) + return 0; + + if (!P_ENABLE_ALL_TM_MOVES && !IsBagPocketNonEmpty(POCKET_TM_HM)) + return 0; + + u16 moves[MAX_RELEARNER_MOVES] = {0}; + u16 species = GetMonData(mon, MON_DATA_SPECIES_OR_EGG, 0); + + if (species == SPECIES_EGG) + return 0; + + return GetRelearnerTMMoves(mon, moves); +} + +u8 GetNumberOfTutorMoves(struct Pokemon *mon) +{ + if (!FlagGet(P_FLAG_TUTOR_MOVES) && !P_ENABLE_MOVE_RELEARNERS) + return 0; + + u16 moves[MAX_RELEARNER_MOVES] = {0}; + u16 species = GetMonData(mon, MON_DATA_SPECIES_OR_EGG, 0); + + if (species == SPECIES_EGG) + return 0; + + return GetRelearnerTutorMoves(mon, moves); +} + u8 GetLevelUpMovesBySpecies(u16 species, u16 *moves) { u8 numMoves = 0; @@ -5761,50 +5999,6 @@ u8 GetLevelUpMovesBySpecies(u16 species, u16 *moves) return numMoves; } -u8 GetNumberOfRelearnableMoves(struct Pokemon *mon) -{ - u16 learnedMoves[MAX_MON_MOVES]; - u16 moves[MAX_LEVEL_UP_MOVES]; - u8 numMoves = 0; - u16 species = GetMonData(mon, MON_DATA_SPECIES_OR_EGG, 0); - u8 level = GetMonData(mon, MON_DATA_LEVEL, 0); - const struct LevelUpMove *learnset = GetSpeciesLevelUpLearnset(species); - int i, j, k; - - if (species == SPECIES_EGG) - return 0; - - for (i = 0; i < MAX_MON_MOVES; i++) - learnedMoves[i] = GetMonData(mon, MON_DATA_MOVE1 + i, 0); - - for (i = 0; i < MAX_LEVEL_UP_MOVES; i++) - { - u16 moveLevel; - - if (learnset[i].move == LEVEL_UP_MOVE_END) - break; - - moveLevel = learnset[i].level; - - if (moveLevel <= level) - { - for (j = 0; j < MAX_MON_MOVES && learnedMoves[j] != learnset[i].move; j++) - ; - - if (j == MAX_MON_MOVES) - { - for (k = 0; k < numMoves && moves[k] != learnset[i].move; k++) - ; - - if (k == numMoves) - moves[numMoves++] = learnset[i].move; - } - } - } - - return numMoves; -} - u16 SpeciesToPokedexNum(u16 species) { if (IsNationalPokedexEnabled()) diff --git a/src/pokemon_summary_screen.c b/src/pokemon_summary_screen.c index 511c4ecc63..23af6dba2d 100644 --- a/src/pokemon_summary_screen.c +++ b/src/pokemon_summary_screen.c @@ -551,12 +551,12 @@ static const struct WindowTemplate sSummaryTemplate[] = }, [PSS_LABEL_WINDOW_PROMPT_RELEARN] = { .bg = 0, - .tilemapLeft = 22, + .tilemapLeft = 18, .tilemapTop = 2, - .width = 8, + .width = 11, .height = 2, .paletteNum = 15, - .baseBlock = 387, + .baseBlock = 800, }, [PSS_LABEL_WINDOW_PORTRAIT_DEX_NUMBER] = { .bg = 0, @@ -1231,6 +1231,9 @@ void ShowPokemonSummaryScreen(u8 mode, void *mons, u8 monIndex, u8 maxMonIndex, if (gMonSpritesGfxPtr == NULL) CreateMonSpritesGfxManager(MON_SPR_GFX_MANAGER_A, MON_SPR_GFX_MODE_NORMAL); + if (ShouldShowMoveRelearner()) + TryUpdateRelearnType(TRY_SET_UPDATE); + SetMainCallback2(CB2_InitSummaryScreen); } @@ -1543,7 +1546,6 @@ static bool8 ExtractMonDataToSummaryStruct(struct Pokemon *mon) sum->ribbonCount = GetMonData(mon, MON_DATA_RIBBON_COUNT); sum->teraType = GetMonData(mon, MON_DATA_TERA_TYPE); sum->isShiny = GetMonData(mon, MON_DATA_IS_SHINY); - sMonSummaryScreen->relearnableMovesNum = P_SUMMARY_SCREEN_MOVE_RELEARNER ? GetNumberOfRelearnableMoves(mon) : 0; return TRUE; } sMonSummaryScreen->switchCounter++; @@ -1703,11 +1705,11 @@ static void Task_HandleInput(u8 taskId) { ChangeSummaryPokemon(taskId, 1); } - else if ((JOY_NEW(DPAD_LEFT)) || GetLRKeysPressed() == MENU_L_PRESSED) + else if (JOY_NEW(DPAD_LEFT)) { ChangePage(taskId, -1); } - else if ((JOY_NEW(DPAD_RIGHT)) || GetLRKeysPressed() == MENU_R_PRESSED) + else if (JOY_NEW(DPAD_RIGHT)) { ChangePage(taskId, 1); } @@ -1755,7 +1757,7 @@ static void Task_HandleInput(u8 taskId) { sMonSummaryScreen->callback = CB2_InitLearnMove; gSpecialVar_0x8004 = sMonSummaryScreen->curMonIndex; - gOriginSummaryScreenPage = sMonSummaryScreen->currPageIndex; + gRelearnMode = sMonSummaryScreen->currPageIndex; StopPokemonAnimations(); PlaySE(SE_SELECT); BeginCloseSummaryScreen(taskId); @@ -1767,6 +1769,24 @@ static void Task_HandleInput(u8 taskId) PlaySE(SE_SELECT); CloseSummaryScreen(taskId); } + else if (JOY_NEW(R_BUTTON)) // R means increase. Level -> Egg -> TM -> Tutor + { + if (P_SUMMARY_SCREEN_MOVE_RELEARNER && (sMonSummaryScreen->currPageIndex == PSS_PAGE_BATTLE_MOVES || sMonSummaryScreen->currPageIndex == PSS_PAGE_CONTEST_MOVES) && !gMain.inBattle) + { + TryUpdateRelearnType(TRY_INCREMENT); + PlaySE(SE_SELECT); + ShowRelearnPrompt(); + } + } + else if (JOY_NEW(L_BUTTON)) // L means decrease. Level <- Egg <- TM <- Tutor + { + if (P_SUMMARY_SCREEN_MOVE_RELEARNER && (sMonSummaryScreen->currPageIndex == PSS_PAGE_BATTLE_MOVES || sMonSummaryScreen->currPageIndex == PSS_PAGE_CONTEST_MOVES) && !gMain.inBattle) + { + TryUpdateRelearnType(TRY_DECREMENT); + PlaySE(SE_SELECT); + ShowRelearnPrompt(); + } + } } } @@ -1889,6 +1909,120 @@ void ExtractMonSkillEvData(struct Pokemon *mon, struct PokeSummary *sum) sum->speed = GetMonData(mon, MON_DATA_SPEED_EV); } +u32 GetRelearnMovesCount(enum MoveRelearnerStates state) +{ + struct Pokemon *mon = &sMonSummaryScreen->currentMon; + + switch (state) + { + case MOVE_RELEARNER_EGG_MOVES: + return GetNumberOfEggMoves(mon); + case MOVE_RELEARNER_TM_MOVES: + return GetNumberOfTMMoves(mon); + case MOVE_RELEARNER_TUTOR_MOVES: + return GetNumberOfTutorMoves(mon); + case MOVE_RELEARNER_LEVEL_UP_MOVES: + return GetNumberOfLevelUpMoves(mon); + default: + return 0; + } +} + +u32 GetCurrentRelearnMovesCount(void) +{ + return GetRelearnMovesCount(gMoveRelearnerState); +} + +bool32 NoMovesAvailableToRelearn(void) +{ + u32 zeroCounter = 0; + for (enum MoveRelearnerStates state = MOVE_RELEARNER_LEVEL_UP_MOVES; state < MOVE_RELEARNER_COUNT; state++) + { + if (GetRelearnMovesCount(state) == 0) + zeroCounter++; + } + + return zeroCounter == MOVE_RELEARNER_COUNT; +} + +bool32 CheckRelearnerStateFlag(enum MoveRelearnerStates state) +{ + if (P_ENABLE_MOVE_RELEARNERS) + return TRUE; + + switch (state) + { + case MOVE_RELEARNER_LEVEL_UP_MOVES: + return TRUE; + case MOVE_RELEARNER_EGG_MOVES: + return FlagGet(P_FLAG_EGG_MOVES); + case MOVE_RELEARNER_TM_MOVES: + return P_TM_MOVES_RELEARNER; + case MOVE_RELEARNER_TUTOR_MOVES: + return FlagGet(P_FLAG_TUTOR_MOVES); + default: + return FALSE; + } +} + +void TryUpdateRelearnType(enum IncrDecrUpdateValues delta) +{ + u32 moveCount; + u32 zeroCounter = 0; + enum MoveRelearnerStates state = gMoveRelearnerState; + + // just in case everything is off, default to level up moves + if ((!P_ENABLE_MOVE_RELEARNERS + && !P_TM_MOVES_RELEARNER + && !FlagGet(P_FLAG_EGG_MOVES) + && !FlagGet(P_FLAG_TUTOR_MOVES))) + { + sMonSummaryScreen->relearnableMovesNum = GetRelearnMovesCount(MOVE_RELEARNER_LEVEL_UP_MOVES); + return; + } + + do + { + switch (delta) + { + default: + case TRY_SET_UPDATE: + moveCount = GetCurrentRelearnMovesCount(); + if (moveCount == 0) + { + delta = TRY_INCREMENT; + continue; + } + else + { + sMonSummaryScreen->relearnableMovesNum = moveCount; + return; + } + // should never reach this, but just in case + break; + case TRY_INCREMENT: + state = state >= MOVE_RELEARNER_TUTOR_MOVES ? MOVE_RELEARNER_LEVEL_UP_MOVES : state + 1; + break; + case TRY_DECREMENT: + state = state == MOVE_RELEARNER_LEVEL_UP_MOVES ? MOVE_RELEARNER_TUTOR_MOVES : state - 1; + break; + } + + if (!CheckRelearnerStateFlag(state)) + continue; + + moveCount = GetRelearnMovesCount(state); + if (moveCount != 0) + { + gMoveRelearnerState = state; + sMonSummaryScreen->relearnableMovesNum = moveCount; + return; + } + zeroCounter++; + + } while (zeroCounter <= MOVE_RELEARNER_COUNT && moveCount == 0); +} + static void ChangeSummaryPokemon(u8 taskId, s8 delta) { s8 monId; @@ -1960,27 +2094,32 @@ static void Task_ChangeSummaryMon(u8 taskId) sMonSummaryScreen->switchCounter = 0; break; case 4: + if (ExtractMonDataToSummaryStruct(&sMonSummaryScreen->currentMon) == FALSE) + return; + if (P_SUMMARY_SCREEN_RENAME && sMonSummaryScreen->currPageIndex == PSS_PAGE_INFO) ShowUtilityPrompt(SUMMARY_MODE_NORMAL); + if (ShouldShowIvEvPrompt() && sMonSummaryScreen->currPageIndex == PSS_PAGE_SKILLS) { sMonSummaryScreen->skillsPageMode = SUMMARY_SKILLS_MODE_STATS; ChangeStatLabel(SUMMARY_SKILLS_MODE_STATS); } - if (ExtractMonDataToSummaryStruct(&sMonSummaryScreen->currentMon) == FALSE) + + if (P_SUMMARY_SCREEN_MOVE_RELEARNER + && (sMonSummaryScreen->currPageIndex == PSS_PAGE_BATTLE_MOVES + || sMonSummaryScreen->currPageIndex == PSS_PAGE_CONTEST_MOVES)) { - return; + gMoveRelearnerState = MOVE_RELEARNER_LEVEL_UP_MOVES; + TryUpdateRelearnType(TRY_SET_UPDATE); + if (ShouldShowMoveRelearner()) + ShowRelearnPrompt(); + else + ClearWindowTilemap(PSS_LABEL_WINDOW_PROMPT_RELEARN); } else { - if (P_SUMMARY_SCREEN_MOVE_RELEARNER - && (sMonSummaryScreen->currPageIndex == PSS_PAGE_BATTLE_MOVES || sMonSummaryScreen->currPageIndex == PSS_PAGE_CONTEST_MOVES)) - { - if (ShouldShowMoveRelearner()) - PutWindowTilemap(PSS_LABEL_WINDOW_PROMPT_RELEARN); - else - ClearWindowTilemap(PSS_LABEL_WINDOW_PROMPT_RELEARN); - } + ClearWindowTilemap(PSS_LABEL_WINDOW_PROMPT_RELEARN); } break; case 5: @@ -2096,6 +2235,7 @@ static void ChangePage(u8 taskId, s8 delta) { struct PokeSummary *summary = &sMonSummaryScreen->summary; s16 *data = gTasks[taskId].data; + u32 currPageIndex; if (summary->isEgg) return; @@ -2106,17 +2246,17 @@ static void ChangePage(u8 taskId, s8 delta) PlaySE(SE_SELECT); ClearPageWindowTilemaps(sMonSummaryScreen->currPageIndex); - sMonSummaryScreen->currPageIndex += delta; + currPageIndex = sMonSummaryScreen->currPageIndex += delta; data[0] = 0; if (delta == 1) SetTaskFuncWithFollowupFunc(taskId, PssScrollRight, gTasks[taskId].func); else SetTaskFuncWithFollowupFunc(taskId, PssScrollLeft, gTasks[taskId].func); - CreateTextPrinterTask(sMonSummaryScreen->currPageIndex); + CreateTextPrinterTask(currPageIndex); HidePageSpecificSprites(); - if (sMonSummaryScreen->currPageIndex == PSS_PAGE_SKILLS - || (sMonSummaryScreen->currPageIndex + delta) == PSS_PAGE_SKILLS) + if (currPageIndex == PSS_PAGE_SKILLS + || (currPageIndex + delta) == PSS_PAGE_SKILLS) { struct Pokemon *mon = &sMonSummaryScreen->currentMon; @@ -2132,6 +2272,19 @@ static void ChangePage(u8 taskId, s8 delta) { ShowUtilityPrompt(SUMMARY_MODE_NORMAL); } + + // acts like a quick reset + if (currPageIndex == PSS_PAGE_SKILLS) + { + gMoveRelearnerState = MOVE_RELEARNER_LEVEL_UP_MOVES; + TryUpdateRelearnType(TRY_SET_UPDATE); + } + + // to prevent nothing showing + if (currPageIndex >= PSS_PAGE_BATTLE_MOVES && sMonSummaryScreen->relearnableMovesNum == 0) + TryUpdateRelearnType(TRY_SET_UPDATE); + else + ClearWindowTilemap(PSS_LABEL_WINDOW_PROMPT_RELEARN); } static void PssScrollRight(u8 taskId) // Scroll right @@ -2175,6 +2328,7 @@ static void PssScrollRightEnd(u8 taskId) // display right SetTypeIcons(); TryDrawExperienceProgressBar(); SwitchTaskToFollowupFunc(taskId); + ShowRelearnPrompt(); } static void PssScrollLeft(u8 taskId) // Scroll left @@ -2227,6 +2381,7 @@ static void PssScrollLeftEnd(u8 taskId) // display left SetTypeIcons(); TryDrawExperienceProgressBar(); SwitchTaskToFollowupFunc(taskId); + ShowRelearnPrompt(); } static void TryDrawExperienceProgressBar(void) @@ -3238,7 +3393,13 @@ static void PrintPageNamesAndStats(void) PrintTextOnWindow(PSS_LABEL_WINDOW_MOVES_POWER_ACC, gText_Accuracy2, 0, 17, 0, 1); PrintTextOnWindow(PSS_LABEL_WINDOW_MOVES_APPEAL_JAM, gText_Appeal, 0, 1, 0, 1); PrintTextOnWindow(PSS_LABEL_WINDOW_MOVES_APPEAL_JAM, gText_Jam, 0, 17, 0, 1); - PrintTextOnWindowWithFont(PSS_LABEL_WINDOW_PROMPT_RELEARN, gText_Relearn, 0, 4, 0, 0, FONT_SMALL); + + if (sMonSummaryScreen->currPageIndex == PSS_PAGE_BATTLE_MOVES + || sMonSummaryScreen->currPageIndex == PSS_PAGE_CONTEST_MOVES) + { + TryUpdateRelearnType(TRY_SET_UPDATE); + ShowRelearnPrompt(); + } } static void PutPageWindowTilemaps(u8 page) @@ -3314,6 +3475,7 @@ static void ClearPageWindowTilemaps(u8 page) if (InBattleFactory() == TRUE || InSlateportBattleTent() == TRUE) ClearWindowTilemap(PSS_LABEL_WINDOW_POKEMON_INFO_RENTAL); ClearWindowTilemap(PSS_LABEL_WINDOW_POKEMON_INFO_TYPE); + ClearWindowTilemap(PSS_LABEL_WINDOW_PROMPT_RELEARN); break; case PSS_PAGE_SKILLS: ClearWindowTilemap(PSS_LABEL_WINDOW_POKEMON_SKILLS_STATS_LEFT); @@ -3321,6 +3483,7 @@ static void ClearPageWindowTilemaps(u8 page) ClearWindowTilemap(PSS_LABEL_WINDOW_POKEMON_SKILLS_EXP); if (ShouldShowIvEvPrompt()) ClearPageWindowTilemaps(PSS_LABEL_WINDOW_PROMPT_UTILITY); + ClearWindowTilemap(PSS_LABEL_WINDOW_PROMPT_RELEARN); break; case PSS_PAGE_BATTLE_MOVES: if (sMonSummaryScreen->mode == SUMMARY_MODE_SELECT_MOVE) @@ -3331,11 +3494,8 @@ static void ClearPageWindowTilemaps(u8 page) gSprites[sMonSummaryScreen->categoryIconSpriteId].invisible = TRUE; } } - else - { - if (ShouldShowMoveRelearner()) - ClearWindowTilemap(PSS_LABEL_WINDOW_PROMPT_RELEARN); - } + + ClearWindowTilemap(PSS_LABEL_WINDOW_PROMPT_RELEARN); break; case PSS_PAGE_CONTEST_MOVES: if (sMonSummaryScreen->mode == SUMMARY_MODE_SELECT_MOVE) @@ -3343,11 +3503,8 @@ static void ClearPageWindowTilemaps(u8 page) if (sMonSummaryScreen->newMove != MOVE_NONE || sMonSummaryScreen->firstMoveIndex != MAX_MON_MOVES) ClearWindowTilemap(PSS_LABEL_WINDOW_MOVES_APPEAL_JAM); } - else - { - if (ShouldShowMoveRelearner()) - ClearWindowTilemap(PSS_LABEL_WINDOW_PROMPT_RELEARN); - } + + ClearWindowTilemap(PSS_LABEL_WINDOW_PROMPT_RELEARN); break; } @@ -4661,7 +4818,8 @@ static inline bool32 ShouldShowMoveRelearner(void) && sMonSummaryScreen->mode != SUMMARY_MODE_BOX_CURSOR && sMonSummaryScreen->relearnableMovesNum > 0 && !InBattleFactory() - && !InSlateportBattleTent()); + && !InSlateportBattleTent() + && !NoMovesAvailableToRelearn()); } static inline bool32 ShouldShowRename(void) @@ -4682,7 +4840,7 @@ static inline bool32 ShouldShowIvEvPrompt(void) if (P_SUMMARY_SCREEN_IV_EV_BOX_ONLY) { return (P_SUMMARY_SCREEN_IV_EV_INFO || FlagGet(P_FLAG_SUMMARY_SCREEN_IV_EV_INFO)) - && (sMonSummaryScreen->mode == SUMMARY_MODE_BOX|| sMonSummaryScreen->mode == SUMMARY_MODE_BOX_CURSOR); + && (sMonSummaryScreen->mode == SUMMARY_MODE_BOX || sMonSummaryScreen->mode == SUMMARY_MODE_BOX_CURSOR); } else if (!P_SUMMARY_SCREEN_IV_EV_BOX_ONLY) { @@ -4757,6 +4915,49 @@ static inline void ShowUtilityPrompt(s16 mode) PrintTextOnWindow(PSS_LABEL_WINDOW_PROMPT_UTILITY, promptText, stringXPos, 1, 0, 0); } +void ShowRelearnPrompt(void) +{ + u32 currPage = sMonSummaryScreen->currPageIndex; + + if (!ShouldShowMoveRelearner() || !(currPage >= PSS_PAGE_BATTLE_MOVES)) + { + ClearWindowTilemap(PSS_LABEL_WINDOW_PROMPT_RELEARN); + return; + } + + if (GetCurrentRelearnMovesCount() == 0) + return; + + const u8* relearnText; + + int relearnTextXPos; + + switch (gMoveRelearnerState) + { + case MOVE_RELEARNER_LEVEL_UP_MOVES: + relearnText = gText_Relearn_LevelUp; + break; + case MOVE_RELEARNER_EGG_MOVES: + relearnText = gText_Relearn_Egg; + break; + case MOVE_RELEARNER_TM_MOVES: + relearnText = gText_Relearn_TM; + break; + case MOVE_RELEARNER_TUTOR_MOVES: + relearnText = gText_Relearn_Tutor; + break; + default: + relearnText = gText_Relearn; + break; + } + + relearnTextXPos = GetStringRightAlignXOffset(FONT_NORMAL, relearnText, 0); + + FillWindowPixelBuffer(PSS_LABEL_WINDOW_PROMPT_RELEARN, PIXEL_FILL(0)); + PutWindowTilemap(PSS_LABEL_WINDOW_PROMPT_RELEARN); + PrintTextOnWindowWithFont(PSS_LABEL_WINDOW_PROMPT_RELEARN, relearnText, relearnTextXPos, 4, 0, 0, FONT_SMALL); +} + static void CB2_ReturnToSummaryScreenFromNamingScreen(void) { SetMonData(&gPlayerParty[gSpecialVar_0x8004], MON_DATA_NICKNAME, gStringVar2); diff --git a/src/scrcmd.c b/src/scrcmd.c index 54edd76c61..8645908bd9 100644 --- a/src/scrcmd.c +++ b/src/scrcmd.c @@ -34,6 +34,7 @@ #include "menu.h" #include "money.h" #include "move.h" +#include "move_relearner.h" #include "mystery_event_script.h" #include "palette.h" #include "party_menu.h" @@ -3255,3 +3256,38 @@ void Script_EndTrainerCanSeeIf(struct ScriptContext *ctx) if (ctx->breakOnTrainerBattle && sScriptConditionTable[condition][ctx->comparisonResult] == 1) StopScript(ctx); } + +bool8 ScrCmd_setmoverelearnerstate(struct ScriptContext *ctx) +{ + enum MoveRelearnerStates state = VarGet(ScriptReadHalfword(ctx)); + + Script_RequestEffects(SCREFF_V1); + + gMoveRelearnerState = state; + return FALSE; +} + +bool8 ScrCmd_getmoverelearnerstate(struct ScriptContext *ctx) +{ + u32 varId = ScriptReadHalfword(ctx); + + Script_RequestEffects(SCREFF_V1); + Script_RequestWriteVar(varId); + + u16 *varPointer = GetVarPointer(varId); + *varPointer = gMoveRelearnerState; + return FALSE; +} + +bool8 ScrCmd_istmrelearneractive(struct ScriptContext *ctx) +{ + const u8 *ptr = (const u8 *)ScriptReadWord(ctx); + + Script_RequestEffects(SCREFF_V1); + + if ((P_TM_MOVES_RELEARNER || P_ENABLE_MOVE_RELEARNERS) + && (P_ENABLE_ALL_TM_MOVES || IsBagPocketNonEmpty(POCKET_TM_HM))) + ScriptCall(ctx, ptr); + + return FALSE; +} diff --git a/src/strings.c b/src/strings.c index 341bb6b780..4f7fddc6e7 100644 --- a/src/strings.c +++ b/src/strings.c @@ -1240,7 +1240,7 @@ const u8 gText_TrainerHill1F[] = _("1F"); const u8 gText_TrainerHill2F[] = _("2F"); const u8 gText_TrainerHill3F[] = _("3F"); const u8 gText_TrainerHill4F[] = _("4F"); -const u8 gText_TeachWhichMoveToPkmn[] = _("Teach which move to\n{STR_VAR_1}?"); +const u8 gText_TeachWhichMoveToPkmn[] = _("Teach which {STR_VAR_3} to\n{STR_VAR_1}?"); const u8 gText_MoveRelearnerTeachMoveConfirm[] = _("Teach {STR_VAR_2}?"); const u8 gText_MoveRelearnerPkmnLearnedMove[] = _("{STR_VAR_1} learned\n{STR_VAR_2}!"); const u8 gText_MoveRelearnerPkmnTryingToLearnMove[] = _("{STR_VAR_1} is trying to learn\n{STR_VAR_2}.\pBut {STR_VAR_1} can't learn more\nthan four moves.\pDelete an older move to make\nroom for {STR_VAR_2}?"); @@ -1301,6 +1301,10 @@ const u8 gText_BasePointsResetToZero[] = _("{STR_VAR_1}'s base points\nwere all const u8 gText_AM[] = _("AM"); const u8 gText_PM[] = _("PM"); const u8 gText_Relearn[] = _("{START_BUTTON} RELEARN"); // future note: don't decap this, because it mimics the summary screen BG graphics which will not get decapped +const u8 gText_Relearn_LevelUp[] = _("{START_BUTTON} RELEARN LEVEL"); +const u8 gText_Relearn_Egg[] = _("{START_BUTTON} RELEARN EGG"); +const u8 gText_Relearn_TM[] = _("{START_BUTTON} RELEARN TM"); +const u8 gText_Relearn_Tutor[] = _("{START_BUTTON} RELEARN TUTOR"); const u8 gText_Rename[] = _("RENAME"); const u8 gText_CannotSendMonToBoxHM[] = _("Cannot send that mon to the box,\nbecause it knows a HM move.{PAUSE_UNTIL_PRESS}"); const u8 gText_CannotSendMonToBoxActive[] = _("Cannot send an active battler\nto the box.{PAUSE_UNTIL_PRESS}"); diff --git a/test/pokemon.c b/test/pokemon.c index 207124225e..c902e56e45 100644 --- a/test/pokemon.c +++ b/test/pokemon.c @@ -5,6 +5,7 @@ #include "test/overworld_script.h" #include "test/test.h" #include "constants/characters.h" +#include "constants/move_relearner.h" TEST("Nature independent from Hidden Nature") { diff --git a/tools/learnset_helpers/make_teachables.py b/tools/learnset_helpers/make_teachables.py index 6f3b186b68..a0e94124f7 100644 --- a/tools/learnset_helpers/make_teachables.py +++ b/tools/learnset_helpers/make_teachables.py @@ -150,6 +150,7 @@ def create_tutor_moves_array(tutors: list[str]) -> None: header = dedent("""\ // DO NOT MODIFY THIS FILE! It is auto-generated by tools/learnset_helpers/make_teachables.py // Set the config P_TUTOR_MOVES_ARRAY in include/config/pokemon.h to TRUE to enable this array! + // Also need by tutor moves relearner! const u16 gTutorMoves[] = { """)