diff --git a/include/constants/item_config.h b/include/constants/item_config.h new file mode 100644 index 000000000..4f111bc0c --- /dev/null +++ b/include/constants/item_config.h @@ -0,0 +1,33 @@ +#ifndef GUARD_CONSTANTS_ITEM_CONFIG_H +#define GUARD_CONSTANTS_ITEM_CONFIG_H + +// Used by other branches to communicate with each other. +#define ITEM_EXPANSION + +#ifndef GEN_3 +#define GEN_3 0 +#define GEN_4 1 +#define GEN_5 2 +#define GEN_6 3 +#define GEN_7 4 +#define GEN_8 5 +#endif + +// Item config +#define I_SHINY_CHARM_REROLLS 3 // Amount of re-rolls if the player has the Shiny Charm. Set to 0 to disable Shiny Charm's effects. +#define I_KEY_FOSSILS GEN_7 // In Gen4+, all Gen 3 fossils became regular items. +#define I_KEY_ESCAPE_ROPE GEN_7 // In Gen8, Escape Rope became a Key Item. Keep in mind, this will make it free to buy in marts. + +// Ball config +#define I_LURE_BALL_MODIFIER GEN_7 // In Gen7+, Lure Ball's catch multiplier is x5 instead of x3. +#define I_NET_BALL_MODIFIER GEN_7 // In Gen7+, Net Ball's catch multiplier is x5 instead of x3. +#define I_REPEAT_BALL_MODIFIER GEN_7 // In Gen7+, Repeat Ball's catch multiplier is x3.5 instead of x3. +#define I_DUSK_BALL_MODIFIER GEN_7 // In Gen7+, Dusk Ball's catch multiplier is x3 instead of x3.5. +#define I_QUICK_BALL_MODIFIER GEN_7 // In Gen5+, Quick Ball's catch multiplier is x5 instead of x4. +#define I_DREAM_BALL_MODIFIER GEN_8 // In Gen8+, Dream Ball's catch multiplier is x4 when the target is asleep. +#define I_TIMER_BALL_MODIFIER GEN_7 // In Gen5+, Timer Ball's effectiveness increases by x0.3 per turn instead of x0.1 +#define I_DIVE_BALL_MODIFIER GEN_7 // In Gen4+, Dive Ball's effectiveness increases by when Surfing or Fishing. +#define I_HEAVY_BALL_MODIFIER GEN_7 // In Gen7+, Heavy Ball's ranges change. See Cmd_handleballthrow. +#define I_NEST_BALL_MODIFIER GEN_7 // Nest Ball's formula varies depending on the Gen. See Cmd_handleballthrow. + +#endif // GUARD_CONSTANTS_ITEM_CONFIG_H diff --git a/include/constants/pokemon.h b/include/constants/pokemon.h index 47e197824..8cc78d8ce 100644 --- a/include/constants/pokemon.h +++ b/include/constants/pokemon.h @@ -91,7 +91,6 @@ // Shiny odds #define SHINY_ODDS 8 // Actual probability is SHINY_ODDS/65536 -#define SHINY_CHARM_REROLLS 3 // Amount of re-rolls if has Shiny Charm. // Flags for Get(Box)MonData / Set(Box)MonData #define MON_DATA_PERSONALITY 0 diff --git a/include/item.h b/include/item.h index 87ff57bc7..909078943 100644 --- a/include/item.h +++ b/include/item.h @@ -2,6 +2,7 @@ #define GUARD_ITEM_H #include "constants/item.h" +#include "constants/item_config.h" typedef void (*ItemUseFunc)(u8); diff --git a/include/wild_encounter.h b/include/wild_encounter.h index a9479d34d..626eee468 100644 --- a/include/wild_encounter.h +++ b/include/wild_encounter.h @@ -30,6 +30,7 @@ struct WildPokemonHeader }; extern bool8 gIsFishingEncounter; +extern bool8 gIsSurfingEncounter; extern const struct WildPokemonHeader gWildMonHeaders[]; diff --git a/src/battle_main.c b/src/battle_main.c index c05061656..ccbdafebf 100644 --- a/src/battle_main.c +++ b/src/battle_main.c @@ -5073,6 +5073,7 @@ static void FreeResetData_ReturnToOvOrDoEvolutions(void) if (!gPaletteFade.active) { gIsFishingEncounter = FALSE; + gIsSurfingEncounter = FALSE; ResetSpriteData(); if (gLeveledUpInBattle == 0 || gBattleOutcome != B_OUTCOME_WON) { diff --git a/src/battle_script_commands.c b/src/battle_script_commands.c index bc689117a..642e7b53d 100644 --- a/src/battle_script_commands.c +++ b/src/battle_script_commands.c @@ -9751,6 +9751,7 @@ static void Cmd_removelightscreenreflect(void) // brick break static void Cmd_handleballthrow(void) { u8 ballMultiplier = 10; + s8 ballAddition = 0; if (gBattleControllerExecFlags) return; @@ -9780,32 +9781,73 @@ static void Cmd_handleballthrow(void) else catchRate = gBaseStats[gBattleMons[gBattlerTarget].species].catchRate; + + #ifdef POKEMON_EXPANSION + if (IS_ULTRA_BEAST(gBattleMons[gBattlerTarget].species)) + { + if (gLastUsedItem == ITEM_BEAST_BALL) + ballMultiplier = 50; + else + ballMultiplier = 1; + } + else + { + #endif + if (gLastUsedItem > ITEM_SAFARI_BALL) { switch (gLastUsedItem) { case ITEM_NET_BALL: if (IS_BATTLER_OF_TYPE(gBattlerTarget, TYPE_WATER) || IS_BATTLER_OF_TYPE(gBattlerTarget, TYPE_BUG)) - ballMultiplier = 30; + #if I_NET_BALL_MODIFIER >= GEN_7 + ballMultiplier = 50; + #else + ballMultiplier = 30; + #endif break; case ITEM_DIVE_BALL: - if (GetCurrentMapType() == MAP_TYPE_UNDERWATER) - ballMultiplier = 35; + #if I_DIVE_BALL_MODIFIER >= GEN_4 + if (GetCurrentMapType() == MAP_TYPE_UNDERWATER || gIsFishingEncounter || gIsSurfingEncounter) + ballMultiplier = 35; + #else + if (GetCurrentMapType() == MAP_TYPE_UNDERWATER) + ballMultiplier = 35; + #endif break; case ITEM_NEST_BALL: - if (gBattleMons[gBattlerTarget].level < 40) - { - ballMultiplier = 40 - gBattleMons[gBattlerTarget].level; - if (ballMultiplier <= 9) - ballMultiplier = 10; - } + #if I_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 I_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; + } + #endif break; case ITEM_REPEAT_BALL: if (GetSetPokedexFlag(SpeciesToNationalPokedexNum(gBattleMons[gBattlerTarget].species), FLAG_GET_CAUGHT)) - ballMultiplier = 30; + #if I_REPEAT_BALL_MODIFIER >= GEN_7 + ballMultiplier = 35; + #else + ballMultiplier = 30; + #endif break; case ITEM_TIMER_BALL: - ballMultiplier = gBattleResults.battleTurnCounter + 10; + #if I_TIMER_BALL_MODIFIER >= GEN_5 + ballMultiplier = (gBattleResults.battleTurnCounter * 3) + 10; + #else + ballMultiplier = gBattleResults.battleTurnCounter + 10; + #endif if (ballMultiplier > 40) ballMultiplier = 40; break; @@ -9829,7 +9871,11 @@ static void Cmd_handleballthrow(void) break; case ITEM_LURE_BALL: if (gIsFishingEncounter) - ballMultiplier = 30; + #if I_LURE_BALL_MODIFIER >= GEN_7 + ballMultiplier = 50; + #else + ballMultiplier = 30; + #endif break; case ITEM_MOON_BALL: for (i = 0; i < EVOS_PER_MON; i++) @@ -9851,16 +9897,36 @@ static void Cmd_handleballthrow(void) break; case ITEM_HEAVY_BALL: i = GetPokedexHeightWeight(SpeciesToNationalPokedexNum(gBattleMons[gBattlerTarget].species), 1); - if (i < 1024) - ballMultiplier = 5; - else if (i < 2048) - ballMultiplier = 10; - else if (i < 3072) - ballMultiplier = 20; - else if (i < 4096) - ballMultiplier = 30; - else - ballMultiplier = 40; + #if I_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 I_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_FAST_BALL: if (gBaseStats[gBattleMons[gBattlerTarget].species].baseSpeed >= 100) @@ -9868,19 +9934,48 @@ static void Cmd_handleballthrow(void) break; case ITEM_QUICK_BALL: if (gBattleResults.battleTurnCounter == 0) - ballMultiplier = 40; + #if I_QUICK_BALL_MODIFIER >= GEN_5 + ballMultiplier = 50; + #else + ballMultiplier = 40; + #endif break; case ITEM_DUSK_BALL: RtcCalcLocalTime(); if ((gLocalTime.hours >= 20 && gLocalTime.hours <= 3) || gMapHeader.cave || gMapHeader.mapType == MAP_TYPE_UNDERGROUND) - ballMultiplier = 30; + #if I_DUSK_BALL_MODIFIER >= GEN_7 + ballMultiplier = 30; + #else + ballMultiplier = 35; + #endif + break; + case ITEM_DREAM_BALL: + #if I_DREAM_BALL_MODIFIER >= GEN_8 + if (gBattleMons[gBattlerTarget].status1 & STATUS1_SLEEP) + ballMultiplier = 40; + #else + ballMultiplier = 10; + #endif + break; + case ITEM_BEAST_BALL: + ballMultiplier = 1; break; } } else ballMultiplier = sBallCatchBonuses[gLastUsedItem - ITEM_ULTRA_BALL]; - odds = (catchRate * ballMultiplier / 10) + #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 + catchRate = catchRate + ballAddition; + + odds = ((catchRate) * ballMultiplier / 10) * (gBattleMons[gBattlerTarget].maxHP * 3 - gBattleMons[gBattlerTarget].hp * 2) / (3 * gBattleMons[gBattlerTarget].maxHP); diff --git a/src/data/items.h b/src/data/items.h index f2a5767d4..f82c4f9f9 100644 --- a/src/data/items.h +++ b/src/data/items.h @@ -1281,9 +1281,16 @@ const struct Item gItems[] = { .name = _("Escape Rope"), .itemId = ITEM_ESCAPE_ROPE, - .price = 550, .description = sEscapeRopeDesc, + #if I_KEY_ESCAPE_ROPE >= GEN_8 + .price = 0, + .importance = 1, + .pocket = POCKET_KEY_ITEMS, + #else + .price = 550, + .importance = 0, .pocket = POCKET_ITEMS, + #endif .type = 2, .fieldUseFunc = ItemUseOutOfBattle_EscapeRope, .secondaryId = 0, @@ -4883,8 +4890,13 @@ const struct Item gItems[] = .itemId = ITEM_OLD_AMBER, .price = 0, .description = sOldAmberDesc, + #if I_KEY_FOSSILS >= GEN_4 .importance = 1, .pocket = POCKET_KEY_ITEMS, + #else + .importance = 0, + .pocket = POCKET_ITEMS, + #endif .type = 4, .fieldUseFunc = ItemUseOutOfBattle_CannotUse, .secondaryId = 0, @@ -4948,8 +4960,13 @@ const struct Item gItems[] = .itemId = ITEM_HELIX_FOSSIL, .price = 0, .description = sHelixFossilDesc, + #if I_KEY_FOSSILS >= GEN_4 + .importance = 1, + .pocket = POCKET_KEY_ITEMS, + #else .importance = 0, .pocket = POCKET_ITEMS, + #endif .type = 4, .fieldUseFunc = ItemUseOutOfBattle_CannotUse, .secondaryId = 0, @@ -4961,8 +4978,13 @@ const struct Item gItems[] = .itemId = ITEM_DOME_FOSSIL, .price = 0, .description = sDomeFossilDesc, + #if I_KEY_FOSSILS >= GEN_4 + .importance = 1, + .pocket = POCKET_KEY_ITEMS, + #else .importance = 0, .pocket = POCKET_ITEMS, + #endif .type = 4, .fieldUseFunc = ItemUseOutOfBattle_CannotUse, .secondaryId = 0, @@ -4974,8 +4996,13 @@ const struct Item gItems[] = .itemId = ITEM_ROOT_FOSSIL, .price = 0, .description = sRootFossilDesc, + #if I_KEY_FOSSILS >= GEN_4 + .importance = 1, + .pocket = POCKET_KEY_ITEMS, + #else .importance = 0, .pocket = POCKET_ITEMS, + #endif .type = 4, .fieldUseFunc = ItemUseOutOfBattle_CannotUse, .secondaryId = 0, @@ -4987,8 +5014,13 @@ const struct Item gItems[] = .itemId = ITEM_CLAW_FOSSIL, .price = 0, .description = sClawFossilDesc, + #if I_KEY_FOSSILS >= GEN_4 + .importance = 1, + .pocket = POCKET_KEY_ITEMS, + #else .importance = 0, .pocket = POCKET_ITEMS, + #endif .type = 4, .fieldUseFunc = ItemUseOutOfBattle_CannotUse, .secondaryId = 0, diff --git a/src/item_use.c b/src/item_use.c index 1f223c627..462fab48a 100755 --- a/src/item_use.c +++ b/src/item_use.c @@ -910,7 +910,9 @@ void Task_UseDigEscapeRopeOnField(u8 taskId) static void ItemUseOnFieldCB_EscapeRope(u8 taskId) { Overworld_ResetStateAfterDigEscRope(); - RemoveUsedItem(); + #if I_KEY_ESCAPE_ROPE < GEN_8 + RemoveUsedItem(); + #endif gTasks[taskId].data[0] = 0; DisplayItemMessageOnField(taskId, gStringVar4, Task_UseDigEscapeRopeOnField); } diff --git a/src/pokemon.c b/src/pokemon.c index 4fbdcffd0..5547baeb3 100644 --- a/src/pokemon.c +++ b/src/pokemon.c @@ -2233,7 +2233,7 @@ void CreateBoxMon(struct BoxPokemon *boxMon, u16 species, u8 level, u8 fixedIV, personality = Random32(); shinyValue = HIHALF(value) ^ LOHALF(value) ^ HIHALF(personality) ^ LOHALF(personality); rolls++; - } while (shinyValue >= SHINY_ODDS && rolls < SHINY_CHARM_REROLLS); + } while (shinyValue >= SHINY_ODDS && rolls < I_SHINY_CHARM_REROLLS); } } diff --git a/src/wild_encounter.c b/src/wild_encounter.c index 6ef4987f2..7d8edca4c 100644 --- a/src/wild_encounter.c +++ b/src/wild_encounter.c @@ -39,6 +39,7 @@ static bool8 IsAbilityAllowingEncounter(u8 level); // EWRAM vars EWRAM_DATA static u8 sWildEncountersDisabled = 0; EWRAM_DATA bool8 gIsFishingEncounter = 0; +EWRAM_DATA bool8 gIsSurfingEncounter = 0; EWRAM_DATA static u32 sFeebasRngValue = 0; #include "data/wild_encounters.h" @@ -615,6 +616,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; BattleSetup_StartWildBattle(); return TRUE; }