Make movelist calculations happen during compilation instead of runtime (#7967)
Some checks are pending
CI / build (push) Waiting to run
CI / allcontributors (push) Waiting to run

This commit is contained in:
FosterProgramming 2025-10-20 15:31:42 +02:00 committed by GitHub
parent 786859b6bb
commit eac5da89ad
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
20 changed files with 16937 additions and 10374 deletions

View File

@ -212,6 +212,7 @@ LEARNSET_HELPERS_DIR := $(TOOLS_DIR)/learnset_helpers
LEARNSET_HELPERS_DATA_DIR := $(LEARNSET_HELPERS_DIR)/porymoves_files
LEARNSET_HELPERS_BUILD_DIR := $(LEARNSET_HELPERS_DIR)/build
ALL_LEARNABLES_JSON := $(LEARNSET_HELPERS_BUILD_DIR)/all_learnables.json
ALL_TUTORS_JSON := $(LEARNSET_HELPERS_BUILD_DIR)/all_tutors.json
# wild_encounters.h is generated by a Python script
WILD_ENCOUNTERS_TOOL_DIR := $(TOOLS_DIR)/wild_encounters
@ -411,6 +412,8 @@ clean-generated:
@echo "rm -f <AUTO_GEN_TARGETS>"
@rm -f $(ALL_LEARNABLES_JSON)
@echo "rm -f <ALL_LEARNABLES_JSON>"
@rm -f $(ALL_TUTORS_JSON)
@echo "rm -f <ALL_TUTORS_JSON>"
$(C_BUILDDIR)/librfu_intr.o: CFLAGS := -mthumb-interwork -O2 -mabi=apcs-gnu -mtune=arm7tdmi -march=armv4t -fno-toplevel-reorder -Wno-pointer-to-int-cast
$(C_BUILDDIR)/berry_crush.o: override CFLAGS += -Wno-address-of-packed-member
@ -500,7 +503,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 $(INCLUDE_DIRS)/config/pokemon.h $(C_SUBDIR)/pokemon.c
TEACHABLE_DEPS := $(ALL_LEARNABLES_JSON) $(ALL_TUTORS_JSON) $(INCLUDE_DIRS)/constants/tms_hms.h $(INCLUDE_DIRS)/config/pokemon.h $(INCLUDE_DIRS)/config/pokedex_plus_hgss.h $(LEARNSET_HELPERS_DIR)/make_teachables.py
$(LEARNSET_HELPERS_BUILD_DIR):
@mkdir -p $@
@ -508,8 +511,11 @@ $(LEARNSET_HELPERS_BUILD_DIR):
$(ALL_LEARNABLES_JSON): $(wildcard $(LEARNSET_HELPERS_DATA_DIR)/*.json) | $(LEARNSET_HELPERS_BUILD_DIR)
python3 $(LEARNSET_HELPERS_DIR)/make_learnables.py $(LEARNSET_HELPERS_DATA_DIR) $@
$(ALL_TUTORS_JSON): $(shell find data/ -type f -name '*.inc') $(LEARNSET_HELPERS_DIR)/make_tutors.py
python3 $(LEARNSET_HELPERS_DIR)/make_tutors.py $@
$(DATA_SRC_SUBDIR)/pokemon/teachable_learnsets.h: $(TEACHABLE_DEPS)
python3 $(LEARNSET_HELPERS_DIR)/make_teachables.py $<
python3 $(LEARNSET_HELPERS_DIR)/make_teachables.py $< $(ALL_TUTORS_JSON)
# Linker script
LD_SCRIPT := ld_script_modern.ld

View File

@ -64,7 +64,6 @@
// Learnset helper toggles
#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 FALSE // 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.

View File

@ -0,0 +1,34 @@
#ifndef GUARD_CONSTANTS_TEACHING_TYPES_H
#define GUARD_CONSTANTS_TEACHING_TYPES_H
/*
Teaching Types are not used directly by the ROM but they are used by
make_teachables.py to generate teachable learnsets. To save ROM space, the info is
stored in 1 bit and the python script reads the name of the define.
This means we can have multiple "modes" that define to 1.
You can add additional teaching types but they would need to be described in the
python script.
*/
/* DEFAULT_LEARNING
Vanilla uses: most pokemon
Allow a pokemon to learn all universal moves
*/
#define DEFAULT_LEARNING 0
/* TM_ILLITERATE
Vanilla uses: pokemon with "gimmick" moveset (Ditto, Smeargle, Magikarp, ...)
Pokemon can't learn any universal moves (unless it was added to their teachable learnset)
*/
#define TM_ILLITERATE 1
/* ALL_TEACHABLES
Vanilla uses: Mew
Allows a pokemon to learn almost every teachable move (whether from TM or tutors)
Some moves are excluded, they are listed in SignatureTeachables
*/
#define ALL_TEACHABLES 1
#endif // GUARD_CONSTANTS_TEACHING_TYPES_H

View File

@ -1,6 +1,8 @@
#ifndef GUARD_POKEDEX_PLUS_HGSS_H
#define GUARD_POKEDEX_PLUS_HGSS_H
extern const u16 gTutorMoves[];
void CB2_OpenPokedexPlusHGSS(void);
void Task_DisplayCaughtMonDexPageHGSS(u8);

View File

@ -505,7 +505,7 @@ struct SpeciesInfo /*0xC4*/
u32 cannotBeTraded:1;
u32 perfectIVCount:3; // This species will always generate with the specified amount of perfect IVs.
u32 dexForceRequired:1; // This species will be taken into account for Pokédex ratings even if they have the "isMythical" flag set.
u32 tmIlliterate:1; // This species will be unable to learn the universal moves.
u32 teachingType:1; // Not used in the ROM but used in compilation (check constants/teaching_types.h for explanations)
u32 isFrontierBanned:1; // This species is not allowed to participate in Battle Frontier facilities.
u32 padding4:11;
// Shadow settings
@ -700,9 +700,8 @@ extern const struct SpriteTemplate gBattlerSpriteTemplates[];
extern const u32 sExpCandyExperienceTable[];
extern const struct AbilityInfo 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);

View File

@ -0,0 +1,33 @@
{
"universalMoves":
[
"MOVE_BIDE",
"MOVE_FRUSTRATION",
"MOVE_HIDDEN_POWER",
"MOVE_MIMIC",
"MOVE_NATURAL_GIFT",
"MOVE_RAGE",
"MOVE_RETURN",
"MOVE_SECRET_POWER",
"MOVE_SUBSTITUTE",
"MOVE_TERA_BLAST"
],
"signatureTeachables":
[
"MOVE_BADDY_BAD",
"MOVE_BOUNCY_BUBBLE",
"MOVE_BUZZY_BUZZ",
"MOVE_DRAGON_ASCENT",
"MOVE_FLOATY_FALL",
"MOVE_FREEZY_FROST",
"MOVE_GLITZY_GLOW",
"MOVE_RELIC_SONG",
"MOVE_SAPPY_SEED",
"MOVE_SECRET_SWORD",
"MOVE_SIZZLY_SLIDE",
"MOVE_SPARKLY_SWIRL",
"MOVE_SPLISHY_SPLASH",
"MOVE_VOLT_TACKLE",
"MOVE_ZIPPY_ZAP"
]
}

View File

@ -1,4 +1,5 @@
#include "constants/abilities.h"
#include "constants/teaching_types.h"
#include "species_info/shared_dex_text.h"
#include "species_info/shared_front_pic_anims.h"

View File

@ -1188,7 +1188,7 @@ const struct SpeciesInfo gSpeciesInfoGen1[] =
gOverworldPalette_Caterpie,
gShinyOverworldPalette_Caterpie
)
.tmIlliterate = TRUE,
.teachingType = TM_ILLITERATE,
.levelUpLearnset = sCaterpieLevelUpLearnset,
.teachableLearnset = sCaterpieTeachableLearnset,
.evolutions = EVOLUTION({EVO_LEVEL, 7, SPECIES_METAPOD}),
@ -1256,7 +1256,7 @@ const struct SpeciesInfo gSpeciesInfoGen1[] =
gOverworldPalette_Metapod,
gShinyOverworldPalette_Metapod
)
.tmIlliterate = TRUE,
.teachingType = TM_ILLITERATE,
.levelUpLearnset = sMetapodLevelUpLearnset,
.teachableLearnset = sMetapodTeachableLearnset,
.evolutions = EVOLUTION({EVO_LEVEL, 10, SPECIES_BUTTERFREE}),
@ -1499,7 +1499,7 @@ const struct SpeciesInfo gSpeciesInfoGen1[] =
gOverworldPalette_Weedle,
gShinyOverworldPalette_Weedle
)
.tmIlliterate = TRUE,
.teachingType = TM_ILLITERATE,
.levelUpLearnset = sWeedleLevelUpLearnset,
.teachableLearnset = sWeedleTeachableLearnset,
.evolutions = EVOLUTION({EVO_LEVEL, 7, SPECIES_KAKUNA}),
@ -1577,7 +1577,7 @@ const struct SpeciesInfo gSpeciesInfoGen1[] =
gOverworldPalette_Kakuna,
gShinyOverworldPalette_Kakuna
)
.tmIlliterate = TRUE,
.teachingType = TM_ILLITERATE,
.levelUpLearnset = sKakunaLevelUpLearnset,
.teachableLearnset = sKakunaTeachableLearnset,
.evolutions = EVOLUTION({EVO_LEVEL, 10, SPECIES_BEEDRILL}),
@ -17203,7 +17203,7 @@ const struct SpeciesInfo gSpeciesInfoGen1[] =
TRACKS_SPOT,
sAnimTable_Following
)
.tmIlliterate = TRUE,
.teachingType = TM_ILLITERATE,
.levelUpLearnset = sMagikarpLevelUpLearnset,
.teachableLearnset = sMagikarpTeachableLearnset,
.evolutions = EVOLUTION({EVO_LEVEL, 20, SPECIES_GYARADOS}),
@ -17565,7 +17565,7 @@ const struct SpeciesInfo gSpeciesInfoGen1[] =
gOverworldPalette_Ditto,
gShinyOverworldPalette_Ditto
)
.tmIlliterate = TRUE,
.teachingType = TM_ILLITERATE,
.levelUpLearnset = sDittoLevelUpLearnset,
.teachableLearnset = sDittoTeachableLearnset,
},
@ -20239,6 +20239,7 @@ const struct SpeciesInfo gSpeciesInfoGen1[] =
.isMythical = TRUE,
.isFrontierBanned = TRUE,
.perfectIVCount = LEGENDARY_PERFECT_IV_COUNT,
.teachingType = ALL_TEACHABLES,
.levelUpLearnset = sMewLevelUpLearnset,
.teachableLearnset = sMewTeachableLearnset,
},

