diff --git a/include/constants/battle_config.h b/include/constants/battle_config.h index 93202bed7..946080137 100644 --- a/include/constants/battle_config.h +++ b/include/constants/battle_config.h @@ -140,6 +140,16 @@ #define B_BERRIES_INSTANT GEN_7 // In Gen4+, most berries activate on battle start/switch-in if applicable. In Gen3, they only activate either at the move end or turn end. #define B_X_ITEMS_BUFF GEN_7 // In Gen7+, the X Items raise a stat by 2 stages instead of 1. #define B_MENTAL_HERB GEN_5 // In Gen5+, the Mental Herb cures Infatuation, Taunt, Encore, Torment, Heal Block, and Disable +#define B_NET_BALL_MODIFIER GEN_7 // In Gen7+, Net Ball's catch multiplier is x5 instead of x3. +#define B_DIVE_BALL_MODIFIER GEN_7 // In Gen4+, Dive Ball's effectiveness increases by when Surfing or Fishing. +#define B_NEST_BALL_MODIFIER GEN_7 // Nest Ball's formula varies depending on the Gen. See Cmd_handleballthrow. +#define B_REPEAT_BALL_MODIFIER GEN_7 // In Gen7+, Repeat Ball's catch multiplier is x3.5 instead of x3. +#define B_TIMER_BALL_MODIFIER GEN_7 // In Gen5+, Timer Ball's effectiveness increases by x0.3 per turn instead of x0.1 +#define B_DUSK_BALL_MODIFIER GEN_7 // In Gen7+, Dusk Ball's catch multiplier is x3 instead of x3.5. +#define B_QUICK_BALL_MODIFIER GEN_7 // In Gen5+, Quick Ball's catch multiplier is x5 instead of x4. +#define B_LURE_BALL_MODIFIER GEN_7 // In Gen7+, Lure Ball's catch multiplier is x5 instead of x3. +#define B_HEAVY_BALL_MODIFIER GEN_7 // In Gen7+, Heavy Ball's ranges change. See Cmd_handleballthrow. +#define B_DREAM_BALL_MODIFIER GEN_8 // In Gen8, Dream Ball's catch multiplier is x4 when the target is asleep or has the ability Comatose. // 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/wild_encounter.h b/include/wild_encounter.h index 200243170..5f4fcf536 100644 --- a/include/wild_encounter.h +++ b/include/wild_encounter.h @@ -30,6 +30,8 @@ struct WildPokemonHeader }; extern const struct WildPokemonHeader gWildMonHeaders[]; +extern bool8 gIsFishingEncounter; +extern bool8 gIsSurfingEncounter; void DisableWildEncounters(bool8 disabled); bool8 StandardWildEncounter(u16 currMetaTileBehavior, u16 previousMetaTileBehavior); diff --git a/src/battle_main.c b/src/battle_main.c index 25db65f78..62e31b8e1 100644 --- a/src/battle_main.c +++ b/src/battle_main.c @@ -49,6 +49,7 @@ #include "trig.h" #include "tv.h" #include "util.h" +#include "wild_encounter.h" #include "window.h" #include "constants/abilities.h" #include "constants/battle_config.h" @@ -4925,6 +4926,8 @@ static void FreeResetData_ReturnToOvOrDoEvolutions(void) { if (!gPaletteFade.active) { + gIsFishingEncounter = FALSE; + gIsSurfingEncounter = FALSE; ResetSpriteData(); if (gLeveledUpInBattle && (gBattleOutcome == B_OUTCOME_WON || gBattleOutcome == B_OUTCOME_CAUGHT)) { diff --git a/src/battle_script_commands.c b/src/battle_script_commands.c index 8d6dd5bdb..6def00fed 100644 --- a/src/battle_script_commands.c +++ b/src/battle_script_commands.c @@ -42,6 +42,8 @@ #include "constants/battle_string_ids.h" #include "battle_setup.h" #include "overworld.h" +#include "wild_encounter.h" +#include "rtc.h" #include "party_menu.h" #include "constants/battle_config.h" #include "battle_arena.h" @@ -56,6 +58,7 @@ #include "constants/party_menu.h" extern struct MusicPlayerInfo gMPlayInfo_BGM; +extern struct Evolution gEvolutionTable[][EVOS_PER_MON]; extern const u8* const gBattleScriptsForMoveEffects[]; @@ -1160,15 +1163,6 @@ static const u8 sTerrainToType[] = [BATTLE_TERRAIN_PLAIN] = TYPE_NORMAL, }; -// - ITEM_ULTRA_BALL skips Master Ball and ITEM_NONE -static const u8 sBallCatchBonuses[] = -{ - [ITEM_ULTRA_BALL - ITEM_ULTRA_BALL] = 20, - [ITEM_GREAT_BALL - ITEM_ULTRA_BALL] = 15, - [ITEM_POKE_BALL - ITEM_ULTRA_BALL] = 10, - [ITEM_SAFARI_BALL - ITEM_ULTRA_BALL] = 15 -}; - // In Battle Palace, moves are chosen based on the pokemons nature rather than by the player // Moves are grouped into "Attack", "Defense", or "Support" (see PALACE_MOVE_GROUP_*) // Each nature has a certain percent chance of selecting a move from a particular group @@ -12406,7 +12400,8 @@ static u8 GetCatchingBattler(void) static void Cmd_handleballthrow(void) { - u8 ballMultiplier = 0; + u8 ballMultiplier = 10; + s8 ballAddition = 0; if (gBattleControllerExecFlags) return; @@ -12428,7 +12423,7 @@ static void Cmd_handleballthrow(void) } else { - u32 odds; + u32 odds, i; u8 catchRate; if (gLastUsedItem == ITEM_SAFARI_BALL) @@ -12436,53 +12431,193 @@ static void Cmd_handleballthrow(void) else catchRate = gBaseStats[gBattleMons[gBattlerTarget].species].catchRate; - if (gLastUsedItem > ITEM_SAFARI_BALL) + #ifdef POKEMON_EXPANSION + if (gBaseStats[gBattleMons[gBattlerTarget].species].flags & F_ULTRA_BEAST) { - switch (gLastUsedItem) - { - case ITEM_NET_BALL: - if (IS_BATTLER_OF_TYPE(gBattlerTarget, TYPE_WATER) || IS_BATTLER_OF_TYPE(gBattlerTarget, TYPE_BUG)) + if (gLastUsedItem == ITEM_BEAST_BALL) + ballMultiplier = 50; + else + ballMultiplier = 1; + } + else + { + #endif + + switch (gLastUsedItem) + { + case ITEM_ULTRA_BALL: + ballMultiplier = 20; + case ITEM_GREAT_BALL: + case ITEM_SAFARI_BALL: + #ifdef ITEM_EXPANSION + case ITEM_SPORT_BALL: + #endif + ballMultiplier = 15; + case ITEM_NET_BALL: + if (IS_BATTLER_OF_TYPE(gBattlerTarget, TYPE_WATER) || IS_BATTLER_OF_TYPE(gBattlerTarget, TYPE_BUG)) + #if B_NET_BALL_MODIFIER >= GEN_7 + ballMultiplier = 50; + #else ballMultiplier = 30; - else - ballMultiplier = 10; - break; - case ITEM_DIVE_BALL: + #endif + break; + case ITEM_DIVE_BALL: + #if B_DIVE_BALL_MODIFIER >= GEN_4 + if (GetCurrentMapType() == MAP_TYPE_UNDERWATER || gIsFishingEncounter || gIsSurfingEncounter) + ballMultiplier = 35; + #else if (GetCurrentMapType() == MAP_TYPE_UNDERWATER) ballMultiplier = 35; - else - ballMultiplier = 10; - break; - case ITEM_NEST_BALL: + #endif + break; + case ITEM_NEST_BALL: + #if B_NEST_BALL_MODIFIER >= GEN_6 + //((41 - Pokémon's level) ÷ 10)× if Pokémon's level is between 1 and 29, 1× otherwise. + if (gBattleMons[gBattlerTarget].level < 30) + ballMultiplier = 41 - gBattleMons[gBattlerTarget].level; + #elif B_NEST_BALL_MODIFIER == GEN_5 + //((41 - Pokémon's level) ÷ 10)×, minimum 1× + if (gBattleMons[gBattlerTarget].level < 31) + ballMultiplier = 41 - gBattleMons[gBattlerTarget].level; + #else + //((40 - Pokémon's level) ÷ 10)×, minimum 1× if (gBattleMons[gBattlerTarget].level < 40) { ballMultiplier = 40 - gBattleMons[gBattlerTarget].level; if (ballMultiplier <= 9) ballMultiplier = 10; } - else - { - ballMultiplier = 10; - } - break; - case ITEM_REPEAT_BALL: - if (GetSetPokedexFlag(SpeciesToNationalPokedexNum(gBattleMons[gBattlerTarget].species), FLAG_GET_CAUGHT)) + #endif + break; + case ITEM_REPEAT_BALL: + if (GetSetPokedexFlag(SpeciesToNationalPokedexNum(gBattleMons[gBattlerTarget].species), FLAG_GET_CAUGHT)) + #if B_REPEAT_BALL_MODIFIER >= GEN_7 + ballMultiplier = 35; + #else ballMultiplier = 30; - else - ballMultiplier = 10; - break; - case ITEM_TIMER_BALL: + #endif + break; + case ITEM_TIMER_BALL: + #if B_TIMER_BALL_MODIFIER >= GEN_5 + ballMultiplier = (gBattleResults.battleTurnCounter * 3) + 10; + #else ballMultiplier = gBattleResults.battleTurnCounter + 10; - if (ballMultiplier > 40) + #endif + if (ballMultiplier > 40) + ballMultiplier = 40; + break; + #ifdef ITEM_EXPANSION + case ITEM_DUSK_BALL: + RtcCalcLocalTime(); + if ((gLocalTime.hours >= 20 && gLocalTime.hours <= 3) || gMapHeader.cave || gMapHeader.mapType == MAP_TYPE_UNDERGROUND) + #if B_DUSK_BALL_MODIFIER >= GEN_7 + ballMultiplier = 30; + #else + ballMultiplier = 35; + #endif + break; + case ITEM_QUICK_BALL: + if (gBattleResults.battleTurnCounter == 0) + #if B_QUICK_BALL_MODIFIER >= GEN_5 + ballMultiplier = 50; + #else + ballMultiplier = 40; + #endif + break; + case ITEM_LEVEL_BALL: + if (gBattleMons[gBattlerAttacker].level >= 4 * gBattleMons[gBattlerTarget].level) + ballMultiplier = 80; + else if (gBattleMons[gBattlerAttacker].level > 2 * gBattleMons[gBattlerTarget].level) + ballMultiplier = 40; + else if (gBattleMons[gBattlerAttacker].level > gBattleMons[gBattlerTarget].level) + ballMultiplier = 20; + break; + case ITEM_LURE_BALL: + if (gIsFishingEncounter) + #if B_LURE_BALL_MODIFIER >= GEN_7 + ballMultiplier = 50; + #else + ballMultiplier = 30; + #endif + break; + case ITEM_MOON_BALL: + for (i = 0; i < EVOS_PER_MON; i++) + { + if (gEvolutionTable[gBattleMons[gBattlerTarget].species][i].method == EVO_ITEM + && gEvolutionTable[gBattleMons[gBattlerTarget].species][i].param == ITEM_MOON_STONE) ballMultiplier = 40; - break; - case ITEM_LUXURY_BALL: - case ITEM_PREMIER_BALL: - ballMultiplier = 10; - break; } + break; + case ITEM_LOVE_BALL: + if (gBattleMons[gBattlerTarget].species == gBattleMons[gBattlerAttacker].species) + { + u8 gender1 = GetMonGender(&gEnemyParty[gBattlerPartyIndexes[gBattlerTarget]]); + u8 gender2 = GetMonGender(&gPlayerParty[gBattlerPartyIndexes[gBattlerAttacker]]); + + if (gender1 != gender2 && gender1 != MON_GENDERLESS && gender2 != MON_GENDERLESS) + ballMultiplier = 80; + } + break; + case ITEM_FAST_BALL: + if (gBaseStats[gBattleMons[gBattlerTarget].species].baseSpeed >= 100) + ballMultiplier = 40; + break; + case ITEM_HEAVY_BALL: + i = GetPokedexHeightWeight(SpeciesToNationalPokedexNum(gBattleMons[gBattlerTarget].species), 1); + #if B_HEAVY_BALL_MODIFIER >= GEN_7 + if (i < 1000) + ballAddition = -20; + else if (i < 2000) + ballAddition = 0; + else if (i < 3000) + ballAddition = 20; + else + ballAddition = 30; + #elif B_HEAVY_BALL_MODIFIER >= GEN_4 + if (i < 2048) + ballAddition = -20; + else if (i < 3072) + ballAddition = 20; + else if (i < 4096) + ballAddition = 30; + else + ballAddition = 40; + #else + if (i < 1024) + ballAddition = -20; + else if (i < 2048) + ballAddition = 0; + else if (i < 3072) + ballAddition = 20; + else if (i < 4096) + ballAddition = 30; + else + ballAddition = 40; + #endif + break; + case ITEM_DREAM_BALL: + #if B_DREAM_BALL_MODIFIER >= GEN_8 + if (gBattleMons[gBattlerTarget].status1 & STATUS1_SLEEP || GetBattlerAbility(gBattlerTarget) == ABILITY_COMATOSE) + ballMultiplier = 40; + #else + ballMultiplier = 10; + #endif + break; + case ITEM_BEAST_BALL: + ballMultiplier = 1; + break; + #endif } + + #ifdef POKEMON_EXPANSION + } + #endif + + // catchRate is unsigned, which means that it may potentially overflow if sum is applied directly. + if (catchRate < 21 && ballAddition == -20) + catchRate = 1; else - ballMultiplier = sBallCatchBonuses[gLastUsedItem - ITEM_ULTRA_BALL]; + catchRate = catchRate + ballAddition; odds = (catchRate * ballMultiplier / 10) * (gBattleMons[gBattlerTarget].maxHP * 3 - gBattleMons[gBattlerTarget].hp * 2) @@ -12524,16 +12659,17 @@ static void Cmd_handleballthrow(void) u8 shakes; u8 maxShakes; - gBattleSpritesDataPtr->animationData->isCriticalCapture = 0; //initialize + gBattleSpritesDataPtr->animationData->isCriticalCapture = 0; gBattleSpritesDataPtr->animationData->criticalCaptureSuccess = 0; + if (CriticalCapture(odds)) { - maxShakes = 1; //critical capture doesn't gauarantee capture + maxShakes = BALL_1_SHAKE; // critical capture doesn't guarantee capture gBattleSpritesDataPtr->animationData->isCriticalCapture = 1; } else { - maxShakes = 4; + maxShakes = BALL_3_SHAKES_SUCCESS; } if (gLastUsedItem == ITEM_MASTER_BALL) @@ -12558,10 +12694,21 @@ static void Cmd_handleballthrow(void) UndoFormChange(gBattlerPartyIndexes[gBattlerTarget], GET_BATTLER_SIDE(gBattlerTarget), FALSE); gBattlescriptCurrInstr = BattleScript_SuccessBallThrow; SetMonData(&gEnemyParty[gBattlerPartyIndexes[gBattlerTarget]], MON_DATA_POKEBALL, &gLastUsedItem); + if (CalculatePlayerPartyCount() == PARTY_SIZE) gBattleCommunication[MULTISTRING_CHOOSER] = 0; else gBattleCommunication[MULTISTRING_CHOOSER] = 1; + + #ifdef ITEM_EXPANSION + if (gLastUsedItem == ITEM_HEAL_BALL) + { + MonRestorePP(&gEnemyParty[gBattlerPartyIndexes[gBattlerTarget]]); + HealStatusConditions(&gEnemyParty[gBattlerPartyIndexes[gBattlerTarget]], gBattlerPartyIndexes[gBattlerTarget], STATUS1_ANY, gBattlerTarget); + gBattleMons[gBattlerTarget].hp = gBattleMons[gBattlerTarget].maxHP; + SetMonData(&gEnemyParty[gBattlerPartyIndexes[gBattlerTarget]], MON_DATA_HP, &gBattleMons[gBattlerTarget].hp); + } + #endif } else // not caught { @@ -12569,7 +12716,7 @@ static void Cmd_handleballthrow(void) gLastUsedBall = gLastUsedItem; if (IsCriticalCapture()) - gBattleCommunication[MULTISTRING_CHOOSER] = shakes + 3; + gBattleCommunication[MULTISTRING_CHOOSER] = BALL_3_SHAKES_FAIL; else gBattleCommunication[MULTISTRING_CHOOSER] = shakes; diff --git a/src/wild_encounter.c b/src/wild_encounter.c index 330a0014b..ef0d57247 100644 --- a/src/wild_encounter.c +++ b/src/wild_encounter.c @@ -41,6 +41,8 @@ static bool8 IsAbilityAllowingEncounter(u8 level); // EWRAM vars EWRAM_DATA static u8 sWildEncountersDisabled = 0; EWRAM_DATA static u32 sFeebasRngValue = 0; +EWRAM_DATA bool8 gIsFishingEncounter = 0; +EWRAM_DATA bool8 gIsSurfingEncounter = 0; #include "data/wild_encounters.h" @@ -652,6 +654,7 @@ bool8 StandardWildEncounter(u16 currMetaTileBehavior, u16 previousMetaTileBehavi { if (TryGenerateWildMon(gWildMonHeaders[headerId].waterMonsInfo, WILD_AREA_WATER, WILD_CHECK_REPEL | WILD_CHECK_KEEN_EYE) == TRUE) { + gIsSurfingEncounter = TRUE; if (TryDoDoubleWildBattle()) { struct Pokemon mon1 = gEnemyParty[0]; @@ -803,6 +806,7 @@ void FishingWildEncounter(u8 rod) } IncrementGameStat(GAME_STAT_FISHING_CAPTURES); SetPokemonAnglerSpecies(species); + gIsFishingEncounter = TRUE; BattleSetup_StartWildBattle(); }