- 146
- Posts
- 17
- Years
- Age 26
- Seen Dec 22, 2024
If you've implemented this code before 6/8/2020, please, reimplement it! There were quite a few bugs and things not accounted for when I originally posted this, but everything should be 100% good to go now. My apologies. I hope this isn't much of an inconvenience for you.
Ever since playing Pokemon Clover, I have been absolutely in love with how they handled difficulty. If you haven't played it, it works like this: Your experience gain slows down as you hit a soft level cap. For example, if you want a player to be around level 13 for the first gym, once a player hits level 13, their experience gain will slow down further and further as the player's level gets higher. This promotes less grinding and more strategy, which I think is a great way to design a Pokemon hack.
So, after finding some unofficial documentation on Pastebin, I decided to try to implement it myself, and to my surprise, it wasn't very hard.
First, go into include\event_data.h and add this define above the includes:
At the bottom of the file (before #endif, of course), add these lines:
event_data.h is used for these, as both src\battle_script_commands.c and src\daycare.c both include it, and it includes include\constants\flags.h, which we need for one of our arrays when we define it.
Now, go into src\battle_script_commands.c and search for this line:
All of our variables for this will go between the end of the list of static void Cmd_whatever and void (* const gBattleScriptingCommandsTable[])(void) =, so this may not be the end of the list of methods if you're using pokemon_expansion or some other modification.
Under that line, add a new array of flags that will denote your level caps under that line. For this tutorial, I'll be using badge flags to denote my caps (meaning I'll have a soft cap for every gym leader), but you can define whatever flags you so desire here.
If you don't want to use badges, or want to use more flags than just badges, check out include\constants\flags.h for a list of predefined flags that are available for use.
Under your newly defined array of flags, add these lines:
A quick explanation of everything:
sLevelCaps is going to be whatever you want your level cap to be. As of right now, everything is set up in increments of 10--this was done purely for this example, and you should definitely change this to fit the difficulty curve of your hack. You'll want to make sure that sLevelCaps is the same length as your sLevelCapFlags array, as they need to be the same length for some code later on. So for example, if you have 13 custom flags, you'll want sLevelCaps to have 13 different values as well.
sLevelCapReduction is used to scale experience gain more and more the further you get away from the cap. These values are taken directly from the first unofficial Clover documentation linked above.
sRelativePartyScaling is another mechanic in Clover that allows lower leveled Pokemon in your party to gain more experience. For example, if the average level of a team is 45, and the Pokemon you are training is level 37, 37 - 45 = -8. Since arrays begin at 0, we then add 14 to this number (as 14 is the lowest negative value shown in the second unofficial Clover documentation) and are left with 6. sRelativePartyScaling[6] is 1.80, so total calculated experience will be multiplied by 1.8 for the underleveled Pokemon.
Next, search for Cmd_getexp(void), and add these methods above it:
GetTeamLevel simply returns the average level of your party for the relative party scaling, while not including levels that are too low that can skew the average.
GetPkmnExpMultiplier finds the level difference (if you're above the level cap, of course) and applies that reduction, then applies the relative party experience modifier, and returns these modifiers multiplied together.
Big shoutouts to blakcat for his help with reworking this code to work properly in double-battles.
Now, search for this if statement within case 2 of Cmd_getexp:
Immediately after that if statement, add this line:
Then, replace these lines:
With these lines:
Which should apply your multiplier to your gained experience. With that, we are done with the battle experience gain system. Thanks again to blakcat for helping rework my original code.
Now, to add functionality to the Day Care experience gain system. Open up src\daycare.c and look for this if statement in the TakeSelectedPokemonFromDaycare method:
We're going to completely replace this if statement with this new one:
Thanks to defining the flag and level cap arrays as externs in the include\event_data.h file earlier, we do not need to redefine them here.
And now, we're going to replace the entire GetLevelAfterDaycareSteps method with some very similar code:
Now, this is handled quite a bit differently than the battle system, so let me explain. The original Day Care gives your Pokemon one experience point for every step you take after dropping it off at the Day Care. I found that the best way to reduce experience gained from the Day Care was to simply take the total amount of steps and divide that number by the level difference plus one. After doing that, add it to the Day Care Pokemon's original experience from when it got dropped off, and then set the new experience.
I tried making this work with the same level reduction curve that is used in src\battle_script_commands.c, but this seemed to work much better. As for dividing the level cap in half if i <= 2, in my testing I found that since earlier levels require a lot less experience to level up, the level caps should proc earlier than under a regular battle circumstance. This can be adjusted depending on what your level caps are/how many you have, I just found 1 or 2 to be a good cutoff point.
And that's it!
Your hack now has a soft cap experience gain system.
Thanks to LuckyTyphlosion on Pastebin for posting their findings regarding the level cap system and how it works.
Ever since playing Pokemon Clover, I have been absolutely in love with how they handled difficulty. If you haven't played it, it works like this: Your experience gain slows down as you hit a soft level cap. For example, if you want a player to be around level 13 for the first gym, once a player hits level 13, their experience gain will slow down further and further as the player's level gets higher. This promotes less grinding and more strategy, which I think is a great way to design a Pokemon hack.
So, after finding some unofficial documentation on Pastebin, I decided to try to implement it myself, and to my surprise, it wasn't very hard.
First, go into include\event_data.h and add this define above the includes:
Code:
#define NUM_SOFT_CAPS 8
At the bottom of the file (before #endif, of course), add these lines:
Code:
extern const u16 sLevelCapFlags[NUM_SOFT_CAPS];
extern const u16 sLevelCaps[NUM_SOFT_CAPS];
Now, go into src\battle_script_commands.c and search for this line:
Code:
static void Cmd_trainerslideout(void);
Under that line, add a new array of flags that will denote your level caps under that line. For this tutorial, I'll be using badge flags to denote my caps (meaning I'll have a soft cap for every gym leader), but you can define whatever flags you so desire here.
Code:
const u16 sLevelCapFlags[NUM_SOFT_CAPS] =
{
FLAG_BADGE01_GET, FLAG_BADGE02_GET, FLAG_BADGE03_GET, FLAG_BADGE04_GET,
FLAG_BADGE05_GET, FLAG_BADGE06_GET, FLAG_BADGE07_GET, FLAG_BADGE08_GET,
};
Under your newly defined array of flags, add these lines:
Code:
const u16 sLevelCaps[NUM_SOFT_CAPS] = { 10, 20, 30, 40, 50, 60, 70, 80 };
const double sLevelCapReduction[7] = { .5, .33, .25, .20, .15, .10, .05 };
const double sRelativePartyScaling[27] =
{
3.00, 2.75, 2.50, 2.33, 2.25,
2.00, 1.80, 1.70, 1.60, 1.50,
1.40, 1.30, 1.20, 1.10, 1.00,
0.90, 0.80, 0.75, 0.66, 0.50,
0.40, 0.33, 0.25, 0.20, 0.15,
0.10, 0.05,
};
sLevelCaps is going to be whatever you want your level cap to be. As of right now, everything is set up in increments of 10--this was done purely for this example, and you should definitely change this to fit the difficulty curve of your hack. You'll want to make sure that sLevelCaps is the same length as your sLevelCapFlags array, as they need to be the same length for some code later on. So for example, if you have 13 custom flags, you'll want sLevelCaps to have 13 different values as well.
sLevelCapReduction is used to scale experience gain more and more the further you get away from the cap. These values are taken directly from the first unofficial Clover documentation linked above.
sRelativePartyScaling is another mechanic in Clover that allows lower leveled Pokemon in your party to gain more experience. For example, if the average level of a team is 45, and the Pokemon you are training is level 37, 37 - 45 = -8. Since arrays begin at 0, we then add 14 to this number (as 14 is the lowest negative value shown in the second unofficial Clover documentation) and are left with 6. sRelativePartyScaling[6] is 1.80, so total calculated experience will be multiplied by 1.8 for the underleveled Pokemon.
Next, search for Cmd_getexp(void), and add these methods above it:
Code:
u8 GetTeamLevel(void)
{
u8 i;
u16 partyLevel = 0;
u16 threshold = 0;
for (i = 0; i < PARTY_SIZE; i++)
{
if (GetMonData(&gPlayerParty[i], MON_DATA_SPECIES) != SPECIES_NONE)
partyLevel += gPlayerParty[i].level;
else
break;
}
partyLevel /= i;
threshold = partyLevel * .8;
partyLevel = 0;
for (i = 0; i < PARTY_SIZE; i++)
{
if (GetMonData(&gPlayerParty[i], MON_DATA_SPECIES) != SPECIES_NONE)
{
if (gPlayerParty[i].level >= threshold)
partyLevel += gPlayerParty[i].level;
}
else
break;
}
partyLevel /= i;
return partyLevel;
}
double GetPkmnExpMultiplier(u8 level)
{
u8 i;
double lvlCapMultiplier = 1.0;
u8 levelDiff;
s8 avgDiff;
// multiply the usual exp yield by the soft cap multiplier
for (i = 0; i < NUM_SOFT_CAPS; i++)
{
if (!FlagGet(sLevelCapFlags[i]) && level >= sLevelCaps[i])
{
levelDiff = level - sLevelCaps[i];
if (levelDiff > 6)
levelDiff = 6;
lvlCapMultiplier = sLevelCapReduction[levelDiff];
break;
}
}
// multiply the usual exp yield by the party level multiplier
avgDiff = level - GetTeamLevel();
if (avgDiff >= 12)
avgDiff = 12;
else if (avgDiff <= -14)
avgDiff = -14;
avgDiff += 14;
return lvlCapMultiplier * sRelativePartyScaling[avgDiff];
}
GetPkmnExpMultiplier finds the level difference (if you're above the level cap, of course) and applies that reduction, then applies the relative party experience modifier, and returns these modifiers multiplied together.
Big shoutouts to blakcat for his help with reworking this code to work properly in double-battles.
Now, search for this if statement within case 2 of Cmd_getexp:
Code:
if (GetMonData(&gPlayerParty[gBattleStruct->expGetterMonId], MON_DATA_HP))
Immediately after that if statement, add this line:
Code:
double expMultiplier = GetPkmnExpMultiplier(gPlayerParty[gBattleStruct->expGetterMonId].level);
Then, replace these lines:
Code:
if (gBattleStruct->sentInPokes & 1)
gBattleMoveDamage = *exp;
else
gBattleMoveDamage = 0;
if (holdEffect == HOLD_EFFECT_EXP_SHARE)
gBattleMoveDamage += gExpShareExp;
With these lines:
Code:
if (gBattleStruct->sentInPokes & 1)
gBattleMoveDamage = *exp * expMultiplier;
else
gBattleMoveDamage = 0;
if (holdEffect == HOLD_EFFECT_EXP_SHARE)
gBattleMoveDamage += gExpShareExp * expMultiplier;
Now, to add functionality to the Day Care experience gain system. Open up src\daycare.c and look for this if statement in the TakeSelectedPokemonFromDaycare method:
Code:
if (GetMonData(&pokemon, MON_DATA_LEVEL) != MAX_LEVEL)
We're going to completely replace this if statement with this new one:
Code:
if (GetMonData(&pokemon, MON_DATA_LEVEL) != MAX_LEVEL)
{
u8 level;
u8 i;
u8 cap;
experience = GetMonData(&pokemon, MON_DATA_EXP) + daycareMon->steps;
SetMonData(&pokemon, MON_DATA_EXP, &experience);
level = GetLevelFromMonExp(&pokemon);
for (i = 0; i < NUM_SOFT_CAPS; i++)
{
if (i <= 2)
cap = sLevelCaps[i] / 2;
else
cap = sLevelCaps[i];
if (!FlagGet(sLevelCapFlags[i]) && level >= cap)
{
u8 levelDiff;
u32 newSteps;
levelDiff = level - cap;
newSteps = daycareMon->steps / (levelDiff + 1);
experience = GetBoxMonData(&daycareMon->mon, MON_DATA_EXP) + newSteps;
SetMonData(&pokemon, MON_DATA_EXP, &experience);
break;
}
}
ApplyDaycareExperience(&pokemon);
}
And now, we're going to replace the entire GetLevelAfterDaycareSteps method with some very similar code:
Code:
static u8 GetLevelAfterDaycareSteps(struct BoxPokemon *mon, u32 steps)
{
struct BoxPokemon tempMon = *mon;
u32 experience = GetBoxMonData(mon, MON_DATA_EXP) + steps;
u8 i;
u8 level;
u8 cap;
// set experience now to be able to get levelAfter
SetBoxMonData(&tempMon, MON_DATA_EXP, &experience);
level = GetLevelFromBoxMonExp(&tempMon);
// loop through to check caps
for (i = 0; i < NUM_SOFT_CAPS; i++)
{
if (i <= 2)
cap = sLevelCaps[i] / 2;
else
cap = sLevelCaps[i];
if (!FlagGet(sLevelCapFlags[i]) && level >= cap)
{
u8 levelDiff;
u32 newSteps;
levelDiff = level - cap;
newSteps = steps / (levelDiff + 1);
experience = GetBoxMonData(mon, MON_DATA_EXP) + newSteps;
SetBoxMonData(&tempMon, MON_DATA_EXP, &experience);
break;
}
}
return GetLevelFromBoxMonExp(&tempMon);
}
I tried making this work with the same level reduction curve that is used in src\battle_script_commands.c, but this seemed to work much better. As for dividing the level cap in half if i <= 2, in my testing I found that since earlier levels require a lot less experience to level up, the level caps should proc earlier than under a regular battle circumstance. This can be adjusted depending on what your level caps are/how many you have, I just found 1 or 2 to be a good cutoff point.
And that's it!
Your hack now has a soft cap experience gain system.
Thanks to LuckyTyphlosion on Pastebin for posting their findings regarding the level cap system and how it works.
Last edited: