EV Caps and EV Items (#5269)

* EV Caps and EV Items

Introduces EV caps, inspired by level caps, with configurable options for various cap implementations. Additionally, modifies EV items to interact with these caps based on a configurable setting.

* EV Caps fixes

Changed the EV caps to be less redundant and work better overall.

* EV Caps Fix part 2

Set the items back to ItemUseOutOfBattle_Medicine, got rid of ItemUseOutOfBattle_EVItem and ItemUseCB_EVItem, and reverted CB2_ReturnToPartyMenuUsingItem to CB2_ReturnToPartyMenuUsingRareCandy

* EV Caps Fixes Part 3

Fixed being able to use EV items to go over 252 limit on single stats.

* Update src/ev_caps.c

Co-authored-by: Alex <93446519+AlexOn1ine@users.noreply.github.com>

---------

Co-authored-by: Alex <93446519+AlexOn1ine@users.noreply.github.com>
This commit is contained in:
Erickson Russell 2024-09-01 17:15:39 -04:00 committed by GitHub
parent 484acdc138
commit de242c8a29
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
5 changed files with 110 additions and 29 deletions

16
include/config/ev_caps.h Normal file
View file

@ -0,0 +1,16 @@
#ifndef GUARD_CONFIG_EV_CAP_H
#define GUARD_CONFIG_EV_CAP_H
// Constants for EV Cap Types
#define EV_CAP_NONE 0 // Regular behavior, no EV caps are applied
#define EV_CAP_FLAG_LIST 1 // EV cap is chosen according to the first unset flag in `sEVCapFlagMap`
#define EV_CAP_VARIABLE 2 // EV cap is chosen according to the contents of the event variable specified by B_EV_CAP_VARIABLE
#define EV_CAP_NO_GAIN 3 // No EVs can be gained
// Configs for EV Cap
#define B_EV_CAP_TYPE EV_CAP_NONE // [EV_CAP_NONE, EV_CAP_FLAG_LIST, EV_CAP_VARIABLE, EV_CAP_NO_GAIN] choose the type of EV cap to apply#define B_EV_CAP_VARIABLE 12 // event variable used to derive EV cap if B_EV_CAP_TYPE is set to EV_CAP_VARIABLE
#define B_EV_CAP_VARIABLE 8 // event variable used to derive EV cap if B_EV_CAP_TYPE is set to EV_CAP_VARIABLE
#define B_EV_ITEMS_CAP FALSE // If set to true, EV-boosting items can't be used to go over the EV cap
#endif /*GUARD_CONFIG_EV_CAP_H*/

View file

@ -8,6 +8,7 @@
#include "config/level_caps.h"
#include "config/pokemon.h"
#include "config/overworld.h"
#include "config/ev_caps.h"
// Invalid Versions show as "----------" in Gen 4 and Gen 5's summary screen.
// In Gens 6 and 7, invalid versions instead show "a distant land" in the summary screen.

10
include/ev_caps.h Normal file
View file

@ -0,0 +1,10 @@
#ifndef GUARD_EV_CAP_H
#define GUARD_EV_CAP_H
#if B_EV_CAP_TYPE != EV_CAP_NONE && B_EV_CAP_TYPE != EV_CAP_FLAG_LIST && B_EV_CAP_TYPE != EV_CAP_VARIABLE && B_EV_CAP_TYPE != EV_CAP_NO_GAIN
#error "Invalid choice for B_EV_CAP_TYPE, must be one of [EV_CAP_NONE, EV_CAP_FLAG_LIST, EV_CAP_VARIABLE, EV_CAP_NO_GAIN]"
#endif
u32 GetCurrentEVCap(void);
#endif /* GUARD_EV_CAP_H */

41
src/ev_caps.c Normal file
View file

@ -0,0 +1,41 @@
#include "global.h"
#include "battle.h"
#include "event_data.h"
#include "ev_caps.h"
#include "pokemon.h"
u32 GetCurrentEVCap(void)
{
static const u16 sEvCapFlagMap[][2] = {
// Define EV caps for each milestone
{FLAG_BADGE01_GET, 30},
{FLAG_BADGE02_GET, 90},
{FLAG_BADGE03_GET, 150},
{FLAG_BADGE04_GET, 210},
{FLAG_BADGE05_GET, 270},
{FLAG_BADGE06_GET, 330},
{FLAG_BADGE07_GET, 390},
{FLAG_BADGE08_GET, 450},
{FLAG_IS_CHAMPION, MAX_TOTAL_EVS},
};
if (B_EV_CAP_TYPE == EV_CAP_FLAG_LIST)
{
for (u32 evCap = 0; evCap < ARRAY_COUNT(sEvCapFlagMap); evCap++)
{
if (!FlagGet(sEvCapFlagMap[evCap][0]))
return sEvCapFlagMap[evCap][1];
}
}
else if (B_EV_CAP_TYPE == EV_CAP_VARIABLE)
{
return VarGet(B_EV_CAP_VARIABLE);
}
else if (B_EV_CAP_TYPE == EV_CAP_NO_GAIN)
{
return 0;
}
return MAX_TOTAL_EVS;
}

View file

@ -60,6 +60,7 @@
#include "constants/union_room.h"
#include "constants/weather.h"
#include "wild_encounter.h"
#include "ev_caps.h"
#define FRIENDSHIP_EVO_THRESHOLD ((P_FRIENDSHIP_EVO_THRESHOLD >= GEN_9) ? 160 : 220)
@ -3752,6 +3753,9 @@ bool8 PokemonUseItemEffects(struct Pokemon *mon, u16 item, u8 partyIndex, u8 mov
s8 evChange;
u16 evCount;
// Determine the EV cap to use
u32 maxAllowedEVs = !B_EV_ITEMS_CAP ? MAX_TOTAL_EVS : GetCurrentEVCap();
// Get item hold effect
heldItem = GetMonData(mon, MON_DATA_HELD_ITEM, NULL);
if (heldItem == ITEM_ENIGMA_BERRY_E_READER)
@ -3879,27 +3883,31 @@ bool8 PokemonUseItemEffects(struct Pokemon *mon, u16 item, u8 partyIndex, u8 mov
if (evChange > 0) // Increasing EV (HP or Atk)
{
// Has EV increase limit already been reached?
if (evCount >= MAX_TOTAL_EVS)
// Check if the total EV limit is reached
if (evCount >= maxAllowedEVs)
return TRUE;
if (itemEffect[10] & ITEM10_IS_VITAMIN)
evCap = EV_ITEM_RAISE_LIMIT;
else
evCap = MAX_PER_STAT_EVS;
// Ensure the increase does not exceed the max EV per stat (252)
evCap = (itemEffect[10] & ITEM10_IS_VITAMIN) ? EV_ITEM_RAISE_LIMIT : MAX_PER_STAT_EVS;
// Check if the per-stat limit is reached
if (dataSigned >= evCap)
break;
// Limit the increase
return TRUE; // Prevents item use if the per-stat cap is already reached
if (dataSigned + evChange > evCap)
temp2 = evCap - (dataSigned + evChange) + evChange;
temp2 = evCap - dataSigned;
else
temp2 = evChange;
if (evCount + temp2 > MAX_TOTAL_EVS)
temp2 += MAX_TOTAL_EVS - (evCount + temp2);
// Ensure the total EVs do not exceed the maximum allowed (510)
if (evCount + temp2 > maxAllowedEVs)
temp2 = maxAllowedEVs - evCount;
// Prevent item use if no EVs can be increased
if (temp2 == 0)
return TRUE;
// Apply the EV increase
dataSigned += temp2;
}
else if (evChange < 0) // Decreasing EV (HP or Atk)
@ -4064,27 +4072,31 @@ bool8 PokemonUseItemEffects(struct Pokemon *mon, u16 item, u8 partyIndex, u8 mov
evChange = temp2;
if (evChange > 0) // Increasing EV
{
// Has EV increase limit already been reached?
if (evCount >= MAX_TOTAL_EVS)
// Check if the total EV limit is reached
if (evCount >= maxAllowedEVs)
return TRUE;
if (itemEffect[10] & ITEM10_IS_VITAMIN)
evCap = EV_ITEM_RAISE_LIMIT;
else
evCap = MAX_PER_STAT_EVS;
// Ensure the increase does not exceed the max EV per stat (252)
evCap = (itemEffect[10] & ITEM10_IS_VITAMIN) ? EV_ITEM_RAISE_LIMIT : MAX_PER_STAT_EVS;
// Check if the per-stat limit is reached
if (dataSigned >= evCap)
break;
// Limit the increase
return TRUE; // Prevents item use if the per-stat cap is already reached
if (dataSigned + evChange > evCap)
temp2 = evCap - (dataSigned + evChange) + evChange;
temp2 = evCap - dataSigned;
else
temp2 = evChange;
if (evCount + temp2 > MAX_TOTAL_EVS)
temp2 += MAX_TOTAL_EVS - (evCount + temp2);
// Ensure the total EVs do not exceed the maximum allowed (510)
if (evCount + temp2 > maxAllowedEVs)
temp2 = maxAllowedEVs - evCount;
// Prevent item use if no EVs can be increased
if (temp2 == 0)
return TRUE;
// Apply the EV increase
dataSigned += temp2;
}
else if (evChange < 0) // Decreasing EV
@ -5195,6 +5207,7 @@ void MonGainEVs(struct Pokemon *mon, u16 defeatedSpecies)
int i, multiplier;
u8 stat;
u8 bonus;
u32 currentEVCap = GetCurrentEVCap();
heldItem = GetMonData(mon, MON_DATA_HELD_ITEM, 0);
if (heldItem == ITEM_ENIGMA_BERRY_E_READER)
@ -5224,7 +5237,7 @@ void MonGainEVs(struct Pokemon *mon, u16 defeatedSpecies)
for (i = 0; i < NUM_STATS; i++)
{
if (totalEVs >= MAX_TOTAL_EVS)
if (totalEVs >= currentEVCap)
break;
if (CheckPartyHasHadPokerus(mon, 0))
@ -5275,8 +5288,8 @@ void MonGainEVs(struct Pokemon *mon, u16 defeatedSpecies)
if (holdEffect == HOLD_EFFECT_MACHO_BRACE)
evIncrease *= 2;
if (totalEVs + (s16)evIncrease > MAX_TOTAL_EVS)
evIncrease = ((s16)evIncrease + MAX_TOTAL_EVS) - (totalEVs + evIncrease);
if (totalEVs + (s16)evIncrease > currentEVCap)
evIncrease = ((s16)evIncrease + currentEVCap) - (totalEVs + evIncrease);
if (evs[i] + (s16)evIncrease > MAX_PER_STAT_EVS)
{