View File

@ -3970,9 +3970,9 @@ const struct SpeciesInfo gSpeciesInfoGen2[] =
gOverworldPalette_Unown, \
gShinyOverworldPalette_Unown, \
) \
.teachingType = TM_ILLITERATE, \
.levelUpLearnset = sUnownLevelUpLearnset, \
.teachableLearnset = sUnownTeachableLearnset, \
.tmIlliterate = TRUE, \
.formSpeciesIdTable = sUnownFormSpeciesIdTable, \
}
@ -4074,7 +4074,7 @@ const struct SpeciesInfo gSpeciesInfoGen2[] =
gOverworldPalette_Wynaut,
gShinyOverworldPalette_Wynaut
)
.tmIlliterate = TRUE,
.teachingType = TM_ILLITERATE,
.levelUpLearnset = sWynautLevelUpLearnset,
.teachableLearnset = sWynautTeachableLearnset,
.evolutions = EVOLUTION({EVO_LEVEL, 15, SPECIES_WOBBUFFET}),
@ -4163,7 +4163,7 @@ const struct SpeciesInfo gSpeciesInfoGen2[] =
TRACKS_FOOT,
sAnimTable_Following
)
.tmIlliterate = TRUE,
.teachingType = TM_ILLITERATE,
.levelUpLearnset = sWobbuffetLevelUpLearnset,
.teachableLearnset = sWobbuffetTeachableLearnset,
},
@ -7697,7 +7697,7 @@ const struct SpeciesInfo gSpeciesInfoGen2[] =
gOverworldPalette_Smeargle,
gShinyOverworldPalette_Smeargle
)
.tmIlliterate = TRUE,
.teachingType = TM_ILLITERATE,
.levelUpLearnset = sSmeargleLevelUpLearnset,
.teachableLearnset = sSmeargleTeachableLearnset,
},

View File

@ -1501,7 +1501,7 @@ const struct SpeciesInfo gSpeciesInfoGen3[] =
gOverworldPalette_Wurmple,
gShinyOverworldPalette_Wurmple
)
.tmIlliterate = TRUE,
.teachingType = TM_ILLITERATE,
.levelUpLearnset = sWurmpleLevelUpLearnset,
.teachableLearnset = sWurmpleTeachableLearnset,
.evolutions = EVOLUTION({EVO_LEVEL, 7, SPECIES_SILCOON, CONDITIONS({IF_PID_UPPER_MODULO_10_GT, 4})},
@ -1571,7 +1571,7 @@ const struct SpeciesInfo gSpeciesInfoGen3[] =
gOverworldPalette_Silcoon,
gShinyOverworldPalette_Silcoon
)
.tmIlliterate = TRUE,
.teachingType = TM_ILLITERATE,
.levelUpLearnset = sSilcoonLevelUpLearnset,
.teachableLearnset = sSilcoonTeachableLearnset,
.evolutions = EVOLUTION({EVO_LEVEL, 10, SPECIES_BEAUTIFLY}),
@ -1744,7 +1744,7 @@ const struct SpeciesInfo gSpeciesInfoGen3[] =
gOverworldPalette_Cascoon,
gShinyOverworldPalette_Cascoon
)
.tmIlliterate = TRUE,
.teachingType = TM_ILLITERATE,
.levelUpLearnset = sCascoonLevelUpLearnset,
.teachableLearnset = sCascoonTeachableLearnset,
.evolutions = EVOLUTION({EVO_LEVEL, 10, SPECIES_DUSTOX}),
@ -11668,7 +11668,7 @@ const struct SpeciesInfo gSpeciesInfoGen3[] =
gOverworldPalette_Beldum,
gShinyOverworldPalette_Beldum
)
.tmIlliterate = TRUE,
.teachingType = TM_ILLITERATE,
.levelUpLearnset = sBeldumLevelUpLearnset,
.teachableLearnset = sBeldumTeachableLearnset,
.evolutions = EVOLUTION({EVO_LEVEL, 20, SPECIES_METANG}),

View File

@ -1183,7 +1183,7 @@ const struct SpeciesInfo gSpeciesInfoGen4[] =
TRACKS_FOOT,
sAnimTable_Following
)
.tmIlliterate = TRUE,
.teachingType = TM_ILLITERATE,
.levelUpLearnset = sKricketotLevelUpLearnset,
.teachableLearnset = sKricketotTeachableLearnset,
.evolutions = EVOLUTION({EVO_LEVEL, 10, SPECIES_KRICKETUNE}),
@ -1865,7 +1865,7 @@ const struct SpeciesInfo gSpeciesInfoGen4[] =
gOverworldPalette_BurmyPlant,
gShinyOverworldPalette_BurmyPlant
)
.tmIlliterate = TRUE,
.teachingType = TM_ILLITERATE,
.levelUpLearnset = sBurmyLevelUpLearnset,
.teachableLearnset = sBurmyTeachableLearnset,
.formSpeciesIdTable = sBurmyFormSpeciesIdTable,
@ -1934,7 +1934,7 @@ const struct SpeciesInfo gSpeciesInfoGen4[] =
gOverworldPalette_BurmySandy,
gShinyOverworldPalette_BurmySandy
)
.tmIlliterate = TRUE,
.teachingType = TM_ILLITERATE,
.levelUpLearnset = sBurmyLevelUpLearnset,
.teachableLearnset = sBurmyTeachableLearnset,
.formSpeciesIdTable = sBurmyFormSpeciesIdTable,
@ -2003,7 +2003,7 @@ const struct SpeciesInfo gSpeciesInfoGen4[] =
gOverworldPalette_BurmyTrash,
gShinyOverworldPalette_BurmyTrash
)
.tmIlliterate = TRUE,
.teachingType = TM_ILLITERATE,
.levelUpLearnset = sBurmyLevelUpLearnset,
.teachableLearnset = sBurmyTeachableLearnset,
.formSpeciesIdTable = sBurmyFormSpeciesIdTable,
@ -2360,7 +2360,7 @@ const struct SpeciesInfo gSpeciesInfoGen4[] =
TRACKS_FOOT,
sAnimTable_Following
)
.tmIlliterate = TRUE,
.teachingType = TM_ILLITERATE,
.levelUpLearnset = sCombeeLevelUpLearnset,
.teachableLearnset = sCombeeTeachableLearnset,
.evolutions = EVOLUTION({EVO_LEVEL, 21, SPECIES_VESPIQUEN, CONDITIONS({IF_GENDER, MON_FEMALE})}),

View File

@ -9394,7 +9394,7 @@ const struct SpeciesInfo gSpeciesInfoGen5[] =
gOverworldPalette_Tynamo,
gShinyOverworldPalette_Tynamo
)
.tmIlliterate = TRUE,
.teachingType = TM_ILLITERATE,
.levelUpLearnset = sTynamoLevelUpLearnset,
.teachableLearnset = sTynamoTeachableLearnset,
.evolutions = EVOLUTION({EVO_LEVEL, 39, SPECIES_EELEKTRIK}),

View File

@ -1169,7 +1169,7 @@ const struct SpeciesInfo gSpeciesInfoGen6[] =
gOverworldPalette_Scatterbug, \
gShinyOverworldPalette_Scatterbug \
) \
.tmIlliterate = TRUE, \
.teachingType = TM_ILLITERATE, \
.levelUpLearnset = sScatterbugLevelUpLearnset, \
.teachableLearnset = sScatterbugTeachableLearnset, \
.eggMoveLearnset = sScatterbugEggMoveLearnset, \
@ -1253,7 +1253,7 @@ const struct SpeciesInfo gSpeciesInfoGen6[] =
gOverworldPalette_Spewpa, \
gShinyOverworldPalette_Spewpa \
) \
.tmIlliterate = TRUE, \
.teachingType = TM_ILLITERATE, \
.levelUpLearnset = sSpewpaLevelUpLearnset, \
.teachableLearnset = sSpewpaTeachableLearnset, \
.formSpeciesIdTable = sSpewpaFormSpeciesIdTable, \

