From 6dfcde7d48741b9d4e1604272dbfa697c778178b Mon Sep 17 00:00:00 2001 From: Frank DeBlasio <35279583+fdeblasio@users.noreply.github.com> Date: Mon, 12 Jan 2026 11:45:44 -0500 Subject: [PATCH] Decoupled trainer gender from encounter music (#8892) --- include/constants/trainers.h | 23 ++++++++++++----------- include/data.h | 3 ++- src/battle_main.c | 4 ++-- src/battle_partner.c | 2 +- src/pokemon.c | 4 ++-- tools/trainerproc/main.c | 21 +++++++++------------ 6 files changed, 28 insertions(+), 29 deletions(-) diff --git a/include/constants/trainers.h b/include/constants/trainers.h index 77a7a5d623..f96ba5e7df 100644 --- a/include/constants/trainers.h +++ b/include/constants/trainers.h @@ -359,22 +359,23 @@ enum TrainerClassID TRAINER_CLASS_COUNT, }; -#define TRAINER_ENCOUNTER_MUSIC_MALE 0 // standard male encounter music -#define TRAINER_ENCOUNTER_MUSIC_FEMALE 1 // standard female encounter music -#define TRAINER_ENCOUNTER_MUSIC_GIRL 2 // used for male Tubers and Young Couples too -#define TRAINER_ENCOUNTER_MUSIC_SUSPICIOUS 3 -#define TRAINER_ENCOUNTER_MUSIC_INTENSE 4 -#define TRAINER_ENCOUNTER_MUSIC_COOL 5 +#define TRAINER_ENCOUNTER_MUSIC_MALE 0 // Used for Youngsters, Bug Catchers, male School Kids, Sailors, male running and cycling Triathletes, male Pokémon Breeders, and Campers +#define TRAINER_ENCOUNTER_MUSIC_FEMALE 1 // Used for Lasses, Ladies, Beauties, Aroma Ladies, female running and cycling Triathletes, female Pokémon Breeders, and Parasol Ladies +#define TRAINER_ENCOUNTER_MUSIC_GIRL 2 // Used for Tubers, Picnickers, female School Kids, and Young Couples +#define TRAINER_ENCOUNTER_MUSIC_SUSPICIOUS 3 // Used for Ninja Boys, Collectors, Hex Maniacs, Poké Maniacs, and Bug Maniacs +#define TRAINER_ENCOUNTER_MUSIC_INTENSE 4 // Used for Guitarists, Psychics, Black Belts, Battle Girls, Dragon Tamers, Experts, and Old Couples +#define TRAINER_ENCOUNTER_MUSIC_COOL 5 // Used for Cool Trainers, Pokémon Rangers, and Bird Keepers #define TRAINER_ENCOUNTER_MUSIC_AQUA 6 #define TRAINER_ENCOUNTER_MUSIC_MAGMA 7 -#define TRAINER_ENCOUNTER_MUSIC_SWIMMER 8 -#define TRAINER_ENCOUNTER_MUSIC_TWINS 9 // used for other trainer classes too +#define TRAINER_ENCOUNTER_MUSIC_SWIMMER 8 // Used for Swimmers, swimming Triathletes, and Sis and Bros +#define TRAINER_ENCOUNTER_MUSIC_TWINS 9 // Used for Twins, Poké Fans, and Sr. and Jr.s #define TRAINER_ENCOUNTER_MUSIC_ELITE_FOUR 10 -#define TRAINER_ENCOUNTER_MUSIC_HIKER 11 // used for other trainer classes too +#define TRAINER_ENCOUNTER_MUSIC_HIKER 11 // Used for Hikers, Ruin Maniacs, Kindlers, and Fishermen #define TRAINER_ENCOUNTER_MUSIC_INTERVIEWER 12 -#define TRAINER_ENCOUNTER_MUSIC_RICH 13 // Rich Boys and Gentlemen +#define TRAINER_ENCOUNTER_MUSIC_RICH 13 // Used for Rich Boys and Gentlemen -#define F_TRAINER_FEMALE (1 << 7) +#define TRAINER_GENDER_MALE 0 +#define TRAINER_GENDER_FEMALE 1 // Trainer party defines #define TRAINER_MON_MALE 1 diff --git a/include/data.h b/include/data.h index d7698a394b..d81bdf473e 100644 --- a/include/data.h +++ b/include/data.h @@ -119,7 +119,8 @@ struct Trainer u16 items[MAX_TRAINER_ITEMS]; struct StartingStatuses startingStatus; // this trainer starts a battle with a given status. see include/constants/battle.h for values u8 trainerClass; - u8 encounterMusic_gender; // last bit is gender + u8 encounterMusic:7; + u8 gender:1; enum TrainerPicID trainerPic; u8 trainerName[TRAINER_NAME_LENGTH + 1]; u8 battleType:2; diff --git a/src/battle_main.c b/src/battle_main.c index 660de0769a..7aa24bbcb0 100644 --- a/src/battle_main.c +++ b/src/battle_main.c @@ -1905,7 +1905,7 @@ u8 CreateNPCTrainerPartyFromTrainer(struct Pokemon *party, const struct Trainer if (trainer->battleType != TRAINER_BATTLE_TYPE_SINGLES) personalityValue = 0x80; - else if (trainer->encounterMusic_gender & F_TRAINER_FEMALE) + else if (trainer->gender == TRAINER_GENDER_FEMALE) personalityValue = 0x78; // Use personality more likely to result in a female Pokémon else personalityValue = 0x88; // Use personality more likely to result in a male Pokémon @@ -3824,7 +3824,7 @@ static void TryDoEventsBeforeFirstTurn(void) if (ShouldDoTrainerSlide(GetBattlerAtPosition(B_POSITION_OPPONENT_RIGHT), TRAINER_SLIDE_BEFORE_FIRST_TURN)) { // Ensures only trainer A slide is played in single-trainer doubles (B == A / B == TRAINER_NONE) and 2v1 multibattles (B == 0xFFFF) - if (!((TRAINER_BATTLE_PARAM.opponentB == TRAINER_BATTLE_PARAM.opponentA) + if (!((TRAINER_BATTLE_PARAM.opponentB == TRAINER_BATTLE_PARAM.opponentA) || (TRAINER_BATTLE_PARAM.opponentB == TRAINER_NONE) || (TRAINER_BATTLE_PARAM.opponentB == 0xFFFF))) { diff --git a/src/battle_partner.c b/src/battle_partner.c index 5b07483dc7..14f8d562c9 100644 --- a/src/battle_partner.c +++ b/src/battle_partner.c @@ -113,7 +113,7 @@ void FillPartnerParty(u16 trainerId) StringCopy(trainerName, gBattlePartners[difficulty][trainerId - TRAINER_PARTNER(PARTNER_NONE)].trainerName); SetMonData(&gPlayerParty[i + 3], MON_DATA_OT_NAME, trainerName); - j = gBattlePartners[difficulty][SanitizeTrainerId(trainerId - TRAINER_PARTNER(PARTNER_NONE))].encounterMusic_gender >> 7; + j = gBattlePartners[difficulty][SanitizeTrainerId(trainerId - TRAINER_PARTNER(PARTNER_NONE))].gender; SetMonData(&gPlayerParty[i + 3], MON_DATA_OT_GENDER, &j); } } diff --git a/src/pokemon.c b/src/pokemon.c index 739885e1e3..889b24fcf4 100644 --- a/src/pokemon.c +++ b/src/pokemon.c @@ -5087,7 +5087,7 @@ u8 GetTrainerEncounterMusicId(u16 trainerOpponentId) else if (InTrainerHillChallenge()) return GetTrainerEncounterMusicIdInTrainerHill(trainerOpponentId); else - return gTrainers[difficulty][sanitizedTrainerId].encounterMusic_gender & (F_TRAINER_FEMALE - 1); + return gTrainers[difficulty][sanitizedTrainerId].encounterMusic; } u16 ModifyStatByNature(u8 nature, u16 stat, enum Stat statIndex) @@ -5617,7 +5617,7 @@ u32 GetRelearnerTMMoves(struct Pokemon *mon, u16 *moves) if (!CanLearnTeachableMove(species, move)) continue; - + if (!MonKnowsMove(mon, move)) moves[numMoves++] = move; } diff --git a/tools/trainerproc/main.c b/tools/trainerproc/main.c index 9f874050db..a530bac4c5 100644 --- a/tools/trainerproc/main.c +++ b/tools/trainerproc/main.c @@ -115,7 +115,7 @@ struct Trainer struct String encounter_music; int encounter_music_line; - enum Gender gender; + struct String gender; int gender_line; struct String pic; @@ -1206,8 +1206,7 @@ static bool parse_trainer(struct Parser *p, const struct Parsed *parsed, struct if (trainer->gender_line) any_error = !set_show_parse_error(p, key.location, "duplicate 'Gender'"); trainer->gender_line = value.location.line; - if (!token_gender(p, &value, &trainer->gender)) - any_error = !show_parse_error(p); + trainer->gender = token_string(&value); } else if (is_literal_token(&key, "Pic")) { @@ -1825,23 +1824,21 @@ static void fprint_trainers(const char *output_path, FILE *f, struct Parsed *par fprintf(f, ",\n"); } - fprintf(f, " .encounterMusic_gender =\n"); - if (trainer->gender == GENDER_FEMALE) + if (!is_empty_string(trainer->gender)) { fprintf(f, "#line %d\n", trainer->gender_line); - fprintf(f, "F_TRAINER_FEMALE | \n"); + fprintf(f, " .gender = "); + fprint_constant(f, "TRAINER_GENDER", trainer->gender); + fprintf(f, ",\n"); } + if (!is_empty_string(trainer->encounter_music)) { fprintf(f, "#line %d\n", trainer->encounter_music_line); - fprintf(f, " "); + fprintf(f, " .encounterMusic = "); fprint_constant(f, "TRAINER_ENCOUNTER_MUSIC", trainer->encounter_music); + fprintf(f, ",\n"); } - else - { - fprintf(f, "0"); - } - fprintf(f, ",\n"); if (trainer->items_n > 0) {