Move Relearners for TMs, Tutors and Egg moves (#8040)

Co-authored-by: khbsd <khb.dev@gmail.com>
Co-authored-by: psf <77138753+pkmnsnfrn@users.noreply.github.com>
This commit is contained in:
PCG 2025-11-10 02:24:01 +05:30 committed by GitHub
parent 996453b0b2
commit d2425c1468
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
26 changed files with 1072 additions and 148 deletions

View File

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

View File

@ -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::

View File

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

View File

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

View File

@ -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!$"

View File

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

View File

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

View File

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

View File

@ -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);

View File

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

View File

@ -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);

View File

@ -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);

View File

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

View File

@ -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[];

View File

@ -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},

View File

@ -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 },

View File

@ -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)

View File

@ -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"

View File

@ -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++)
{

View File

@ -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;

View File

@ -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())

View File

@ -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);

View File

@ -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;
}

View File

@ -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}");

View File

@ -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")
{

View File

@ -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[] = {
""")