View File

@ -4291,6 +4291,7 @@ const struct SpeciesInfo gSpeciesInfoGen7[] =
gOverworldPalette_Pyukumuku,
gShinyOverworldPalette_Pyukumuku
)
.teachingType = TM_ILLITERATE,
.levelUpLearnset = sPyukumukuLevelUpLearnset,
.teachableLearnset = sPyukumukuTeachableLearnset,
.eggMoveLearnset = sPyukumukuEggMoveLearnset,
@ -5900,7 +5901,7 @@ const struct SpeciesInfo gSpeciesInfoGen7[] =
gShinyOverworldPalette_Cosmog
)
.isLegendary = TRUE,
.tmIlliterate = TRUE,
.teachingType = TM_ILLITERATE,
.isFrontierBanned = TRUE,
.perfectIVCount = LEGENDARY_PERFECT_IV_COUNT,
.levelUpLearnset = sCosmogLevelUpLearnset,
@ -5970,7 +5971,7 @@ const struct SpeciesInfo gSpeciesInfoGen7[] =
gShinyOverworldPalette_Cosmoem
)
.isLegendary = TRUE,
.tmIlliterate = TRUE,
.teachingType = TM_ILLITERATE,
.isFrontierBanned = TRUE,
.perfectIVCount = LEGENDARY_PERFECT_IV_COUNT,
.levelUpLearnset = sCosmoemLevelUpLearnset,

View File

@ -1230,7 +1230,7 @@ const struct SpeciesInfo gSpeciesInfoGen8[] =
gOverworldPalette_Blipbug,
gShinyOverworldPalette_Blipbug
)
.tmIlliterate = TRUE,
.teachingType = TM_ILLITERATE,
.levelUpLearnset = sBlipbugLevelUpLearnset,
.teachableLearnset = sBlipbugTeachableLearnset,
.eggMoveLearnset = sBlipbugEggMoveLearnset,
@ -2461,7 +2461,7 @@ const struct SpeciesInfo gSpeciesInfoGen8[] =
gOverworldPalette_Applin,
gShinyOverworldPalette_Applin
)
.tmIlliterate = TRUE,
.teachingType = TM_ILLITERATE,
.levelUpLearnset = sApplinLevelUpLearnset,
.teachableLearnset = sApplinTeachableLearnset,
.eggMoveLearnset = sApplinEggMoveLearnset,

File diff suppressed because it is too large Load Diff

View File

