From e9d01b480829daaefde6bea250a50b94cb3b5668 Mon Sep 17 00:00:00 2001 From: Ariel Antonitis Date: Sat, 22 May 2021 02:10:12 -0400 Subject: [PATCH] Added a non-RNG, PID-preserving way to modify pokemon nature. (wip) Preserve shiny value when changing nature. (WIP) Preserve shiny value. (WIP) Preserve nature. (WIP) Removed some debugging statements. (WIP) Removed more debugging statements. (WIP) Nature changing now supports Unown. --- include/constants/pokemon.h | 1 + src/pokemon.c | 90 +++++++++++++++++++++++++++++++++++-- 2 files changed, 87 insertions(+), 4 deletions(-) diff --git a/include/constants/pokemon.h b/include/constants/pokemon.h index 51ef0c015b..a6b150e9e0 100644 --- a/include/constants/pokemon.h +++ b/include/constants/pokemon.h @@ -181,6 +181,7 @@ #define MON_DATA_SPEED2 86 #define MON_DATA_SPATK2 87 #define MON_DATA_SPDEF2 88 +#define MON_DATA_NATURE 89 // Ribbon IDs used by TV and Pokénav #define CHAMPION_RIBBON 0 diff --git a/src/pokemon.c b/src/pokemon.c index b4eb6f01af..da3b80baf0 100644 --- a/src/pokemon.c +++ b/src/pokemon.c @@ -3980,6 +3980,9 @@ u32 GetBoxMonData(struct BoxPokemon *boxMon, s32 field, u8 *data) | (substruct3->worldRibbon << 26); } break; + case MON_DATA_NATURE: + return boxMon->personality % 25; + break; default: break; } @@ -4032,6 +4035,10 @@ void SetMonData(struct Pokemon *mon, s32 field, const void *dataArg) break; case MON_DATA_SPECIES2: break; + case MON_DATA_NATURE: // Calculate stats after settings + SetBoxMonData(&mon->box, field, data); + CalculateMonStats(mon); + break; default: SetBoxMonData(&mon->box, field, data); break; @@ -4298,6 +4305,81 @@ void SetBoxMonData(struct BoxPokemon *boxMon, s32 field, const void *dataArg) substruct3->spDefenseIV = (ivs >> 25) & MAX_IV_MASK; break; } + case MON_DATA_NATURE: + { + u32 pid = boxMon->personality; + u32 otId = boxMon->otId; + s8 diff = (data[0] % 25) - (pid % 25); // difference between new nature and current nature, [-24,24] + bool8 preserveShiny = FALSE; + bool8 preserveLetter = FALSE; + u16 shinyValue = HIHALF(pid) ^ LOHALF(pid); + s32 tweak; + u32 pidTemp; + // See https://bulbapedia.bulbagarden.net/wiki/Personality_value#Nature + // Goal here is to preserve as much of the PID as possible + // To preserve gender & substruct order, we add/subtract multiples of 5376 that is 0 % 256, 0 % 24, 1 % 25 + // i.e, to increase the nature by n % 25, we add n*5376 % 19200 (LCM of 24, 25, 256) to the pid + // Ability number is determined by parity and so adding multiples of 5376 preserves it + // TODO: For genderless pokemon, 576/600 can be used instead of 5376/19200 + if (diff == 0) // No change + break; + else if (diff < 0) + diff = 25+diff; + tweak = (diff*5376) % 19200; + pidTemp = pid + tweak; + // If the pokemon is shiny or if changing the PID would make it shiny, preserve its shiny value + if (IsShinyOtIdPersonality(otId, pid) || IsShinyOtIdPersonality(otId, pidTemp)) + preserveShiny = TRUE; + if (substruct0->species == SPECIES_UNOWN) // Preserve Unown letter + preserveLetter = TRUE; + if (preserveShiny && preserveLetter) { // honestly though, how many shiny Unown are out there ? + while (pidTemp > pid) { + if ((HIHALF(pidTemp) ^ LOHALF(pidTemp) ^ shinyValue) < SHINY_ODDS) + if (GET_UNOWN_LETTER(pidTemp) == GET_UNOWN_LETTER(pid)) + break; + pidTemp += 19200; + } + } else if (preserveShiny) { + while (pidTemp > pid) { + if ((HIHALF(pidTemp) ^ LOHALF(pidTemp) ^ shinyValue) < SHINY_ODDS) + break; + pidTemp += 19200; + } + } else if (preserveLetter) { + while (pidTemp > pid) { + if (GET_UNOWN_LETTER(pidTemp) == GET_UNOWN_LETTER(pid)) + break; + pidTemp += 19200; + } + } + if (pidTemp < pid) { // overflow; search backwards + tweak -= 19200; + pidTemp = pid + tweak; + if (preserveShiny && preserveLetter) { + while (pidTemp < pid) { + if ((HIHALF(pidTemp) ^ LOHALF(pidTemp) ^ shinyValue) < SHINY_ODDS) + if (GET_UNOWN_LETTER(pidTemp) == GET_UNOWN_LETTER(pid)) + break; + pidTemp -= 19200; + } + } else if (preserveShiny) { + while (pidTemp < pid) { + if ((HIHALF(pidTemp) ^ LOHALF(pidTemp) ^ shinyValue) < SHINY_ODDS) + break; + pidTemp -= 19200; + } + } else if (preserveLetter) { + while (pidTemp < pid) { + if (GET_UNOWN_LETTER(pidTemp) == GET_UNOWN_LETTER(pid)) + break; + pidTemp -= 19200; + } + } + } + if (pid % 24 == pidTemp % 24 || pid % 256 == pidTemp % 256) + boxMon->personality = pidTemp; + break; + } default: break; } @@ -4878,7 +4960,7 @@ bool8 PokemonUseItemEffects(struct Pokemon *mon, u16 item, u8 partyIndex, u8 mov temp2 = itemEffect[itemEffectParam]; dataSigned = GetMonData(mon, sGetMonDataEVConstants[temp1], NULL); evChange = temp2; - + if (evChange > 0) // Increasing EV (HP or Atk) { // Has EV increase limit already been reached? @@ -4976,7 +5058,7 @@ bool8 PokemonUseItemEffects(struct Pokemon *mon, u16 item, u8 partyIndex, u8 mov { if (!usedByAI) { - // Restore HP + // Restore HP dataUnsigned = GetMonData(mon, MON_DATA_HP, NULL) + dataUnsigned; if (dataUnsigned > GetMonData(mon, MON_DATA_MAX_HP, NULL)) dataUnsigned = GetMonData(mon, MON_DATA_MAX_HP, NULL); @@ -5055,7 +5137,7 @@ bool8 PokemonUseItemEffects(struct Pokemon *mon, u16 item, u8 partyIndex, u8 mov dataUnsigned = CalculatePPWithBonus(moveId, GetMonData(mon, MON_DATA_PP_BONUSES, NULL), moveIndex); } SetMonData(mon, MON_DATA_PP1 + moveIndex, &dataUnsigned); - + // Heal battler PP too (if applicable) if (gMain.inBattle && battlerId != MAX_BATTLERS_COUNT && !(gBattleMons[battlerId].status2 & STATUS2_TRANSFORMED) @@ -5092,7 +5174,7 @@ bool8 PokemonUseItemEffects(struct Pokemon *mon, u16 item, u8 partyIndex, u8 mov case 5: effectFlags = itemEffect[i]; temp1 = 0; - + // Loop through and try each of the ITEM5 effects while (effectFlags != 0) {