diff --git a/data/event_scripts.s b/data/event_scripts.s index 848a831feaca..d77b9b9de504 100644 --- a/data/event_scripts.s +++ b/data/event_scripts.s @@ -30,6 +30,7 @@ #include "constants/frontier_util.h" #include "constants/game_stat.h" #include "constants/item.h" +#include "constants/item_config.h" #include "constants/items.h" #include "constants/heal_locations.h" #include "constants/layouts.h" diff --git a/data/scripts/repel.inc b/data/scripts/repel.inc index fc178603c0e8..fc32df1d9c1f 100644 --- a/data/scripts/repel.inc +++ b/data/scripts/repel.inc @@ -1,6 +1,115 @@ EventScript_RepelWoreOff:: +.if I_REPEL_LURE_MENU == TRUE + checkitem ITEM_REPEL, 1 + goto_if_eq VAR_RESULT, TRUE, EventScript_RepelUseAnother + checkitem ITEM_SUPER_REPEL, 1 + goto_if_eq VAR_RESULT, TRUE, EventScript_RepelUseAnother + checkitem ITEM_MAX_REPEL, 1 + goto_if_eq VAR_RESULT, TRUE, EventScript_RepelUseAnother +.else + checkitem VAR_LAST_REPEL_LURE_USED, 1 + goto_if_eq VAR_RESULT, TRUE, EventScript_RepelUseAnother +.endif + lock msgbox Text_RepelWoreOff, MSGBOX_SIGN + release + end + +EventScript_RepelUseAnother: + lock + msgbox Text_UseAnotherRepel, MSGBOX_YESNO +.if I_REPEL_LURE_MENU == TRUE + callnative TryDrawRepelMenu + goto_if_eq VAR_RESULT, FALSE, EventScript_RepelWoreOff_Chose + waitstate + goto_if_eq VAR_RESULT, 127, EventScript_RepelWoreOff_End +EventScript_RepelWoreOff_Chose: + callnative HandleRepelMenuChoice + bufferitemname 1, VAR_0x8004 + removeitem VAR_0x8004, 1 + playse SE_REPEL + msgbox Text_UsedNewRepelLure, MSGBOX_SIGN +.else + goto_if_eq VAR_RESULT, YES, EventScript_UsedRepel +.endif +EventScript_RepelWoreOff_End: + release + end + +EventScript_UsedRepel: + bufferitemname 1, VAR_LAST_REPEL_LURE_USED + playse SE_REPEL + lock + msgbox Text_UsedNewRepelLure, MSGBOX_SIGN + removeitem VAR_LAST_REPEL_LURE_USED, 1 + waitse + callnative HandleUseExpiredRepel + release + end + +EventScript_LureWoreOff:: +.if I_REPEL_LURE_MENU == TRUE + checkitem ITEM_LURE, 1 + goto_if_eq VAR_RESULT, TRUE, EventScript_LureUseAnother + checkitem ITEM_SUPER_LURE, 1 + goto_if_eq VAR_RESULT, TRUE, EventScript_LureUseAnother + checkitem ITEM_MAX_LURE, 1 + goto_if_eq VAR_RESULT, TRUE, EventScript_LureUseAnother +.else + checkitem VAR_LAST_REPEL_LURE_USED, 1 + goto_if_eq VAR_RESULT, TRUE, EventScript_LureUseAnother +.endif + lock + msgbox Text_LureWoreOff, MSGBOX_SIGN + release + end + +EventScript_LureUseAnother: + lock + msgbox Text_UseAnotherLure, MSGBOX_YESNO +.if I_REPEL_LURE_MENU == TRUE + callnative TryDrawLureMenu + goto_if_eq VAR_RESULT, FALSE, EventScript_LureWoreOff_Chose + waitstate + goto_if_eq VAR_RESULT, 127, EventScript_LureWoreOff_End +EventScript_LureWoreOff_Chose: + callnative HandleLureMenuChoice + bufferitemname 1, VAR_0x8004 + removeitem VAR_0x8004, 1 + playse SE_REPEL + msgbox Text_UsedNewRepelLure, MSGBOX_SIGN +.else + goto_if_eq VAR_RESULT, YES, EventScript_UsedLure +.endif +EventScript_LureWoreOff_End: + release + end + +EventScript_UsedLure: + bufferitemname 1, VAR_LAST_REPEL_LURE_USED + playse SE_REPEL + lock + msgbox Text_UsedNewRepelLure, MSGBOX_SIGN + removeitem VAR_LAST_REPEL_LURE_USED, 1 + waitse + callnative HandleUseExpiredLure + release end Text_RepelWoreOff: .string "REPEL's effect wore off…$" + +Text_UseAnotherRepel:: + .string "REPEL's effect wore off!\n" + .string "Use another?$" + +Text_LureWoreOff: + .string "Lure's effect wore off…$" + +Text_UseAnotherLure:: + .string "Lure's effect wore off!\n" + .string "Use another?$" + +Text_UsedNewRepelLure:: + .string "{PLAYER} used the\n" + .string "{STR_VAR_2}.$" diff --git a/include/constants/item.h b/include/constants/item.h index a224291faf86..a32f4545afbf 100644 --- a/include/constants/item.h +++ b/include/constants/item.h @@ -16,4 +16,10 @@ #define KEYITEMS_POCKET 4 #define POCKETS_COUNT 5 +#define REPEL_LURE_MASK (1 << 15) +#define IS_LAST_USED_LURE(var) (var & REPEL_LURE_MASK) +#define REPEL_LURE_STEPS(var) (var & (REPEL_LURE_MASK - 1)) +#define LURE_STEP_COUNT (IS_LAST_USED_LURE(VarGet(VAR_REPEL_STEP_COUNT)) ? REPEL_LURE_STEPS(VarGet(VAR_REPEL_STEP_COUNT)) : 0) +#define REPEL_STEP_COUNT (!IS_LAST_USED_LURE(VarGet(VAR_REPEL_STEP_COUNT)) ? REPEL_LURE_STEPS(VarGet(VAR_REPEL_STEP_COUNT)) : 0) + #endif // GUARD_ITEM_CONSTANTS_H diff --git a/include/constants/item_config.h b/include/constants/item_config.h index 9261241a5fc1..e645b301daba 100644 --- a/include/constants/item_config.h +++ b/include/constants/item_config.h @@ -9,4 +9,9 @@ #define I_SITRUS_BERRY_HEAL GEN_LATEST // In Gen4+, Sitrus Berry was changed from healing 30 HP to healing 25% of Max HP. #define I_VITAMIN_EV_CAP GEN_LATEST // In Gen8, the Vitamins no longer have a cap of 100 EV per stat. +// Repel/Lure config +// These two settings are both independent and complementary. +#define VAR_LAST_REPEL_LURE_USED 0 // If this var has been assigned, last Repel/Lure used will be saved and the player will get prompted with the vanilla repel YES/NO option, unless I_REPEL_LURE_MENU is set to TRUE. +#define I_REPEL_LURE_MENU TRUE // If TRUE, the player is able to choose which Repel/Lure to use once the previous one runs out. Cursor position is saved by VAR_LAST_REPEL_LURE_USED if not 0. + #endif // GUARD_CONSTANTS_ITEM_CONFIG_H diff --git a/include/item_use.h b/include/item_use.h index d55f7cf0d7f1..bdbf2cdeac53 100644 --- a/include/item_use.h +++ b/include/item_use.h @@ -20,6 +20,7 @@ void ItemUseOutOfBattle_PPUp(u8); void ItemUseOutOfBattle_RareCandy(u8); void ItemUseOutOfBattle_TMHM(u8); void ItemUseOutOfBattle_Repel(u8); +void ItemUseOutOfBattle_Lure(u8); void ItemUseOutOfBattle_EscapeRope(u8); void ItemUseOutOfBattle_BlackWhiteFlute(u8); void ItemUseOutOfBattle_EvolutionStone(u8); diff --git a/include/strings.h b/include/strings.h index 3736adf9b01e..959bb8c22d18 100644 --- a/include/strings.h +++ b/include/strings.h @@ -938,6 +938,7 @@ extern const u8 gText_BootedUpTM[]; extern const u8 gText_TMHMContainedVar1[]; extern const u8 gText_PlayerUsedVar2[]; extern const u8 gText_RepelEffectsLingered[]; +extern const u8 gText_LureEffectsLingered[]; extern const u8 gText_UsedVar2WildLured[]; extern const u8 gText_UsedVar2WildRepelled[]; extern const u8 gText_BoxFull[]; diff --git a/src/data/items.h b/src/data/items.h index b0b53c31f545..5087ca03b540 100644 --- a/src/data/items.h +++ b/src/data/items.h @@ -1565,7 +1565,7 @@ const struct Item gItems[] = .description = sLureDesc, .pocket = POCKET_ITEMS, .type = ITEM_USE_BAG_MENU, - .fieldUseFunc = ItemUseOutOfBattle_CannotUse, + .fieldUseFunc = ItemUseOutOfBattle_Lure, .secondaryId = 0, .flingPower = 30, }, @@ -1579,7 +1579,7 @@ const struct Item gItems[] = .description = sSuperLureDesc, .pocket = POCKET_ITEMS, .type = ITEM_USE_BAG_MENU, - .fieldUseFunc = ItemUseOutOfBattle_CannotUse, + .fieldUseFunc = ItemUseOutOfBattle_Lure, .secondaryId = 0, .flingPower = 30, }, @@ -1593,7 +1593,7 @@ const struct Item gItems[] = .description = sMaxLureDesc, .pocket = POCKET_ITEMS, .type = ITEM_USE_BAG_MENU, - .fieldUseFunc = ItemUseOutOfBattle_CannotUse, + .fieldUseFunc = ItemUseOutOfBattle_Lure, .secondaryId = 0, .flingPower = 30, }, diff --git a/src/item_use.c b/src/item_use.c index 913c0700fa6f..fee00853fe22 100644 --- a/src/item_use.c +++ b/src/item_use.c @@ -67,7 +67,9 @@ static void Task_ShowTMHMContainedMessage(u8); static void UseTMHMYesNo(u8); static void UseTMHM(u8); static void Task_StartUseRepel(u8); +static void Task_StartUseLure(u8 taskId); static void Task_UseRepel(u8); +static void Task_UseLure(u8 taskId); static void Task_CloseCantUseKeyItemMessage(u8); static void SetDistanceOfClosestHiddenItem(u8, s16, s16); static void CB2_OpenPokeblockFromBag(void); @@ -846,7 +848,7 @@ static void RemoveUsedItem(void) void ItemUseOutOfBattle_Repel(u8 taskId) { - if (VarGet(VAR_REPEL_STEP_COUNT) == 0) + if (REPEL_STEP_COUNT == 0) gTasks[taskId].func = Task_StartUseRepel; else if (!InBattlePyramid()) DisplayItemMessage(taskId, FONT_NORMAL, gText_RepelEffectsLingered, CloseItemMessage); @@ -871,6 +873,53 @@ static void Task_UseRepel(u8 taskId) if (!IsSEPlaying()) { VarSet(VAR_REPEL_STEP_COUNT, ItemId_GetHoldEffectParam(gSpecialVar_ItemId)); + #if VAR_LAST_REPEL_LURE_USED != 0 + VarSet(VAR_LAST_REPEL_LURE_USED, gSpecialVar_ItemId); + #endif + RemoveUsedItem(); + if (!InBattlePyramid()) + DisplayItemMessage(taskId, FONT_NORMAL, gStringVar4, CloseItemMessage); + else + DisplayItemMessageInBattlePyramid(taskId, gStringVar4, Task_CloseBattlePyramidBagMessage); + } +} +void HandleUseExpiredRepel(void) +{ +#if VAR_LAST_REPEL_LURE_USED != 0 + VarSet(VAR_REPEL_STEP_COUNT, ItemId_GetHoldEffectParam(VarGet(VAR_LAST_REPEL_LURE_USED))); +#endif +} + +void ItemUseOutOfBattle_Lure(u8 taskId) +{ + if (LURE_STEP_COUNT == 0) + gTasks[taskId].func = Task_StartUseLure; + else if (!InBattlePyramid()) + DisplayItemMessage(taskId, FONT_NORMAL, gText_LureEffectsLingered, CloseItemMessage); + else + DisplayItemMessageInBattlePyramid(taskId, gText_LureEffectsLingered, Task_CloseBattlePyramidBagMessage); +} + +static void Task_StartUseLure(u8 taskId) +{ + s16* data = gTasks[taskId].data; + + if (++data[8] > 7) + { + data[8] = 0; + PlaySE(SE_REPEL); + gTasks[taskId].func = Task_UseLure; + } +} + +static void Task_UseLure(u8 taskId) +{ + if (!IsSEPlaying()) + { + VarSet(VAR_REPEL_STEP_COUNT, ItemId_GetHoldEffectParam(gSpecialVar_ItemId) | REPEL_LURE_MASK); + #if VAR_LAST_REPEL_LURE_USED != 0 + VarSet(VAR_LAST_REPEL_LURE_USED, gSpecialVar_ItemId); + #endif RemoveUsedItem(); if (!InBattlePyramid()) DisplayItemMessage(taskId, FONT_NORMAL, gStringVar4, CloseItemMessage); @@ -879,6 +928,13 @@ static void Task_UseRepel(u8 taskId) } } +void HandleUseExpiredLure(void) +{ +#if VAR_LAST_REPEL_LURE_USED != 0 + VarSet(VAR_REPEL_STEP_COUNT, ItemId_GetHoldEffectParam(VarGet(VAR_LAST_REPEL_LURE_USED)) | REPEL_LURE_MASK); +#endif +} + static void Task_UsedBlackWhiteFlute(u8 taskId) { if(++gTasks[taskId].data[8] > 7) diff --git a/src/pokemon.c b/src/pokemon.c index 98ac99256007..89edec1061a8 100644 --- a/src/pokemon.c +++ b/src/pokemon.c @@ -3329,7 +3329,6 @@ void CreateBoxMon(struct BoxPokemon *boxMon, u16 species, u8 level, u8 fixedIV, } else // Player is the OT { - u32 shinyValue; value = gSaveBlock2Ptr->playerTrainerId[0] | (gSaveBlock2Ptr->playerTrainerId[1] << 8) | (gSaveBlock2Ptr->playerTrainerId[2] << 16) @@ -3337,22 +3336,24 @@ void CreateBoxMon(struct BoxPokemon *boxMon, u16 species, u8 level, u8 fixedIV, if (gBaseStats[species].flags & SPECIES_FLAG_SHINY_LOCKED) { - do + while (GET_SHINY_VALUE(value, personality) < SHINY_ODDS) { - // Choose random personalities until one that results in a non-shiny Pokémon personality = Random32(); - shinyValue = HIHALF(value) ^ LOHALF(value) ^ HIHALF(personality) ^ LOHALF(personality); - } while (shinyValue < SHINY_ODDS); + } } - else if (CheckBagHasItem(ITEM_SHINY_CHARM, 1)) + else { - u32 rolls = 0; - do + u32 totalRerolls = 0; + if (CheckBagHasItem(ITEM_SHINY_CHARM, 1)) + totalRerolls += I_SHINY_CHARM_REROLLS; + if (LURE_STEP_COUNT != 0) + totalRerolls += 1; + + while (GET_SHINY_VALUE(value, personality) >= SHINY_ODDS && totalRerolls > 0) { personality = Random32(); - shinyValue = HIHALF(value) ^ LOHALF(value) ^ HIHALF(personality) ^ LOHALF(personality); - rolls++; - } while (shinyValue >= SHINY_ODDS && rolls < I_SHINY_CHARM_REROLLS); + totalRerolls--; + } } } diff --git a/src/script_menu.c b/src/script_menu.c index 6633332f3fbc..df7a12f6d863 100644 --- a/src/script_menu.c +++ b/src/script_menu.c @@ -90,12 +90,10 @@ static u16 GetLengthWithExpandedPlayerName(const u8 *str) return length; } -static void DrawMultichoiceMenu(u8 left, u8 top, u8 multichoiceId, bool8 ignoreBPress, u8 cursorPos) +static void DrawMultichoiceMenuInternal(u8 left, u8 top, u8 multichoiceId, bool8 ignoreBPress, u8 cursorPos, const struct MenuAction *actions, int count) { int i; u8 windowId; - u8 count = sMultichoiceLists[multichoiceId].count; - const struct MenuAction *actions = sMultichoiceLists[multichoiceId].list; int width = 0; u8 newWidth; @@ -114,6 +112,84 @@ static void DrawMultichoiceMenu(u8 left, u8 top, u8 multichoiceId, bool8 ignoreB InitMultichoiceCheckWrap(ignoreBPress, count, windowId, multichoiceId); } +static void DrawMultichoiceMenu(u8 left, u8 top, u8 multichoiceId, bool8 ignoreBPress, u8 cursorPos) +{ + DrawMultichoiceMenuInternal(left, top, multichoiceId, ignoreBPress, cursorPos, sMultichoiceLists[multichoiceId].list, sMultichoiceLists[multichoiceId].count); +} + +#if I_REPEL_LURE_MENU == TRUE +void TryDrawRepelMenu(void) +{ + static const u16 repelItems[] = {ITEM_REPEL, ITEM_SUPER_REPEL, ITEM_MAX_REPEL}; + struct MenuAction menuItems[ARRAY_COUNT(repelItems) + 1] = {NULL}; + int i, count = 0, menuPos = 0; + + for (i = 0; i < ARRAY_COUNT(repelItems); i++) + { + if (CheckBagHasItem(repelItems[i], 1)) + { + VarSet(VAR_0x8004 + count, repelItems[i]); + #if VAR_LAST_REPEL_LURE_USED != 0 + if (VarGet(VAR_LAST_REPEL_LURE_USED) == repelItems[i]) + menuPos = count; + #endif + menuItems[count].text = ItemId_GetName(repelItems[i]); + count++; + } + } + + if (count > 1) + DrawMultichoiceMenuInternal(0, 0, 0, FALSE, menuPos, menuItems, count); + + gSpecialVar_Result = (count > 1); +} + +void HandleRepelMenuChoice(void) +{ + gSpecialVar_0x8004 = VarGet(VAR_0x8004 + gSpecialVar_Result); // Get item Id; + VarSet(VAR_REPEL_STEP_COUNT, ItemId_GetHoldEffectParam(gSpecialVar_0x8004)); +#if VAR_LAST_REPEL_LURE_USED != 0 + VarSet(VAR_LAST_REPEL_LURE_USED, gSpecialVar_0x8004); +#endif +} + +void TryDrawLureMenu(void) +{ + static const u16 lureItems[] = {ITEM_LURE, ITEM_SUPER_LURE, ITEM_MAX_LURE}; + struct MenuAction menuItems[ARRAY_COUNT(lureItems) + 1] = {NULL}; + int i, count = 0, menuPos = 0; + + + for (i = 0; i < ARRAY_COUNT(lureItems); i++) + { + if (CheckBagHasItem(lureItems[i], 1)) + { + VarSet(VAR_0x8004 + count, lureItems[i]); + #if VAR_LAST_REPEL_LURE_USED != 0 + if (VarGet(VAR_LAST_REPEL_LURE_USED) == lureItems[i]) + menuPos = count; + #endif + menuItems[count].text = ItemId_GetName(lureItems[i]); + count++; + } + } + + if (count > 1) + DrawMultichoiceMenuInternal(0, 0, 0, FALSE, menuPos, menuItems, count); + + gSpecialVar_Result = (count > 1); +} + +void HandleLureMenuChoice(void) +{ + gSpecialVar_0x8004 = VarGet(VAR_0x8004 + gSpecialVar_Result); // Get item Id; + VarSet(VAR_REPEL_STEP_COUNT, ItemId_GetHoldEffectParam(gSpecialVar_0x8004) | REPEL_LURE_MASK); +#if VAR_LAST_REPEL_LURE_USED != 0 + VarSet(VAR_LAST_REPEL_LURE_USED, gSpecialVar_0x8004); +#endif +} +#endif //I_REPEL_LURE_MENU == TRUE + #define tLeft data[0] #define tTop data[1] #define tRight data[2] diff --git a/src/strings.c b/src/strings.c index 4452fd825608..8815212e0555 100644 --- a/src/strings.c +++ b/src/strings.c @@ -244,6 +244,7 @@ const u8 gText_BootedUpHM[] = _("Booted up an HM."); const u8 gText_TMHMContainedVar1[] = _("It contained\n{STR_VAR_1}.\pTeach {STR_VAR_1}\nto a POKéMON?"); const u8 gText_PlayerUsedVar2[] = _("{PLAYER} used the\n{STR_VAR_2}.{PAUSE_UNTIL_PRESS}"); const u8 gText_RepelEffectsLingered[] = _("But the effects of a REPEL\nlingered from earlier.{PAUSE_UNTIL_PRESS}"); +const u8 gText_LureEffectsLingered[] = _("But the effects of a Lure\nlingered from earlier.{PAUSE_UNTIL_PRESS}"); const u8 gText_UsedVar2WildLured[] = _("{PLAYER} used the\n{STR_VAR_2}.\pWild POKéMON will be lured.{PAUSE_UNTIL_PRESS}"); const u8 gText_UsedVar2WildRepelled[] = _("{PLAYER} used the\n{STR_VAR_2}.\pWild POKéMON will be repelled.{PAUSE_UNTIL_PRESS}"); const u8 gText_BoxFull[] = _("The BOX is full.{PAUSE_UNTIL_PRESS}"); diff --git a/src/wild_encounter.c b/src/wild_encounter.c index 8bf8c45e0eb4..69f60182f66a 100644 --- a/src/wild_encounter.c +++ b/src/wild_encounter.c @@ -20,11 +20,13 @@ #include "constants/abilities.h" #include "constants/battle_config.h" #include "constants/game_stat.h" +#include "constants/item.h" #include "constants/items.h" #include "constants/layouts.h" #include "constants/weather.h" extern const u8 EventScript_RepelWoreOff[]; +extern const u8 EventScript_LureWoreOff[]; #define MAX_ENCOUNTER_RATE 2880 @@ -54,6 +56,7 @@ static void FeebasSeedRng(u16 seed); static bool8 IsWildLevelAllowedByRepel(u8 level); static void ApplyFluteEncounterRateMod(u32 *encRate); static void ApplyCleanseTagEncounterRateMod(u32 *encRate); +static u8 GetMaxLevelOfSpeciesInWildTable(const struct WildPokemon *wildMon, u16 species, u8 area); static bool8 TryGetAbilityInfluencedWildMonIndex(const struct WildPokemon *wildMon, u8 type, u16 ability, u8 *monIndex); static bool8 IsAbilityAllowingEncounter(u8 level); @@ -181,58 +184,82 @@ static void FeebasSeedRng(u16 seed) // LAND_WILD_COUNT static u8 ChooseWildMonIndex_Land(void) { + u8 wildMonIndex = 0; + bool8 swap = FALSE; u8 rand = Random() % ENCOUNTER_CHANCE_LAND_MONS_TOTAL; if (rand < ENCOUNTER_CHANCE_LAND_MONS_SLOT_0) - return 0; + wildMonIndex = 0; else if (rand >= ENCOUNTER_CHANCE_LAND_MONS_SLOT_0 && rand < ENCOUNTER_CHANCE_LAND_MONS_SLOT_1) - return 1; + wildMonIndex = 1; else if (rand >= ENCOUNTER_CHANCE_LAND_MONS_SLOT_1 && rand < ENCOUNTER_CHANCE_LAND_MONS_SLOT_2) - return 2; + wildMonIndex = 2; else if (rand >= ENCOUNTER_CHANCE_LAND_MONS_SLOT_2 && rand < ENCOUNTER_CHANCE_LAND_MONS_SLOT_3) - return 3; + wildMonIndex = 3; else if (rand >= ENCOUNTER_CHANCE_LAND_MONS_SLOT_3 && rand < ENCOUNTER_CHANCE_LAND_MONS_SLOT_4) - return 4; + wildMonIndex = 4; else if (rand >= ENCOUNTER_CHANCE_LAND_MONS_SLOT_4 && rand < ENCOUNTER_CHANCE_LAND_MONS_SLOT_5) - return 5; + wildMonIndex = 5; else if (rand >= ENCOUNTER_CHANCE_LAND_MONS_SLOT_5 && rand < ENCOUNTER_CHANCE_LAND_MONS_SLOT_6) - return 6; + wildMonIndex = 6; else if (rand >= ENCOUNTER_CHANCE_LAND_MONS_SLOT_6 && rand < ENCOUNTER_CHANCE_LAND_MONS_SLOT_7) - return 7; + wildMonIndex = 7; else if (rand >= ENCOUNTER_CHANCE_LAND_MONS_SLOT_7 && rand < ENCOUNTER_CHANCE_LAND_MONS_SLOT_8) - return 8; + wildMonIndex = 8; else if (rand >= ENCOUNTER_CHANCE_LAND_MONS_SLOT_8 && rand < ENCOUNTER_CHANCE_LAND_MONS_SLOT_9) - return 9; + wildMonIndex = 9; else if (rand >= ENCOUNTER_CHANCE_LAND_MONS_SLOT_9 && rand < ENCOUNTER_CHANCE_LAND_MONS_SLOT_10) - return 10; + wildMonIndex = 10; else - return 11; + wildMonIndex = 11; + + if (LURE_STEP_COUNT != 0 && (Random() % 10 < 2)) + swap = TRUE; + + if (swap) + wildMonIndex = 11 - wildMonIndex; + + return wildMonIndex; } // ROCK_WILD_COUNT / WATER_WILD_COUNT static u8 ChooseWildMonIndex_WaterRock(void) { + u8 wildMonIndex = 0; + bool8 swap = FALSE; u8 rand = Random() % ENCOUNTER_CHANCE_WATER_MONS_TOTAL; if (rand < ENCOUNTER_CHANCE_WATER_MONS_SLOT_0) - return 0; + wildMonIndex = 0; else if (rand >= ENCOUNTER_CHANCE_WATER_MONS_SLOT_0 && rand < ENCOUNTER_CHANCE_WATER_MONS_SLOT_1) - return 1; + wildMonIndex = 1; else if (rand >= ENCOUNTER_CHANCE_WATER_MONS_SLOT_1 && rand < ENCOUNTER_CHANCE_WATER_MONS_SLOT_2) - return 2; + wildMonIndex = 2; else if (rand >= ENCOUNTER_CHANCE_WATER_MONS_SLOT_2 && rand < ENCOUNTER_CHANCE_WATER_MONS_SLOT_3) - return 3; + wildMonIndex = 3; else - return 4; + wildMonIndex = 4; + + if (LURE_STEP_COUNT != 0 && (Random() % 10 < 2)) + swap = TRUE; + + if (swap) + wildMonIndex = 4 - wildMonIndex; + + return wildMonIndex; } // FISH_WILD_COUNT static u8 ChooseWildMonIndex_Fishing(u8 rod) { u8 wildMonIndex = 0; + bool8 swap = FALSE; u8 rand = Random() % max(max(ENCOUNTER_CHANCE_FISHING_MONS_OLD_ROD_TOTAL, ENCOUNTER_CHANCE_FISHING_MONS_GOOD_ROD_TOTAL), ENCOUNTER_CHANCE_FISHING_MONS_SUPER_ROD_TOTAL); + if (LURE_STEP_COUNT != 0 && (Random() % 10 < 2)) + swap = TRUE; + switch (rod) { case OLD_ROD: @@ -240,6 +267,9 @@ static u8 ChooseWildMonIndex_Fishing(u8 rod) wildMonIndex = 0; else wildMonIndex = 1; + + if (swap) + wildMonIndex = 1 - wildMonIndex; break; case GOOD_ROD: if (rand < ENCOUNTER_CHANCE_FISHING_MONS_GOOD_ROD_SLOT_2) @@ -248,6 +278,9 @@ static u8 ChooseWildMonIndex_Fishing(u8 rod) wildMonIndex = 3; if (rand >= ENCOUNTER_CHANCE_FISHING_MONS_GOOD_ROD_SLOT_3 && rand < ENCOUNTER_CHANCE_FISHING_MONS_GOOD_ROD_SLOT_4) wildMonIndex = 4; + + if (swap) + wildMonIndex = 6 - wildMonIndex; break; case SUPER_ROD: if (rand < ENCOUNTER_CHANCE_FISHING_MONS_SUPER_ROD_SLOT_5) @@ -260,46 +293,61 @@ static u8 ChooseWildMonIndex_Fishing(u8 rod) wildMonIndex = 8; if (rand >= ENCOUNTER_CHANCE_FISHING_MONS_SUPER_ROD_SLOT_8 && rand < ENCOUNTER_CHANCE_FISHING_MONS_SUPER_ROD_SLOT_9) wildMonIndex = 9; + + if (swap) + wildMonIndex = 14 - wildMonIndex; break; } return wildMonIndex; } -static u8 ChooseWildMonLevel(const struct WildPokemon *wildPokemon) +static u8 ChooseWildMonLevel(const struct WildPokemon *wildPokemon, u8 wildMonIndex, u8 area) { u8 min; u8 max; u8 range; u8 rand; - // Make sure minimum level is less than maximum level - if (wildPokemon->maxLevel >= wildPokemon->minLevel) - { - min = wildPokemon->minLevel; - max = wildPokemon->maxLevel; - } - else + if (LURE_STEP_COUNT == 0) { - min = wildPokemon->maxLevel; - max = wildPokemon->minLevel; - } - range = max - min + 1; - rand = Random() % range; + // Make sure minimum level is less than maximum level + if (wildPokemon[wildMonIndex].maxLevel >= wildPokemon[wildMonIndex].minLevel) + { + min = wildPokemon[wildMonIndex].minLevel; + max = wildPokemon[wildMonIndex].maxLevel; + } + else + { + min = wildPokemon[wildMonIndex].maxLevel; + max = wildPokemon[wildMonIndex].minLevel; + } + range = max - min + 1; + rand = Random() % range; - // check ability for max level mon - if (!GetMonData(&gPlayerParty[0], MON_DATA_SANITY_IS_EGG)) - { - u16 ability = GetMonAbility(&gPlayerParty[0]); - if (ability == ABILITY_HUSTLE || ability == ABILITY_VITAL_SPIRIT || ability == ABILITY_PRESSURE) + // check ability for max level mon + if (!GetMonData(&gPlayerParty[0], MON_DATA_SANITY_IS_EGG)) { - if (Random() % 2 == 0) - return max; + u16 ability = GetMonAbility(&gPlayerParty[0]); + if (ability == ABILITY_HUSTLE || ability == ABILITY_VITAL_SPIRIT || ability == ABILITY_PRESSURE) + { + if (Random() % 2 == 0) + return max; - if (rand != 0) - rand--; + if (rand != 0) + rand--; + } } + return min + rand; + } + else + { + // Looks for the max level of all slots that share the same species as the selected slot. + max = GetMaxLevelOfSpeciesInWildTable(wildPokemon, wildPokemon[wildMonIndex].species, area); + if (max > 0) + return max + 1; + else // Failsafe + return wildPokemon[wildMonIndex].maxLevel + 1; } - return min + rand; } static u16 GetCurrentMapWildMonHeaderId(void) @@ -461,7 +509,7 @@ static bool8 TryGenerateWildMon(const struct WildPokemonInfo *wildMonInfo, u8 ar break; } - level = ChooseWildMonLevel(&wildMonInfo->wildPokemon[wildMonIndex]); + level = ChooseWildMonLevel(wildMonInfo->wildPokemon, wildMonIndex, area); if (flags & WILD_CHECK_REPEL && !IsWildLevelAllowedByRepel(level)) return FALSE; if (gMapHeader.mapLayoutId != LAYOUT_BATTLE_FRONTIER_BATTLE_PIKE_ROOM_WILD_MONS && flags & WILD_CHECK_KEEN_EYE && !IsAbilityAllowingEncounter(level)) @@ -474,7 +522,7 @@ static bool8 TryGenerateWildMon(const struct WildPokemonInfo *wildMonInfo, u8 ar static u16 GenerateFishingWildMon(const struct WildPokemonInfo *wildMonInfo, u8 rod) { u8 wildMonIndex = ChooseWildMonIndex_Fishing(rod); - u8 level = ChooseWildMonLevel(&wildMonInfo->wildPokemon[wildMonIndex]); + u8 level = ChooseWildMonLevel(wildMonInfo->wildPokemon, wildMonIndex, WILD_AREA_FISHING); CreateWildMon(wildMonInfo->wildPokemon[wildMonIndex].species, level); return wildMonInfo->wildPokemon[wildMonIndex].species; @@ -521,6 +569,8 @@ static bool8 DoWildEncounterRateTest(u32 encounterRate, bool8 ignoreAbility) encounterRate = encounterRate * 80 / 100; ApplyFluteEncounterRateMod(&encounterRate); ApplyCleanseTagEncounterRateMod(&encounterRate); + if (LURE_STEP_COUNT != 0) + encounterRate *= 2; if (!ignoreAbility && !GetMonData(&gPlayerParty[0], MON_DATA_SANITY_IS_EGG)) { u32 ability = GetMonAbility(&gPlayerParty[0]); @@ -825,7 +875,7 @@ void FishingWildEncounter(u8 rod) if (CheckFeebas() == TRUE) { - u8 level = ChooseWildMonLevel(&sWildFeebas); + u8 level = ChooseWildMonLevel(&sWildFeebas, 0, WILD_AREA_FISHING); species = sWildFeebas.species; CreateWildMon(species, level); @@ -892,24 +942,37 @@ u16 GetLocalWaterMon(void) bool8 UpdateRepelCounter(void) { - u16 steps; + u16 repelLureVar = VarGet(VAR_REPEL_STEP_COUNT); + u16 steps = REPEL_LURE_STEPS(repelLureVar); + bool32 isLure = IS_LAST_USED_LURE(repelLureVar); if (InBattlePike() || InBattlePyramid()) return FALSE; if (InUnionRoom() == TRUE) return FALSE; - steps = VarGet(VAR_REPEL_STEP_COUNT); - if (steps != 0) { steps--; - VarSet(VAR_REPEL_STEP_COUNT, steps); - if (steps == 0) + if (!isLure) { - ScriptContext_SetupScript(EventScript_RepelWoreOff); - return TRUE; + VarSet(VAR_REPEL_STEP_COUNT, steps); + if (steps == 0) + { + ScriptContext_SetupScript(EventScript_RepelWoreOff); + return TRUE; + } } + else + { + VarSet(VAR_REPEL_STEP_COUNT, steps | REPEL_LURE_MASK); + if (steps == 0) + { + ScriptContext_SetupScript(EventScript_LureWoreOff); + return TRUE; + } + } + } return FALSE; } @@ -918,7 +981,7 @@ static bool8 IsWildLevelAllowedByRepel(u8 wildLevel) { u8 i; - if (!VarGet(VAR_REPEL_STEP_COUNT)) + if (!REPEL_STEP_COUNT) return TRUE; for (i = 0; i < PARTY_SIZE; i++) @@ -976,6 +1039,34 @@ static bool8 TryGetRandomWildMonIndexByType(const struct WildPokemon *wildMon, u return TRUE; } +#include "data.h" + +static u8 GetMaxLevelOfSpeciesInWildTable(const struct WildPokemon *wildMon, u16 species, u8 area) +{ + u8 i, maxLevel = 0, numMon = 0; + + switch (area) + { + case WILD_AREA_LAND: + numMon = LAND_WILD_COUNT; + break; + case WILD_AREA_WATER: + numMon = WATER_WILD_COUNT; + break; + case WILD_AREA_ROCKS: + numMon = ROCK_WILD_COUNT; + break; + } + + for (i = 0; i < numMon; i++) + { + if (wildMon[i].species == species && wildMon[i].maxLevel > maxLevel) + maxLevel = wildMon[i].maxLevel; + } + + return maxLevel; +} + static bool8 TryGetAbilityInfluencedWildMonIndex(const struct WildPokemon *wildMon, u8 type, u16 ability, u8 *monIndex) { if (GetMonData(&gPlayerParty[0], MON_DATA_SANITY_IS_EGG))