@ -290,9 +290,6 @@ static EWRAM_DATA u16 sLastSelectedPokemon = 0;
static EWRAM_DATA u8 sPokeBallRotation = 0;
static EWRAM_DATA struct PokedexListItem *sPokedexListItem = NULL;
//Pokedex Plus HGSS_Ui
#define MOVES_COUNT_TOTAL (EGG_MOVES_ARRAY_COUNT + MAX_LEVEL_UP_MOVES + NUM_ALL_MACHINES)
EWRAM_DATA static u16 sStatsMoves[MOVES_COUNT_TOTAL] = {0};
EWRAM_DATA static u16 sStatsMovesTMHM_ID[NUM_ALL_MACHINES] = {0};
struct SearchOptionText
@ -409,8 +406,7 @@ struct PokedexView
u8 categoryIconSpriteId; //Physical/Special/Status category
u8 numEggMoves;
u8 numLevelUpMoves;
u8 numTMHMMoves;
u8 numTutorMoves;
u8 numTeachableMoves;
u8 numPreEvolutions;
struct PokemonStats sPokemonStats;
struct EvoScreenData sEvoScreenData;
@ -4871,7 +4867,7 @@ static void Task_LoadStatsScreen(u8 taskId)
sPokedexView->movesTotal = 0;
sPokedexView->numEggMoves = 0;
sPokedexView->numLevelUpMoves = 0;
sPokedexView->numTMHMMoves = 0;
sPokedexView->numTeachableMoves = 0;
if (CalculateMoves())
gMain.state++;
break;
@ -5059,87 +5055,15 @@ 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_ALL_MACHINES; i++)
{
move = GetTMHMMoveId(i + 1);
if (move != MOVE_NONE && CanLearnTeachableMove(species, move))
{
isTMMove[move] = TRUE;
sStatsMovesTMHM_ID[*numTMHMMoves] = GetTMHMItemId(i + 1);
(*numTMHMMoves)++;
sStatsMoves[movesTotal] = move;
movesTotal++;
}
}
}
else
{
for (i = 0; teachableLearnset[i] != MOVE_UNAVAILABLE; i++)
{
move = teachableLearnset[i];
for (u16 j = 0; j < NUM_ALL_MACHINES; j++)
{
if (GetTMHMMoveId(j + 1) == move)
{
isTMMove[move] = TRUE;
sStatsMovesTMHM_ID[*numTMHMMoves] = GetTMHMItemId(j + 1);
(*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);
u16 statsMovesEgg[EGG_MOVES_ARRAY_COUNT] = {0};
u16 statsMovesLevelUp[MAX_LEVEL_UP_MOVES] = {0};
u8 numEggMoves = 0;
u8 numLevelUpMoves = 0;
u8 numTMHMMoves = 0;
u8 numTutorMoves = 0;
u16 movesTotal = 0;
u8 numTeachableMoves = 0;
u8 i;
// Mega and Gmax Pokémon don't have distinct learnsets from their base form; so use base species for calculation
@ -5161,57 +5085,91 @@ static bool8 CalculateMoves(void)
numEggMoves = GetEggMovesBySpecies(species, statsMovesEgg);
}
for (i = 0; i < numEggMoves; i++)
{
sStatsMoves[movesTotal] = statsMovesEgg[i];
movesTotal++;
}
// Level up moves
numLevelUpMoves = GetLevelUpMovesBySpecies(species, statsMovesLevelUp);
for (i = 0; i < numLevelUpMoves; i++)
{
sStatsMoves[movesTotal] = statsMovesLevelUp[i];
movesTotal++;
}
const struct LevelUpMove *learnset = GetSpeciesLevelUpLearnset(species);
for (i = 0; i < MAX_LEVEL_UP_MOVES && learnset[i].move != LEVEL_UP_MOVE_END; i++)
numLevelUpMoves++;
// TM and Tutor moves
movesTotal = AddTMTutorMoves(species, movesTotal, &numTMHMMoves, &numTutorMoves);
const u16 *teachableLearnset = GetSpeciesTeachableLearnset(species);
for (i = 0; teachableLearnset[i] != MOVE_UNAVAILABLE; i++)
numTeachableMoves++;
sPokedexView->numEggMoves = numEggMoves;
sPokedexView->numLevelUpMoves = numLevelUpMoves;
sPokedexView->numTMHMMoves = numTMHMMoves;
sPokedexView->numTutorMoves = numTutorMoves;
sPokedexView->movesTotal = movesTotal;
sPokedexView->numTeachableMoves = numTeachableMoves;
sPokedexView->movesTotal = (numEggMoves + numLevelUpMoves + numTeachableMoves);
return TRUE;
}
static u16 GetSelectedMove(u32 species, u32 selected)
{
if (selected < sPokedexView->numEggMoves)
return GetSpeciesEggMoves(species)[selected];
selected -= sPokedexView->numEggMoves;
if (selected < sPokedexView->numLevelUpMoves)
return GetSpeciesLevelUpLearnset(species)[selected].move;
selected -= sPokedexView->numLevelUpMoves;
if (selected < sPokedexView->numTeachableMoves)
return GetSpeciesTeachableLearnset(species)[selected];
return MOVE_NONE; //It should never get here but it allows us to visually see errors
}
static void PrintStatsScreen_Moves_Top(u8 taskId)
{
u8 numEggMoves = sPokedexView->numEggMoves;
u8 numLevelUpMoves = sPokedexView->numLevelUpMoves;
u8 numTMHMMoves = sPokedexView->numTMHMMoves;
u8 numTutorMoves = sPokedexView->numTutorMoves;
u16 movesTotal = sPokedexView->movesTotal;
u16 selected = sPokedexView->moveSelected;
u8 level;
u8 moves_x = 5;
u8 moves_y = 3;
u16 move;
u16 item;
u16 species = NationalPokedexNumToSpeciesHGSS(sPokedexListItem->dexNum);
//Move
move = sStatsMoves[selected];
u32 item = ITEM_MASTER_BALL;
u32 species = NationalPokedexNumToSpeciesHGSS(sPokedexListItem->dexNum);
u32 selected = sPokedexView->moveSelected;
u32 move = GetSelectedMove(species, selected);
//Moves selected from move max
ConvertIntToDecimalStringN(gStringVar1, (selected+1), STR_CONV_MODE_RIGHT_ALIGN, 3);
ConvertIntToDecimalStringN(gStringVar2, movesTotal, STR_CONV_MODE_RIGHT_ALIGN, 3);
ConvertIntToDecimalStringN(gStringVar2, sPokedexView->movesTotal, STR_CONV_MODE_RIGHT_ALIGN, 3);
StringExpandPlaceholders(gStringVar1, sText_Stats_MoveSelectedMax);
PrintStatsScreenTextSmallWhite(WIN_STATS_MOVES_TOP, gStringVar1, moves_x-1, moves_y+1);
//Calculate and retrieve correct move from the arrays
if (selected < sPokedexView->numEggMoves)
{
PrintStatsScreenTextSmall(WIN_STATS_MOVES_TOP, gText_ThreeDashes, moves_x + 113, moves_y + 9);
item = ITEM_LUCKY_EGG;
}
else if (selected < (sPokedexView->numLevelUpMoves + sPokedexView->numEggMoves))
{
u32 level = GetSpeciesLevelUpLearnset(species)[selected - sPokedexView->numEggMoves].level;
ConvertIntToDecimalStringN(gStringVar1, level, STR_CONV_MODE_LEFT_ALIGN, 3); //Move learn lvl
PrintStatsScreenTextSmall(WIN_STATS_MOVES_TOP, sText_Stats_MoveLevel, moves_x + 113, moves_y + 3); //Level text
PrintStatsScreenTextSmall(WIN_STATS_MOVES_TOP, gStringVar1, moves_x + 113, moves_y + 14); //Print level
item = ITEM_RARE_CANDY;
}
else if (move)
{
u32 TMHMItemId = ITEM_NONE;
for (u32 i = 0; i < NUM_ALL_MACHINES; i++)
{
if (move == GetTMHMMoveId(i + 1))
TMHMItemId = GetTMHMItemId(i + 1);
}
if (TMHMItemId)
{
CopyItemName(TMHMItemId, gStringVar1); //TM name
PrintStatsScreenTextSmall(WIN_STATS_MOVES_TOP, gStringVar1, moves_x + 113, moves_y + 9);
item = TMHMItemId;
}
else
{
PrintStatsScreenTextSmall(WIN_STATS_MOVES_TOP, gText_ThreeDashes, moves_x + 113, moves_y + 9);
item = ITEM_TEACHY_TV;
}
}
else
{
StringCopy(gStringVar4, gText_CommunicationError);
}
//Move name
StringCopy(gStringVar3, GetMoveName(move));
StringCopyPadded(gStringVar3, gStringVar3, CHAR_SPACE, 20);
@ -5229,37 +5187,6 @@ static void PrintStatsScreen_Moves_Top(u8 taskId)
SetSpriteInvisibility(0, TRUE);
}
//Calculate and retrieve correct move from the arrays
if (selected < numEggMoves)
{
PrintStatsScreenTextSmall(WIN_STATS_MOVES_TOP, gText_ThreeDashes, moves_x + 113, moves_y + 9);
item = ITEM_LUCKY_EGG;
}
else if (selected < (numEggMoves + numLevelUpMoves))
{
level = GetSpeciesLevelUpLearnset(species)[(selected-numEggMoves)].level;
ConvertIntToDecimalStringN(gStringVar1, level, STR_CONV_MODE_LEFT_ALIGN, 3); //Move learn lvl
PrintStatsScreenTextSmall(WIN_STATS_MOVES_TOP, sText_Stats_MoveLevel, moves_x + 113, moves_y + 3); //Level text
PrintStatsScreenTextSmall(WIN_STATS_MOVES_TOP, gStringVar1, moves_x + 113, moves_y + 14); //Print level
item = ITEM_RARE_CANDY;
}
else if (selected < (numEggMoves + numLevelUpMoves + numTMHMMoves))
{
CopyItemName(sStatsMovesTMHM_ID[(selected-numEggMoves-numLevelUpMoves)], gStringVar1); //TM name
PrintStatsScreenTextSmall(WIN_STATS_MOVES_TOP, gStringVar1, moves_x + 113, moves_y + 9);
item = sStatsMovesTMHM_ID[(selected-numEggMoves-numLevelUpMoves)];
}
else if (selected < (numEggMoves + numLevelUpMoves + numTMHMMoves + numTutorMoves))
{
PrintStatsScreenTextSmall(WIN_STATS_MOVES_TOP, gText_ThreeDashes, moves_x + 113, moves_y + 9);
item = ITEM_TEACHY_TV;
}
else
{
StringCopy(gStringVar4, gText_CommunicationError);
item = ITEM_MASTER_BALL;
}
//Egg/TM/Level/Tutor Item Icon
gTasks[taskId].data[3] = AddItemIconSprite(ITEM_TAG, ITEM_TAG, item);
gSprites[gTasks[taskId].data[3]].x2 = 203;
@ -5270,13 +5197,11 @@ static void PrintStatsScreen_Moves_Top(u8 taskId)
static void PrintStatsScreen_Moves_Description(u8 taskId)
{
u16 selected = sPokedexView->moveSelected;
u16 move;
u8 moves_x = 5;
u8 moves_y = 5;
//Move
move = sStatsMoves[selected];
u32 species = NationalPokedexNumToSpeciesHGSS(sPokedexListItem->dexNum);
u32 move = GetSelectedMove(species, sPokedexView->moveSelected);
//Move description
if (gTasks[taskId].data[5] == 0)
@ -5295,6 +5220,7 @@ static void PrintStatsScreen_Moves_BottomText(u8 taskId)
{
u8 moves_x = 8;
u8 moves_y = 3;
if (gTasks[taskId].data[5] == 0)
{
PrintStatsScreenTextSmall(WIN_STATS_MOVES_BOTTOM, gText_Power, moves_x, moves_y);
@ -5311,15 +5237,14 @@ static void PrintStatsScreen_Moves_Bottom(u8 taskId)
{
u8 moves_x = 8;
u8 moves_y = 3;
u8 selected = sPokedexView->moveSelected;
u16 move;
//Contest
u8 contest_effectValue;
u8 contest_appeal = 0;
u8 contest_jam = 0;
//Move
move = sStatsMoves[selected];
u32 species = NationalPokedexNumToSpeciesHGSS(sPokedexListItem->dexNum);
u32 move = GetSelectedMove(species, sPokedexView->moveSelected);
//Power + Accuracy
if (gTasks[taskId].data[5] == 0)

View File

@ -95,9 +95,6 @@ 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.
@ -5581,89 +5578,17 @@ bool8 TryIncrementMonLevel(struct Pokemon *mon)
}
}
static const u16 sUniversalMoves[] =
{
MOVE_BIDE,
MOVE_FRUSTRATION,
MOVE_HIDDEN_POWER,
MOVE_MIMIC,
MOVE_NATURAL_GIFT,
MOVE_RAGE,
MOVE_RETURN,
MOVE_SECRET_POWER,
MOVE_SUBSTITUTE,
MOVE_TERA_BLAST,
};
u8 CanLearnTeachableMove(u16 species, u16 move)
{
const u16 *teachableLearnset = GetSpeciesTeachableLearnset(species);
if (species == SPECIES_EGG)
{
return FALSE;
}
else if (species == SPECIES_MEW)
for (u32 i = 0; teachableLearnset[i] != MOVE_UNAVAILABLE; i++)
{
switch (move)
{
case MOVE_BADDY_BAD:
case MOVE_BOUNCY_BUBBLE:
case MOVE_BUZZY_BUZZ:
case MOVE_DRAGON_ASCENT:
case MOVE_FLOATY_FALL:
case MOVE_FREEZY_FROST:
case MOVE_GLITZY_GLOW:
case MOVE_RELIC_SONG:
case MOVE_SAPPY_SEED:
case MOVE_SECRET_SWORD:
case MOVE_SIZZLY_SLIDE:
case MOVE_SPARKLY_SWIRL:
case MOVE_SPLISHY_SPLASH:
case MOVE_VOLT_TACKLE:
case MOVE_ZIPPY_ZAP:
return FALSE;
default:
if (teachableLearnset[i] == move)
return TRUE;
}
}
else
{
u32 i, j;
const u16 *teachableLearnset = GetSpeciesTeachableLearnset(species);
for (i = 0; i < ARRAY_COUNT(sUniversalMoves); i++)
{
if (sUniversalMoves[i] == move)
{
if (!gSpeciesInfo[species].tmIlliterate)
{
if (move == MOVE_TERA_BLAST && GET_BASE_SPECIES_ID(species) == SPECIES_TERAPAGOS)
return FALSE;
if (GET_BASE_SPECIES_ID(species) == SPECIES_PYUKUMUKU && (move == MOVE_HIDDEN_POWER || move == MOVE_RETURN || move == MOVE_FRUSTRATION))
return FALSE;
return TRUE;
}
else
{
const struct LevelUpMove *learnset = GetSpeciesLevelUpLearnset(species);
if (P_TM_LITERACY < GEN_6)
return FALSE;
for (j = 0; j < MAX_LEVEL_UP_MOVES && learnset[j].move != LEVEL_UP_MOVE_END; j++)
{
if (learnset[j].move == move)
return TRUE;
}
return FALSE;
}
}
}
for (i = 0; teachableLearnset[i] != MOVE_UNAVAILABLE; i++)
{
if (teachableLearnset[i] == move)
return TRUE;
}
return FALSE;
}
return FALSE;
}
u8 GetMoveRelearnerMoves(struct Pokemon *mon, u16 *moves)

View File

@ -33,14 +33,14 @@ import typing
CONFIG_ENABLED_PAT = re.compile(r"#define P_LEARNSET_HELPER_TEACHABLE\s+(?P<cfg_val>[^ ]*)")
INCFILE_HAS_TUTOR_PAT = re.compile(r"special ChooseMonForMoveTutor")
INCFILE_MOVE_PAT = re.compile(r"setvar VAR_0x8005, (MOVE_.*)")
ALPHABETICAL_ORDER_ENABLED_PAT = re.compile(r"#define HGSS_SORT_TMS_BY_NUM\s+(?P<cfg_val>[^ ]*)")
TM_LITTERACY_PAT = re.compile(r"#define P_TM_LITERACY\s+GEN_(?P<cfg_val>[^ ]*)")
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"(?P<decl>static const u16 s(?P<name>\w+)TeachableLearnset\[\]) = {[\s\S]*?};")
MOVE_TUTOR_ARRAY_DECL_PAT = re.compile(r"(?P<decl>const u16 gTutorMoves\[\] = {)[\s\S]*? MOVE_UNAVAILABLE,")
SNAKIFY_PAT = re.compile(r"(?!^)([A-Z]+)")
TUTOR_ARRAY_ENABLED_PAT = re.compile(r"#define\s+P_TUTOR_MOVES_ARRAY\s+(?P<cfg_val>[^ ]*)")
TUTOR_ARRAY_ENABLED_PAT = re.compile(r"#define\s+POKEDEX_PLUS_HGSS\s+(?P<cfg_val>[^ ]*)")
POKEMON_TEACHING_TYPE_PAT = re.compile(r"\{[\s\S]*?(.teachingType\s*=\s*(?P<teaching_type>[A-Z_]+),[\s\S]*?)?\.teachableLearnset\s*=\s*s(?P<name>\w+?)TeachableLearnset[\s\S]*?\}")
def enabled() -> bool:
"""
@ -51,22 +51,6 @@ def enabled() -> bool:
cfg_defined = CONFIG_ENABLED_PAT.search(cfg_pokemon)
return cfg_defined is not None and cfg_defined.group("cfg_val") in ("TRUE", "1")
def extract_repo_tutors() -> typing.Generator[str, None, None]:
"""
Yield MOVE constants which are *likely* assigned to a move tutor. This isn't
foolproof, but it's suitable.
"""
for inc_fname in chain(glob.glob("./data/scripts/*.inc"), glob.glob("./data/maps/*/scripts.inc")):
with open(inc_fname, "r") as inc_fp:
incfile = inc_fp.read()
if not INCFILE_HAS_TUTOR_PAT.search(incfile):
continue
for move in INCFILE_MOVE_PAT.finditer(incfile):
yield move.group(1)
def extract_repo_tms() -> typing.Generator[str, None, None]:
"""
Yield MOVE constants assigned to a TM or HM in the user's repo.
@ -80,22 +64,37 @@ def extract_repo_tms() -> typing.Generator[str, None, None]:
for match in match_it:
yield f"MOVE_{match.group(1)}"
def extract_repo_teaching_types() -> dict[str, str]:
species_teaching_types = {}
for families_fname in sorted(glob.glob("src/data/pokemon/species_info/gen_*_families.h")):
with open(families_fname, "r") as family_fp:
family_file = family_fp.read()
for pokemon in POKEMON_TEACHING_TYPE_PAT.finditer(family_file):
if pokemon.group("teaching_type"):
species_teaching_types[pokemon.group("name")] = pokemon.group("teaching_type")
else:
species_teaching_types[pokemon.group("name")] = "DEFAULT_LEARNING"
return species_teaching_types
def extract_repo_universals() -> list[str]:
"""
Return a list of MOVE constants which are deemed to be universal and can
thus be learned by any species.
"""
with open("./src/pokemon.c", "r") as pokemon_fp:
if match := UNIVERSAL_MOVES_PAT.search(pokemon_fp.read()):
return list(filter(lambda s: s, map(lambda s: s.strip(), match.group(1).split(','))))
return list()
def extract_tm_litteracy_config() -> bool:
config = False
with open("./include/config/pokemon.h", "r") as cfg_pokemon_fp:
cfg_pokemon = cfg_pokemon_fp.read()
cfg_defined = TM_LITTERACY_PAT.search(cfg_pokemon)
if cfg_defined:
cfg_val = cfg_defined.group("cfg_val")
if ((cfg_val == "LATEST") or (int(cfg_val) > 6)):
config = True
return config
def prepare_output(all_learnables: dict[str, set[str]], repo_teachables: set[str], header: str) -> str:
def prepare_output(all_learnables: dict[str, set[str]], tms: list[str], tutors: list[str], special_movesets, header: str) -> str:
"""
Build the file content for teachable_learnsets.h.
"""
repo_teaching_types = extract_repo_teaching_types()
tm_litteracy_config = extract_tm_litteracy_config()
with open("./src/data/pokemon/teachable_learnsets.h", "r") as teachables_fp:
old = teachables_fp.read()
@ -115,13 +114,22 @@ def prepare_output(all_learnables: dict[str, set[str]], repo_teachables: set[str
cursor = match_e + 1
continue
if species_upper == "MEW":
new += old[cursor:match_e + 1] # copy the original content and skip.
cursor = match_e + 1
continue
if repo_teaching_types[species.group("name")] == "ALL_TEACHABLES":
learnables = filter(lambda m: m not in special_movesets["signatureTeachables"], tms + tutors)
elif repo_teaching_types[species.group("name")] == "TM_ILLITERATE":
learnables = all_learnables[species_upper]
if not tm_litteracy_config:
learnables = filter(lambda m: m not in special_movesets["universalMoves"], learnables)
else:
learnables = all_learnables[species_upper] + special_movesets["universalMoves"]
repo_species_teachables = filter(lambda m: m in repo_teachables, all_learnables[species_upper])
part1 = list(filter(lambda m: m in learnables, tms))
part2 = list(filter(lambda m: m in learnables, tutors))
repo_species_teachables = part1 + part2
if species_upper == "TERAPAGOS":
repo_species_teachables = filter(lambda m: m != "MOVE_TERA_BLAST", repo_species_teachables)
repo_species_teachables = list(dict.fromkeys(repo_species_teachables))
new += old[cursor:match_b]
new += "\n".join([
f"{species.group('decl')} = {{",
@ -130,39 +138,23 @@ def prepare_output(all_learnables: dict[str, set[str]], repo_teachables: set[str
])
cursor = match_e + 1
tutors_array = MOVE_TUTOR_ARRAY_DECL_PAT.search(old)
match_b, match_e = tutors_array.span()
new += old[cursor:match_b]
new += "\n".join([
f"{tutors_array.group('decl')}",
f" {joinpat.join(chain(sorted(tutors)))},"
f"\n MOVE_UNAVAILABLE,\n"
])
cursor = match_e + 1
new += old[cursor:]
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:"
universals_title = "Near-universal moves found in data/special_movesets.json:"
tmhm_title = "TM/HM moves found in \"include/constants/tms_hms.h\":"
tutor_title = "Tutor moves found from map scripts:"
h_align = max(h_align, len(universals_title), len(tmhm_title), len(tutor_title))
@ -204,30 +196,38 @@ def main():
quit(1)
SOURCE_LEARNSETS_JSON = pathlib.Path(sys.argv[1])
SOURCE_TUTORS_JSON = pathlib.Path(sys.argv[2])
assert SOURCE_LEARNSETS_JSON.exists(), f"{SOURCE_LEARNSETS_JSON=} does not exist"
assert SOURCE_LEARNSETS_JSON.is_file(), f"{SOURCE_LEARNSETS_JSON=} is not a file"
repo_universals = extract_repo_universals()
assert SOURCE_TUTORS_JSON.exists(), f"{SOURCE_TUTORS_JSON=} does not exist"
assert SOURCE_TUTORS_JSON.is_file(), f"{SOURCE_TUTORS_JSON=} is not a file"
repo_tms = list(extract_repo_tms())
repo_tutors = list(extract_repo_tutors())
repo_teachables = set(filter(
lambda move: move not in set(repo_universals),
chain(repo_tms, repo_tutors)
))
order_alphabetically = False
create_tutor_moves_array(repo_tutors)
with open("./include/config/pokedex_plus_hgss.h", "r") as cfg_pokemon_fp:
cfg_pokemon = cfg_pokemon_fp.read()
cfg_defined = ALPHABETICAL_ORDER_ENABLED_PAT.search(cfg_pokemon)
if cfg_defined is None or cfg_defined.group("cfg_val") in ("FALSE", "0"):
repo_tms = sorted(repo_tms)
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)
with open(SOURCE_TUTORS_JSON, "r") as fp:
repo_tutors = json.load(fp)
with open("src/data/pokemon/special_movesets.json", "r") as file:
special_movesets = json.load(file)
h_align = max(map(lambda move: len(move), chain(special_movesets["universalMoves"], repo_tms, repo_tutors))) + 2
header = prepare_header(h_align, repo_tms, repo_tutors, special_movesets["universalMoves"])
with open(SOURCE_LEARNSETS_JSON, "r") as source_fp:
all_learnables = json.load(source_fp)
content = prepare_output(all_learnables, repo_teachables, header)
content = prepare_output(all_learnables, repo_tms, repo_tutors, special_movesets, header)
with open("./src/data/pokemon/teachable_learnsets.h", "w") as teachables_fp:
teachables_fp.write(content)
if __name__ == "__main__":
main()

View File

@ -0,0 +1,68 @@
from itertools import chain
import glob
import json
import pathlib
import re
import sys
import typing
CONFIG_ENABLED_PAT = re.compile(r"#define P_LEARNSET_HELPER_TEACHABLE\s+(?P<cfg_val>[^ ]*)")
INCFILE_HAS_TUTOR_PAT = re.compile(r"special ChooseMonForMoveTutor")
INCFILE_MOVE_PAT = re.compile(r"setvar VAR_0x8005, (MOVE_.*)")
def enabled() -> bool:
"""
Check if the user has explicitly enabled this opt-in helper.
"""
with open("./include/config/pokemon.h", "r") as cfg_pokemon_fp:
cfg_pokemon = cfg_pokemon_fp.read()
cfg_defined = CONFIG_ENABLED_PAT.search(cfg_pokemon)
return cfg_defined is not None and cfg_defined.group("cfg_val") in ("TRUE", "1")
def extract_repo_tutors() -> typing.Generator[str, None, None]:
"""
Yield MOVE constants which are *likely* assigned to a move tutor. This isn't
foolproof, but it's suitable.
"""
for inc_fname in chain(glob.glob("./data/scripts/*.inc"), glob.glob("./data/maps/*/scripts.inc")):
with open(inc_fname, "r") as inc_fp:
incfile = inc_fp.read()
if not INCFILE_HAS_TUTOR_PAT.search(incfile):
continue
for move in INCFILE_MOVE_PAT.finditer(incfile):
yield move.group(1)
def dump_output(file, data):
with open(file, "w") as fp:
fp.write(data)
def main():
if not enabled():
quit()
if len(sys.argv) < 2:
print("Missing required arguments", file=sys.stderr)
print(__doc__, file=sys.stderr)
quit(1)
OUTPUT_FILE = pathlib.Path(sys.argv[1])
assert OUTPUT_FILE.parent.exists(), f"parent of {OUTPUT_FILE=} does not exist"
new_tutors = json.dumps(sorted(list(extract_repo_tutors())), indent=2)
if OUTPUT_FILE.exists() and OUTPUT_FILE.is_file():
with open(OUTPUT_FILE, "r") as fp:
old_tutors = fp.read()
else:
dump_output(OUTPUT_FILE, new_tutors)
return
if new_tutors != old_tutors:
dump_output(OUTPUT_FILE, new_tutors)
if __name__ == "__main__":
main()