diff --git a/asm/macros/battle_script.inc b/asm/macros/battle_script.inc index bee63ee40e..2cb66a4ab5 100644 --- a/asm/macros/battle_script.inc +++ b/asm/macros/battle_script.inc @@ -1587,6 +1587,10 @@ callnative BS_TryUpperHand .4byte \failInstr .endm + + .macro tryupdaterecoiltracker + callnative BS_TryUpdateRecoilTracker + .endm @ various command changed to more readable macros .macro cancelmultiturnmoves battler:req diff --git a/data/battle_scripts_1.s b/data/battle_scripts_1.s index 22a2ffb411..af53e9f371 100644 --- a/data/battle_scripts_1.s +++ b/data/battle_scripts_1.s @@ -7704,6 +7704,7 @@ BattleScript_DoRecoil:: datahpupdate BS_ATTACKER printstring STRINGID_PKMNHITWITHRECOIL waitmessage B_WAIT_TIME_LONG + tryupdaterecoiltracker tryfaintmon BS_ATTACKER BattleScript_RecoilEnd:: return diff --git a/include/constants/battle_script_commands.h b/include/constants/battle_script_commands.h index 11ef2090df..4e653cf9e6 100644 --- a/include/constants/battle_script_commands.h +++ b/include/constants/battle_script_commands.h @@ -316,8 +316,9 @@ #define MOVEEND_SYMBIOSIS 34 #define MOVEEND_OPPORTUNIST 35 // Occurs after other stat change items/abilities to try and copy the boosts #define MOVEEND_SAME_MOVE_TURNS 36 -#define MOVEEND_CLEAR_BITS 37 -#define MOVEEND_COUNT 38 +#define MOVEEND_SET_EVOLUTION_TRACKER 37 +#define MOVEEND_CLEAR_BITS 38 +#define MOVEEND_COUNT 39 // switch cases #define B_SWITCH_NORMAL 0 diff --git a/include/constants/pokemon.h b/include/constants/pokemon.h index c801c4ea06..13b76ed328 100644 --- a/include/constants/pokemon.h +++ b/include/constants/pokemon.h @@ -291,6 +291,9 @@ #define EVO_MOVE_THREE_SEGMENT 44 // Pokémon levels up, knows specified move, has a personality value with a modulus of 1-99 #define EVO_LEVEL_FAMILY_OF_THREE 45 // Pokémon reaches the specified level with a personality value with a modulus of 0 #define EVO_LEVEL_FAMILY_OF_FOUR 46 // Pokémon reaches the specified level with a personality value with a modulus of 1-99 +#define EVO_LEVEL_MOVE_TWENTY_TIMES 47 // Pokémon levels up after having used a move for at least 20 times +#define EVO_LEVEL_RECOIL_DAMAGE_MALE 48 // Pokémon levels up after having suffered specified amount of non-fainting recoil damage as a male +#define EVO_LEVEL_RECOIL_DAMAGE_FEMALE 49 // Pokémon levels up after having suffered specified amount of non-fainting recoil damage as a female // Evolution 'modes,' for GetEvolutionTargetSpecies #define EVO_MODE_NORMAL 0 diff --git a/include/pokemon.h b/include/pokemon.h index aeed6bacf9..2ca1b09d1a 100644 --- a/include/pokemon.h +++ b/include/pokemon.h @@ -113,6 +113,7 @@ enum { MON_DATA_DYNAMAX_LEVEL, MON_DATA_GIGANTAMAX_FACTOR, MON_DATA_TERA_TYPE, + MON_DATA_EVOLUTION_TRACKER, }; struct PokemonSubstruct0 @@ -134,9 +135,10 @@ struct PokemonSubstruct0 struct PokemonSubstruct1 { u16 move1:11; // 2047 moves. - u16 unused_00:5; + u16 evolutionTracker1:5; u16 move2:11; // 2047 moves. - u16 unused_02:5; + u16 evolutionTracker2:4; + u16 unused_02:1; u16 move3:11; // 2047 moves. u16 unused_04:5; u16 move4:11; // 2047 moves. diff --git a/src/battle_script_commands.c b/src/battle_script_commands.c index 327d5be9b1..0a684aafd5 100644 --- a/src/battle_script_commands.c +++ b/src/battle_script_commands.c @@ -356,6 +356,7 @@ static void RemoveAllTerrains(void); static bool8 CanAbilityPreventStatLoss(u16 abilityDef, bool8 isIntimidate); static bool8 CanBurnHitThaw(u16 move); static u32 GetNextTarget(u32 moveTarget, bool32 excludeCurrent); +static void TryUpdateEvolutionTracker(u32 evolutionMethod, u32 upAmount); static void Cmd_attackcanceler(void); static void Cmd_accuracycheck(void); @@ -6201,6 +6202,12 @@ static void Cmd_moveend(void) gBattleStruct->sameMoveTurns[gBattlerAttacker]++; gBattleScripting.moveendState++; break; + case MOVEEND_SET_EVOLUTION_TRACKER: + // If the Pokémon needs to keep track of move usage for its evolutions, do it + if (originallyUsedMove != MOVE_NONE) + TryUpdateEvolutionTracker(EVO_LEVEL_MOVE_TWENTY_TIMES, 1); + gBattleScripting.moveendState++; + break; case MOVEEND_CLEAR_BITS: // Clear/Set bits for things like using a move for all targets and all hits. if (gSpecialStatuses[gBattlerAttacker].instructedChosenTarget) *(gBattleStruct->moveTarget + gBattlerAttacker) = gSpecialStatuses[gBattlerAttacker].instructedChosenTarget & 0x3; @@ -16588,3 +16595,45 @@ void BS_SetPhotonGeyserCategory(void) gBattleStruct->swapDamageCategory = (GetCategoryBasedOnStats(gBattlerAttacker) == DAMAGE_CATEGORY_PHYSICAL); gBattlescriptCurrInstr = cmd->nextInstr; } + +static void TryUpdateEvolutionTracker(u32 evolutionMethod, u32 upAmount) +{ + u32 i; + + if (GetBattlerSide(gBattlerAttacker) == B_SIDE_PLAYER + && !(gBattleTypeFlags & (BATTLE_TYPE_LINK + | BATTLE_TYPE_EREADER_TRAINER + | BATTLE_TYPE_RECORDED_LINK + | BATTLE_TYPE_TRAINER_HILL + | BATTLE_TYPE_FRONTIER))) + { + const struct Evolution *evolutions = GetSpeciesEvolutions(gBattleMons[gBattlerAttacker].species); + if (evolutions == NULL) + return; + + for (i = 0; evolutions[i].method != EVOLUTIONS_END; i++) + { + if (SanitizeSpeciesId(evolutions[i].targetSpecies) == SPECIES_NONE) + continue; + + if (evolutions[i].method == evolutionMethod) + { + // We only have 9 bits to use + u16 val = min(511, GetMonData(&gPlayerParty[gBattlerPartyIndexes[gBattlerAttacker]], MON_DATA_EVOLUTION_TRACKER) + upAmount); + // Reset progress if you faint for the recoil method. + if (gBattleMons[gBattlerAttacker].hp == 0 && (evolutionMethod == EVO_LEVEL_RECOIL_DAMAGE_MALE || evolutionMethod == EVO_LEVEL_RECOIL_DAMAGE_FEMALE)) + val = 0; + SetMonData(&gPlayerParty[gBattlerPartyIndexes[gBattlerAttacker]], MON_DATA_EVOLUTION_TRACKER, &val); + return; + } + } + } +} + +void BS_TryUpdateRecoilTracker(void) +{ + NATIVE_ARGS(); + TryUpdateEvolutionTracker(EVO_LEVEL_RECOIL_DAMAGE_MALE, gBattleMoveDamage); + TryUpdateEvolutionTracker(EVO_LEVEL_RECOIL_DAMAGE_FEMALE, gBattleMoveDamage); + gBattlescriptCurrInstr = cmd->nextInstr; +} diff --git a/src/data/pokemon/species_info/gen_1.h b/src/data/pokemon/species_info/gen_1.h index 8b760edb12..7ce5433295 100644 --- a/src/data/pokemon/species_info/gen_1.h +++ b/src/data/pokemon/species_info/gen_1.h @@ -4508,7 +4508,7 @@ const struct SpeciesInfo gSpeciesInfoGen1[] = ICON(Primeape, 2), FOOTPRINT(Primeape) LEARNSETS(Primeape), - .evolutions = EVOLUTION({EVO_MOVE, MOVE_RAGE_FIST, SPECIES_ANNIHILAPE}), + .evolutions = EVOLUTION({EVO_LEVEL_MOVE_TWENTY_TIMES, MOVE_RAGE_FIST, SPECIES_ANNIHILAPE}), }, #if P_GEN_9_CROSS_EVOS diff --git a/src/data/pokemon/species_info/gen_2.h b/src/data/pokemon/species_info/gen_2.h index 2cafad3a0c..334d8be7a5 100644 --- a/src/data/pokemon/species_info/gen_2.h +++ b/src/data/pokemon/species_info/gen_2.h @@ -5170,7 +5170,7 @@ const struct SpeciesInfo gSpeciesInfoGen2[] = ICON(Stantler, 2), FOOTPRINT(Stantler) LEARNSETS(Stantler), - .evolutions = EVOLUTION({EVO_MOVE, MOVE_PSYSHIELD_BASH, SPECIES_WYRDEER}), + .evolutions = EVOLUTION({EVO_LEVEL_MOVE_TWENTY_TIMES, MOVE_PSYSHIELD_BASH, SPECIES_WYRDEER}), }, #if P_GEN_8_CROSS_EVOS diff --git a/src/data/pokemon/species_info/gen_5.h b/src/data/pokemon/species_info/gen_5.h index f06925ebcb..d3eea269c9 100644 --- a/src/data/pokemon/species_info/gen_5.h +++ b/src/data/pokemon/species_info/gen_5.h @@ -3036,8 +3036,8 @@ const struct SpeciesInfo gSpeciesInfoGen5[] = PALETTES(BasculinWhiteStriped), ICON(BasculinWhiteStriped, 0), LEARNSETS(BasculinWhiteStriped), - .evolutions = EVOLUTION({EVO_NONE, 0, SPECIES_BASCULEGION_MALE}, - {EVO_NONE, 0, SPECIES_BASCULEGION_FEMALE}), + .evolutions = EVOLUTION({EVO_LEVEL_RECOIL_DAMAGE_MALE, 294, SPECIES_BASCULEGION_MALE}, + {EVO_LEVEL_RECOIL_DAMAGE_FEMALE, 294, SPECIES_BASCULEGION_FEMALE}), }, #define BASCULEGION_MISC_INFO \ diff --git a/src/pokedex_plus_hgss.c b/src/pokedex_plus_hgss.c index 2002060653..9f02c46cf9 100644 --- a/src/pokedex_plus_hgss.c +++ b/src/pokedex_plus_hgss.c @@ -244,6 +244,9 @@ static const u8 sText_EVO_WATER_SCROLL[] = _("ScrollOfWatrs is used"); static const u8 sText_EVO_ITEM_NIGHT[] = _("{STR_VAR_2} is used, night"); static const u8 sText_EVO_ITEM_DAY[] = _("{STR_VAR_2} is used, day"); static const u8 sText_EVO_ITEM_HOLD[] = _("{LV}{UP_ARROW}, holds {STR_VAR_2}"); +static const u8 sText_EVO_LEVEL_MOVE_TWENTY_TIMES[] = _("{LV}{UP_ARROW} after 20x {STR_VAR_2}"); +static const u8 sText_EVO_LEVEL_RECOIL_DAMAGE_MALE[] = _("{LV}{UP_ARROW} with {STR_VAR_2} recoil, male"); +static const u8 sText_EVO_LEVEL_RECOIL_DAMAGE_FEMALE[] = _("{LV}{UP_ARROW} with {STR_VAR_2} recoil, female"); static const u8 sText_EVO_UNKNOWN[] = _("Method unknown"); static const u8 sText_EVO_NONE[] = _("{STR_VAR_1} has no evolution."); @@ -6770,6 +6773,18 @@ static u8 PrintEvolutionTargetSpeciesAndMethod(u8 taskId, u16 species, u8 depth, CopyItemName(item, gStringVar2); StringExpandPlaceholders(gStringVar4, sText_EVO_ITEM_HOLD ); break; + case EVO_LEVEL_MOVE_TWENTY_TIMES: + StringCopy(gStringVar2, GetMoveName(evolutions[i].param)); + StringExpandPlaceholders(gStringVar4, sText_EVO_LEVEL_MOVE_TWENTY_TIMES ); + break; + case EVO_LEVEL_RECOIL_DAMAGE_MALE: + ConvertIntToDecimalStringN(gStringVar2, evolutions[i].param, STR_CONV_MODE_LEADING_ZEROS, 3); + StringExpandPlaceholders(gStringVar4, sText_EVO_LEVEL_RECOIL_DAMAGE_MALE); + break; + case EVO_LEVEL_RECOIL_DAMAGE_FEMALE: + ConvertIntToDecimalStringN(gStringVar2, evolutions[i].param, STR_CONV_MODE_LEADING_ZEROS, 3); + StringExpandPlaceholders(gStringVar4, sText_EVO_LEVEL_RECOIL_DAMAGE_FEMALE); + break; default: StringExpandPlaceholders(gStringVar4, sText_EVO_UNKNOWN ); break; diff --git a/src/pokemon.c b/src/pokemon.c index 46e696a4be..0538d00b49 100644 --- a/src/pokemon.c +++ b/src/pokemon.c @@ -2087,6 +2087,19 @@ u32 GetMonData2(struct Pokemon *mon, s32 field) return GetMonData3(mon, field, NULL); } +struct EvolutionTrackerBitfield +{ + u16 a: 5; + u16 b: 4; + u16 unused: 7; +}; + +union EvolutionTracker +{ + u16 value; + struct EvolutionTrackerBitfield asField; +}; + /* GameFreak called GetBoxMonData with either 2 or 3 arguments, for type * safety we have a GetBoxMonData macro (in include/pokemon.h) which * dispatches to either GetBoxMonData2 or GetBoxMonData3 based on the @@ -2099,6 +2112,7 @@ u32 GetBoxMonData3(struct BoxPokemon *boxMon, s32 field, u8 *data) struct PokemonSubstruct1 *substruct1 = NULL; struct PokemonSubstruct2 *substruct2 = NULL; struct PokemonSubstruct3 *substruct3 = NULL; + union EvolutionTracker evoTracker; // Any field greater than MON_DATA_ENCRYPT_SEPARATOR is encrypted and must be treated as such if (field > MON_DATA_ENCRYPT_SEPARATOR) @@ -2479,6 +2493,11 @@ u32 GetBoxMonData3(struct BoxPokemon *boxMon, s32 field, u8 *data) } break; } + case MON_DATA_EVOLUTION_TRACKER: + evoTracker.asField.a = substruct1->evolutionTracker1; + evoTracker.asField.b = substruct1->evolutionTracker2; + retVal = evoTracker.value; + break; default: break; } @@ -2894,6 +2913,16 @@ void SetBoxMonData(struct BoxPokemon *boxMon, s32 field, const void *dataArg) substruct0->teraType = 1 + teraType; break; } + case MON_DATA_EVOLUTION_TRACKER: + { + union EvolutionTracker evoTracker; + u32 evoTrackerValue; + SET32(evoTrackerValue); + evoTracker.value = evoTrackerValue; + substruct1->evolutionTracker1 = evoTracker.asField.a; + substruct1->evolutionTracker2 = evoTracker.asField.b; + break; + } default: break; } @@ -4047,6 +4076,7 @@ u16 GetEvolutionTargetSpecies(struct Pokemon *mon, u8 mode, u16 evolutionItem, s u16 upperPersonality = personality >> 16; u32 holdEffect, currentMap, partnerSpecies, partnerHeldItem, partnerHoldEffect; bool32 consumeItem = FALSE; + u16 evolutionTracker = GetMonData(mon, MON_DATA_EVOLUTION_TRACKER, 0); const struct Evolution *evolutions = GetSpeciesEvolutions(species); if (evolutions == NULL) @@ -4307,6 +4337,18 @@ u16 GetEvolutionTargetSpecies(struct Pokemon *mon, u8 mode, u16 evolutionItem, s consumeItem = TRUE; } break; + case EVO_LEVEL_MOVE_TWENTY_TIMES: + if (evolutionTracker >= 20) + targetSpecies = evolutions[i].targetSpecies; + break; + case EVO_LEVEL_RECOIL_DAMAGE_MALE: + if (evolutionTracker >= evolutions[i].param && GetMonGender(mon) == MON_MALE) + targetSpecies = evolutions[i].targetSpecies; + break; + case EVO_LEVEL_RECOIL_DAMAGE_FEMALE: + if (evolutionTracker >= evolutions[i].param && GetMonGender(mon) == MON_FEMALE) + targetSpecies = evolutions[i].targetSpecies; + break; } } break;