diff --git a/Makefile b/Makefile index 777689907f..241f01874b 100644 --- a/Makefile +++ b/Makefile @@ -451,7 +451,7 @@ $(OBJ_DIR)/sym_common.ld: sym_common.txt $(C_OBJS) $(wildcard common_syms/*.txt) $(OBJ_DIR)/sym_ewram.ld: sym_ewram.txt $(RAMSCRGEN) ewram_data $< ENGLISH > $@ -TEACHABLE_DEPS := $(ALL_LEARNABLES_JSON) $(shell find data/ -type f -name '*.inc') $(INCLUDE_DIRS)/constants/tms_hms.h $(C_SUBDIR)/pokemon.c +TEACHABLE_DEPS := $(ALL_LEARNABLES_JSON) $(shell find data/ -type f -name '*.inc') $(INCLUDE_DIRS)/constants/tms_hms.h $(INCLUDE_DIRS)/config/pokemon.h $(C_SUBDIR)/pokemon.c $(LEARNSET_HELPERS_BUILD_DIR): @mkdir -p $@ diff --git a/include/config/general.h b/include/config/general.h index cff1432bb7..1bb70ce00d 100644 --- a/include/config/general.h +++ b/include/config/general.h @@ -68,14 +68,12 @@ // General settings #define EXPANSION_INTRO TRUE // If TRUE, a custom RHH intro will play after the vanilla copyright screen. -#define POKEDEX_PLUS_HGSS FALSE // If TRUE, enables the custom HGSS style Pokedex. #define SUMMARY_SCREEN_NATURE_COLORS TRUE // If TRUE, nature-based stat boosts and reductions will be red and blue in the summary screen. #define HQ_RANDOM TRUE // If TRUE, replaces the default RNG with an implementation of SFC32 RNG. May break code that relies on RNG. #define COMPETITIVE_PARTY_SYNTAX TRUE // If TRUE, parties are defined in "competitive syntax". #define AUTO_SCROLL_TEXT FALSE // If TRUE, text will automatically scroll to the next line after NUM_FRAMES_AUTO_SCROLL_DELAY. Players can still press A_BUTTON or B_BUTTON to scroll on their own. #define NUM_FRAMES_AUTO_SCROLL_DELAY 49 - // Measurement system constants to be used for UNITS #define UNITS_IMPERIAL 0 // Inches, feet, pounds #define UNITS_METRIC 1 // meters, kilograms diff --git a/include/config/pokedex_plus_hgss.h b/include/config/pokedex_plus_hgss.h new file mode 100644 index 0000000000..667a4bad50 --- /dev/null +++ b/include/config/pokedex_plus_hgss.h @@ -0,0 +1,11 @@ +#ifndef GUARD_CONFIG_POKEDEX_PLUS_HGSS_H +#define GUARD_CONFIG_POKEDEX_PLUS_HGSS_H + +#define POKEDEX_PLUS_HGSS FALSE // If TRUE, enables the custom HGSS style Pokedex. +#define HGSS_DECAPPED FALSE // If TRUE, uses decapped gfx and strings. +#define HGSS_DARK_MODE FALSE // If TRUE, enables dark mode. +#define HGSS_HIDE_UNSEEN_EVOLUTION_NAMES FALSE // If TRUE, hides evolution mon names. +#define HGSS_SORT_TMS_BY_NUM FALSE // If TRUE, sorts the TMS in HGSS Dex by TM number, rather than alphabetically. +#define HGSS_SHOW_EGG_MOVES_FOR_EVOS FALSE // If TRUE, shows Egg Moves for evolved Pokémon too. + +#endif // GUARD_CONFIG_POKEDEX_PLUS_HGSS_H diff --git a/include/config/pokemon.h b/include/config/pokemon.h index 3288065f7c..ee169c79e1 100644 --- a/include/config/pokemon.h +++ b/include/config/pokemon.h @@ -62,7 +62,8 @@ #define P_SHOW_DYNAMIC_TYPES FALSE // If TRUE, all moves with dynamic type changes will be reflected as their current type in battle/summary screens instead of just select ones like in vanilla. // Learnset helper toggles -#define P_LEARNSET_HELPER_TEACHABLE TRUE // If TRUE, teachable_learnsets.h will be populated by tools/learnset_helpers/teachable.py using the included JSON files based on available TMs and tutors. +#define P_LEARNSET_HELPER_TEACHABLE TRUE // If TRUE, teachable_learnsets.h will be populated by tools/learnset_helpers/make_teachables.py using the included JSON files based on available TMs and tutors. +#define P_TUTOR_MOVES_ARRAY TRUE // If TRUE, generates a gTutorMoves array automatically using make_teachables.py. (generally not needed, but the HGSS Pokedex has an optional use for it) // Flag settings // To use the following features in scripting, replace the 0s with the flag ID you're assigning it to. diff --git a/include/pokemon.h b/include/pokemon.h index 830182a324..ac8cdcc445 100644 --- a/include/pokemon.h +++ b/include/pokemon.h @@ -635,6 +635,9 @@ extern const struct SpriteTemplate gBattlerSpriteTemplates[]; extern const u32 sExpCandyExperienceTable[]; extern const struct Ability gAbilitiesInfo[]; extern const struct NatureInfo gNaturesInfo[]; +#if P_TUTOR_MOVES_ARRAY +extern const u16 gTutorMoves[]; +#endif // P_TUTOR_MOVES_ARRAY void ZeroBoxMonData(struct BoxPokemon *boxMon); void ZeroMonData(struct Pokemon *mon); diff --git a/src/data/.gitignore b/src/data/.gitignore index f9205e5627..1a13fa3eeb 100755 --- a/src/data/.gitignore +++ b/src/data/.gitignore @@ -1,3 +1,4 @@ wild_encounters.h region_map/region_map_entries.h region_map/porymap_config.json +tutor_moves.h \ No newline at end of file diff --git a/src/pokedex.c b/src/pokedex.c index 31d60c55bc..09cc8e8d9a 100644 --- a/src/pokedex.c +++ b/src/pokedex.c @@ -30,6 +30,7 @@ #include "window.h" #include "constants/rgb.h" #include "constants/songs.h" +#include "config/pokedex_plus_hgss.h" enum { diff --git a/src/pokedex_area_screen.c b/src/pokedex_area_screen.c index 93afc0af7a..1c8cebb7ea 100644 --- a/src/pokedex_area_screen.c +++ b/src/pokedex_area_screen.c @@ -25,6 +25,7 @@ #include "constants/region_map_sections.h" #include "constants/rgb.h" #include "constants/songs.h" +#include "config/pokedex_plus_hgss.h" // There are two types of indicators for the area screen to show where a Pokémon can occur: // - Area glows, which highlight any of the maps in MAP_GROUP_TOWNS_AND_ROUTES that have the species. diff --git a/src/pokedex_plus_hgss.c b/src/pokedex_plus_hgss.c index b007979667..d21d661bfb 100644 --- a/src/pokedex_plus_hgss.c +++ b/src/pokedex_plus_hgss.c @@ -49,7 +49,7 @@ #include "constants/party_menu.h" #include "constants/rgb.h" #include "constants/songs.h" - +#include "config/pokedex_plus_hgss.h" enum { @@ -265,9 +265,6 @@ static const u32 sPokedexPlusHGSS_ScreenSearchHoenn_Tilemap[] = INCBIN_U32("grap static const u32 sPokedexPlusHGSS_ScreenSearchNational_Tilemap[] = INCBIN_U32("graphics/pokedex/hgss/tilemap_search_screen_national.bin.lz"); #define SCROLLING_MON_X 146 -#define HGSS_DECAPPED FALSE -#define HGSS_DARK_MODE FALSE -#define HGSS_HIDE_UNSEEN_EVOLUTION_NAMES FALSE // For scrolling search parameter #define MAX_SEARCH_PARAM_ON_SCREEN 6 @@ -405,7 +402,7 @@ struct PokedexView u16 monSpriteIds[MAX_MONS_ON_SCREEN]; u8 typeIconSpriteIds[2]; u16 moveSelected; - u8 movesTotal; + u16 movesTotal; u8 statBarsSpriteId; u8 statBarsBgSpriteId; bool8 justScrolled; @@ -5066,78 +5063,124 @@ static void PrintStatsScreen_DestroyMoveItemIcon(u8 taskId) DestroySprite(&gSprites[gTasks[taskId].data[3]]); //Destroy item icon } +static u16 AddTMTutorMoves(u16 species, u16 movesTotal, u8 *numTMHMMoves, u8 *numTutorMoves) +{ + u16 i, move; + bool8 isTMMove[MOVES_COUNT] = {0}; + const u16 *teachableLearnset = GetSpeciesTeachableLearnset(species); + + // TM Moves + if (HGSS_SORT_TMS_BY_NUM) + { + for (i = 0; i < NUM_TECHNICAL_MACHINES + NUM_HIDDEN_MACHINES; i++) + { + move = ItemIdToBattleMoveId(ITEM_TM01 + i); + if (move != MOVE_NONE && CanLearnTeachableMove(species, move)) + { + isTMMove[move] = TRUE; + sStatsMovesTMHM_ID[*numTMHMMoves] = ITEM_TM01 + i; + (*numTMHMMoves)++; + sStatsMoves[movesTotal] = move; + movesTotal++; + } + } + } + else + { + for (i = 0; teachableLearnset[i] != MOVE_UNAVAILABLE; i++) + { + move = teachableLearnset[i]; + for (u16 j = 0; j < NUM_TECHNICAL_MACHINES + NUM_HIDDEN_MACHINES; j++) + { + if (ItemIdToBattleMoveId(ITEM_TM01 + j) == move) + { + isTMMove[move] = TRUE; + sStatsMovesTMHM_ID[*numTMHMMoves] = ITEM_TM01 + j; + (*numTMHMMoves)++; + sStatsMoves[movesTotal] = move; + movesTotal++; + break; + } + } + } + } + + // Tutor Moves +#if P_TUTOR_MOVES_ARRAY + for (i = 0; gTutorMoves[i] != MOVE_UNAVAILABLE; i++) + { + move = gTutorMoves[i]; + if (!isTMMove[move] && CanLearnTeachableMove(species, move)) + { + sStatsMoves[movesTotal] = move; + movesTotal++; + (*numTutorMoves)++; + } + } +#else + for (i = 0; teachableLearnset[i] != MOVE_UNAVAILABLE; i++) + { + move = teachableLearnset[i]; + if (!isTMMove[move] && CanLearnTeachableMove(species, move)) + { + sStatsMoves[movesTotal] = move; + movesTotal++; + (*numTutorMoves)++; + } + } +#endif + return movesTotal; +} + static bool8 CalculateMoves(void) { u16 species = NationalPokedexNumToSpeciesHGSS(sPokedexListItem->dexNum); - const u16 *teachableLearnset = GetSpeciesTeachableLearnset(species); u16 statsMovesEgg[EGG_MOVES_ARRAY_COUNT] = {0}; u16 statsMovesLevelUp[MAX_LEVEL_UP_MOVES] = {0}; - u16 move; u8 numEggMoves = 0; u8 numLevelUpMoves = 0; u8 numTMHMMoves = 0; u8 numTutorMoves = 0; u16 movesTotal = 0; - u8 i,j; + u8 i; - // Mega pokemon don't have distinct learnsets from their base form; so use base species for calculation - if (species >= SPECIES_VENUSAUR_MEGA && species <= SPECIES_GROUDON_PRIMAL) + // Mega and Gmax Pokémon don't have distinct learnsets from their base form; so use base species for calculation + if (gSpeciesInfo[species].isMegaEvolution || gSpeciesInfo[species].isGigantamax) species = GetFormSpeciesId(species, 0); - //Calculate amount of Egg and LevelUp moves - numEggMoves = GetEggMovesBySpecies(species, statsMovesEgg); - numLevelUpMoves = GetLevelUpMovesBySpecies(species, statsMovesLevelUp); + // Egg moves + if (HGSS_SHOW_EGG_MOVES_FOR_EVOS) + { + u16 preSpecies = species; + while (preSpecies != SPECIES_NONE) + { + numEggMoves = GetEggMovesBySpecies(preSpecies, statsMovesEgg); + preSpecies = GetSpeciesPreEvolution(preSpecies); + } + } + else + { + numEggMoves = GetEggMovesBySpecies(species, statsMovesEgg); + } - //Egg moves - for (i=0; i < numEggMoves; i++) + for (i = 0; i < numEggMoves; i++) { sStatsMoves[movesTotal] = statsMovesEgg[i]; movesTotal++; } - //Level up moves - for (i=0; i < numLevelUpMoves; i++) + // Level up moves + numLevelUpMoves = GetLevelUpMovesBySpecies(species, statsMovesLevelUp); + for (i = 0; i < numLevelUpMoves; i++) { sStatsMoves[movesTotal] = statsMovesLevelUp[i]; movesTotal++; } - for (i = 0; teachableLearnset[i] != MOVE_UNAVAILABLE; i++) - { - move = teachableLearnset[i]; - for (j = 0; j < NUM_TECHNICAL_MACHINES + NUM_HIDDEN_MACHINES; j++) - { - if (ItemIdToBattleMoveId(ITEM_TM01 + j) == move) - { - sStatsMovesTMHM_ID[numTMHMMoves] = (ITEM_TM01 + j); - numTMHMMoves++; - - sStatsMoves[movesTotal] = move; - movesTotal++; - break; - } - } - } - - for (i = 0; teachableLearnset[i] != MOVE_UNAVAILABLE; i++) - { - move = teachableLearnset[i]; - for (j = 0; j < NUM_TECHNICAL_MACHINES + NUM_HIDDEN_MACHINES; j++) - { - if (ItemIdToBattleMoveId(ITEM_TM01 + j) == move) - break; - } - - if (j >= NUM_TECHNICAL_MACHINES + NUM_HIDDEN_MACHINES) - { - numTutorMoves++; - - sStatsMoves[movesTotal] = move; - movesTotal++; - } - } + // TM and Tutor moves + movesTotal = AddTMTutorMoves(species, movesTotal, &numTMHMMoves, &numTutorMoves); sPokedexView->numEggMoves = numEggMoves; sPokedexView->numLevelUpMoves = numLevelUpMoves; @@ -5154,8 +5197,8 @@ static void PrintStatsScreen_Moves_Top(u8 taskId) u8 numLevelUpMoves = sPokedexView->numLevelUpMoves; u8 numTMHMMoves = sPokedexView->numTMHMMoves; u8 numTutorMoves = sPokedexView->numTutorMoves; - u8 movesTotal = sPokedexView->movesTotal; - u8 selected = sPokedexView->moveSelected; + u16 movesTotal = sPokedexView->movesTotal; + u16 selected = sPokedexView->moveSelected; u8 level; u8 moves_x = 5; u8 moves_y = 3; @@ -5231,7 +5274,7 @@ static void PrintStatsScreen_Moves_Top(u8 taskId) static void PrintStatsScreen_Moves_Description(u8 taskId) { - u8 selected = sPokedexView->moveSelected; + u16 selected = sPokedexView->moveSelected; u16 move; u8 moves_x = 5; u8 moves_y = 5; diff --git a/src/pokemon.c b/src/pokemon.c index de9893f6bd..9a3869c01f 100644 --- a/src/pokemon.c +++ b/src/pokemon.c @@ -93,6 +93,9 @@ EWRAM_DATA static u8 sTriedEvolving = 0; EWRAM_DATA u16 gFollowerSteps = 0; #include "data/abilities.h" +#if P_TUTOR_MOVES_ARRAY +#include "data/tutor_moves.h" +#endif // P_TUTOR_MOVES_ARRAY // Used in an unreferenced function in RS. // Unreferenced here and in FRLG. diff --git a/tools/learnset_helpers/make_teachables.py b/tools/learnset_helpers/make_teachables.py index 6507610f44..6f3b186b68 100644 --- a/tools/learnset_helpers/make_teachables.py +++ b/tools/learnset_helpers/make_teachables.py @@ -39,6 +39,7 @@ TMHM_MACRO_PAT = re.compile(r"F\((\w+)\)") UNIVERSAL_MOVES_PAT = re.compile(r"static const u16 sUniversalMoves\[\]\s*=\s*{((.|\n)*?)\n};") TEACHABLE_ARRAY_DECL_PAT = re.compile(r"(?Pstatic const u16 s(?P\w+)TeachableLearnset\[\]) = {[\s\S]*?};") SNAKIFY_PAT = re.compile(r"(?!^)([A-Z]+)") +TUTOR_ARRAY_ENABLED_PAT = re.compile(r"#define\s+P_TUTOR_MOVES_ARRAY\s+(?P[^ ]*)") def enabled() -> bool: @@ -134,6 +135,32 @@ def prepare_output(all_learnables: dict[str, set[str]], repo_teachables: set[str return new +def create_tutor_moves_array(tutors: list[str]) -> None: + """ + Generate gTutorMoves[] if P_TUTOR_MOVES_ARRAY is enabled. + """ + # Check if the config is enabled + with open("./include/config/pokemon.h", "r") as cfg_pokemon_fp: + cfg_pokemon = cfg_pokemon_fp.read() + cfg_defined = TUTOR_ARRAY_ENABLED_PAT.search(cfg_pokemon) + if not (cfg_defined and cfg_defined.group("cfg_val") in ("TRUE", "1")): + return + + # If enabled, generate the tutor moves array + 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! + + const u16 gTutorMoves[] = { + """) + + lines = [f" {move}," for move in sorted(tutors)] + lines.append(" MOVE_UNAVAILABLE\n};\n") + + with open("./src/data/tutor_moves.h", "w") as f: + f.write(header + "\n".join(lines)) + + def prepare_header(h_align: int, tmshms: list[str], tutors: list[str], universals: list[str]) -> str: universals_title = "Near-universal moves found from sUniversalMoves:" tmhm_title = "TM/HM moves found in \"include/constants/tms_hms.h\":" @@ -189,6 +216,8 @@ def main(): chain(repo_tms, repo_tutors) )) + create_tutor_moves_array(repo_tutors) + h_align = max(map(lambda move: len(move), chain(repo_universals, repo_teachables))) + 2 header = prepare_header(h_align, repo_tms, repo_tutors, repo_universals)