Merge pull request #1328 from GriffinRichards/doc-pokeuseitem

Document PokemonUseItemEffects
This commit is contained in:
GriffinR
2021-02-22 12:15:19 -05:00
committed by GitHub
8 changed files with 473 additions and 442 deletions

View File

@@ -4640,29 +4640,56 @@ void CopyPlayerPartyMonToBattleData(u8 battlerId, u8 partyIndex)
bool8 ExecuteTableBasedItemEffect(struct Pokemon *mon, u16 item, u8 partyIndex, u8 moveIndex)
{
return PokemonUseItemEffects(mon, item, partyIndex, moveIndex, 0);
return PokemonUseItemEffects(mon, item, partyIndex, moveIndex, FALSE);
}
bool8 PokemonUseItemEffects(struct Pokemon *mon, u16 item, u8 partyIndex, u8 moveIndex, u8 e)
#define UPDATE_FRIENDSHIP_FROM_ITEM \
{ \
if ((retVal == 0 || friendshipOnly) && !ShouldSkipFriendshipChange() && friendshipChange == 0) \
{ \
friendshipChange = itemEffect[itemEffectParam]; \
friendship = GetMonData(mon, MON_DATA_FRIENDSHIP, NULL); \
if (friendshipChange > 0 && holdEffect == HOLD_EFFECT_HAPPINESS_UP) \
friendship += 150 * friendshipChange / 100; \
else \
friendship += friendshipChange; \
if (friendshipChange > 0) \
{ \
if (GetMonData(mon, MON_DATA_POKEBALL, NULL) == ITEM_LUXURY_BALL) \
friendship++; \
if (GetMonData(mon, MON_DATA_MET_LOCATION, NULL) == GetCurrentRegionMapSectionId()) \
friendship++; \
} \
if (friendship < 0) \
friendship = 0; \
if (friendship > MAX_FRIENDSHIP) \
friendship = MAX_FRIENDSHIP; \
SetMonData(mon, MON_DATA_FRIENDSHIP, &friendship); \
retVal = FALSE; \
} \
}
// Returns TRUE if the item has no effect on the Pokémon, FALSE otherwise
bool8 PokemonUseItemEffects(struct Pokemon *mon, u16 item, u8 partyIndex, u8 moveIndex, bool8 usedByAI)
{
u32 dataUnsigned;
s32 dataSigned;
s32 friendship;
s32 cmdIndex;
s32 i;
bool8 retVal = TRUE;
const u8 *itemEffect;
u8 var_3C = 6;
u32 var_38;
s8 var_34 = 0;
u8 itemEffectParam = ITEM_EFFECT_ARG_START;
u32 temp1, temp2;
s8 friendshipChange = 0;
u8 holdEffect;
u8 battlerId = 4;
u32 var_28 = 0;
u8 battlerId = MAX_BATTLERS_COUNT;
u32 friendshipOnly = FALSE;
u16 heldItem;
u8 r10;
u32 r5;
s8 r2;
u8 effectFlags;
s8 evChange;
u16 evCount;
// Get item hold effect
heldItem = GetMonData(mon, MON_DATA_HELD_ITEM, NULL);
if (heldItem == ITEM_ENIGMA_BERRY)
{
@@ -4676,19 +4703,20 @@ bool8 PokemonUseItemEffects(struct Pokemon *mon, u16 item, u8 partyIndex, u8 mov
holdEffect = ItemId_GetHoldEffect(heldItem);
}
// Get battler id (if relevant)
gPotentialItemEffectBattler = gBattlerInMenuId;
if (gMain.inBattle)
{
gActiveBattler = gBattlerInMenuId;
cmdIndex = (GetBattlerSide(gActiveBattler) != B_SIDE_PLAYER);
while (cmdIndex < gBattlersCount)
i = (GetBattlerSide(gActiveBattler) != B_SIDE_PLAYER);
while (i < gBattlersCount)
{
if (gBattlerPartyIndexes[cmdIndex] == partyIndex)
if (gBattlerPartyIndexes[i] == partyIndex)
{
battlerId = cmdIndex;
battlerId = i;
break;
}
cmdIndex += 2;
i += 2;
}
}
else
@@ -4697,11 +4725,13 @@ bool8 PokemonUseItemEffects(struct Pokemon *mon, u16 item, u8 partyIndex, u8 mov
battlerId = MAX_BATTLERS_COUNT;
}
// Skip using the item if it won't do anything
if (!ITEM_HAS_EFFECT(item))
return TRUE;
if (gItemEffectTable[item - ITEM_POTION] == NULL && item != ITEM_ENIGMA_BERRY)
return TRUE;
// Get item effect
if (item == ITEM_ENIGMA_BERRY)
{
if (gMain.inBattle)
@@ -4714,79 +4744,98 @@ bool8 PokemonUseItemEffects(struct Pokemon *mon, u16 item, u8 partyIndex, u8 mov
itemEffect = gItemEffectTable[item - ITEM_POTION];
}
for (cmdIndex = 0; cmdIndex < 6; cmdIndex++)
// Do item effect
for (i = 0; i < ITEM_EFFECT_ARG_START; i++)
{
switch (cmdIndex)
switch (i)
{
// infatuation heal, x attack, sacred ash and dire hit
// Handle ITEM0 effects (infatuation, Dire Hit, X Attack). ITEM0_SACRED_ASH is handled in party_menu.c
case 0:
if ((itemEffect[cmdIndex] & ITEM0_INFATUATION)
// Cure infatuation
if ((itemEffect[i] & ITEM0_INFATUATION)
&& gMain.inBattle && battlerId != MAX_BATTLERS_COUNT && (gBattleMons[battlerId].status2 & STATUS2_INFATUATION))
{
gBattleMons[battlerId].status2 &= ~STATUS2_INFATUATION;
retVal = FALSE;
}
if ((itemEffect[cmdIndex] & ITEM0_DIRE_HIT)
// Dire Hit
if ((itemEffect[i] & ITEM0_DIRE_HIT)
&& !(gBattleMons[gActiveBattler].status2 & STATUS2_FOCUS_ENERGY))
{
gBattleMons[gActiveBattler].status2 |= STATUS2_FOCUS_ENERGY;
retVal = FALSE;
}
if ((itemEffect[cmdIndex] & ITEM0_X_ATTACK)
// X Attack
if ((itemEffect[i] & ITEM0_X_ATTACK)
&& gBattleMons[gActiveBattler].statStages[STAT_ATK] < MAX_STAT_STAGE)
{
gBattleMons[gActiveBattler].statStages[STAT_ATK] += itemEffect[cmdIndex] & ITEM0_X_ATTACK;
gBattleMons[gActiveBattler].statStages[STAT_ATK] += itemEffect[i] & ITEM0_X_ATTACK;
if (gBattleMons[gActiveBattler].statStages[STAT_ATK] > MAX_STAT_STAGE)
gBattleMons[gActiveBattler].statStages[STAT_ATK] = MAX_STAT_STAGE;
retVal = FALSE;
}
break;
// in-battle stat boosting effects
// Handle ITEM1 effects (in-battle stat boosting effects)
case 1:
if ((itemEffect[cmdIndex] & ITEM1_X_DEFEND)
// X Defend
if ((itemEffect[i] & ITEM1_X_DEFEND)
&& gBattleMons[gActiveBattler].statStages[STAT_DEF] < MAX_STAT_STAGE)
{
gBattleMons[gActiveBattler].statStages[STAT_DEF] += (itemEffect[cmdIndex] & ITEM1_X_DEFEND) >> 4;
gBattleMons[gActiveBattler].statStages[STAT_DEF] += (itemEffect[i] & ITEM1_X_DEFEND) >> 4;
if (gBattleMons[gActiveBattler].statStages[STAT_DEF] > MAX_STAT_STAGE)
gBattleMons[gActiveBattler].statStages[STAT_DEF] = MAX_STAT_STAGE;
retVal = FALSE;
}
if ((itemEffect[cmdIndex] & ITEM1_X_SPEED)
// X Speed
if ((itemEffect[i] & ITEM1_X_SPEED)
&& gBattleMons[gActiveBattler].statStages[STAT_SPEED] < MAX_STAT_STAGE)
{
gBattleMons[gActiveBattler].statStages[STAT_SPEED] += itemEffect[cmdIndex] & ITEM1_X_SPEED;
gBattleMons[gActiveBattler].statStages[STAT_SPEED] += itemEffect[i] & ITEM1_X_SPEED;
if (gBattleMons[gActiveBattler].statStages[STAT_SPEED] > MAX_STAT_STAGE)
gBattleMons[gActiveBattler].statStages[STAT_SPEED] = MAX_STAT_STAGE;
retVal = FALSE;
}
break;
// more stat boosting effects
// Handle ITEM2 effects (more stat boosting effects)
case 2:
if ((itemEffect[cmdIndex] & ITEM2_X_ACCURACY)
// X Accuracy
if ((itemEffect[i] & ITEM2_X_ACCURACY)
&& gBattleMons[gActiveBattler].statStages[STAT_ACC] < MAX_STAT_STAGE)
{
gBattleMons[gActiveBattler].statStages[STAT_ACC] += (itemEffect[cmdIndex] & ITEM2_X_ACCURACY) >> 4;
gBattleMons[gActiveBattler].statStages[STAT_ACC] += (itemEffect[i] & ITEM2_X_ACCURACY) >> 4;
if (gBattleMons[gActiveBattler].statStages[STAT_ACC] > MAX_STAT_STAGE)
gBattleMons[gActiveBattler].statStages[STAT_ACC] = MAX_STAT_STAGE;
retVal = FALSE;
}
if ((itemEffect[cmdIndex] & ITEM2_X_SPATK)
// X Sp Attack
if ((itemEffect[i] & ITEM2_X_SPATK)
&& gBattleMons[gActiveBattler].statStages[STAT_SPATK] < MAX_STAT_STAGE)
{
gBattleMons[gActiveBattler].statStages[STAT_SPATK] += itemEffect[cmdIndex] & ITEM2_X_SPATK;
gBattleMons[gActiveBattler].statStages[STAT_SPATK] += itemEffect[i] & ITEM2_X_SPATK;
if (gBattleMons[gActiveBattler].statStages[STAT_SPATK] > MAX_STAT_STAGE)
gBattleMons[gActiveBattler].statStages[STAT_SPATK] = MAX_STAT_STAGE;
retVal = FALSE;
}
break;
// Handle ITEM3 effects (Guard Spec, Rare Candy, cure status)
case 3:
if ((itemEffect[cmdIndex] & ITEM3_GUARD_SPEC)
// Guard Spec
if ((itemEffect[i] & ITEM3_GUARD_SPEC)
&& gSideTimers[GetBattlerSide(gActiveBattler)].mistTimer == 0)
{
gSideTimers[GetBattlerSide(gActiveBattler)].mistTimer = 5;
retVal = FALSE;
}
if ((itemEffect[cmdIndex] & ITEM3_LEVEL_UP)
// Rare Candy
if ((itemEffect[i] & ITEM3_LEVEL_UP)
&& GetMonData(mon, MON_DATA_LEVEL, NULL) != MAX_LEVEL)
{
dataUnsigned = gExperienceTables[gBaseStats[GetMonData(mon, MON_DATA_SPECIES, NULL)].growthRate][GetMonData(mon, MON_DATA_LEVEL, NULL) + 1];
@@ -4794,106 +4843,120 @@ bool8 PokemonUseItemEffects(struct Pokemon *mon, u16 item, u8 partyIndex, u8 mov
CalculateMonStats(mon);
retVal = FALSE;
}
if ((itemEffect[cmdIndex] & ITEM3_SLEEP)
&& HealStatusConditions(mon, partyIndex, 7, battlerId) == 0)
// Cure status
if ((itemEffect[i] & ITEM3_SLEEP)
&& HealStatusConditions(mon, partyIndex, STATUS1_SLEEP, battlerId) == 0)
{
if (battlerId != 4)
if (battlerId != MAX_BATTLERS_COUNT)
gBattleMons[battlerId].status2 &= ~STATUS2_NIGHTMARE;
retVal = FALSE;
}
if ((itemEffect[cmdIndex] & ITEM3_POISON) && HealStatusConditions(mon, partyIndex, STATUS1_PSN_ANY | STATUS1_TOXIC_COUNTER, battlerId) == 0)
if ((itemEffect[i] & ITEM3_POISON) && HealStatusConditions(mon, partyIndex, STATUS1_PSN_ANY | STATUS1_TOXIC_COUNTER, battlerId) == 0)
retVal = FALSE;
if ((itemEffect[cmdIndex] & ITEM3_BURN) && HealStatusConditions(mon, partyIndex, STATUS1_BURN, battlerId) == 0)
if ((itemEffect[i] & ITEM3_BURN) && HealStatusConditions(mon, partyIndex, STATUS1_BURN, battlerId) == 0)
retVal = FALSE;
if ((itemEffect[cmdIndex] & ITEM3_FREEZE) && HealStatusConditions(mon, partyIndex, STATUS1_FREEZE, battlerId) == 0)
if ((itemEffect[i] & ITEM3_FREEZE) && HealStatusConditions(mon, partyIndex, STATUS1_FREEZE, battlerId) == 0)
retVal = FALSE;
if ((itemEffect[cmdIndex] & ITEM3_PARALYSIS) && HealStatusConditions(mon, partyIndex, STATUS1_PARALYSIS, battlerId) == 0)
if ((itemEffect[i] & ITEM3_PARALYSIS) && HealStatusConditions(mon, partyIndex, STATUS1_PARALYSIS, battlerId) == 0)
retVal = FALSE;
if ((itemEffect[cmdIndex] & ITEM3_CONFUSION) // heal confusion
if ((itemEffect[i] & ITEM3_CONFUSION) // heal confusion
&& gMain.inBattle && battlerId != MAX_BATTLERS_COUNT && (gBattleMons[battlerId].status2 & STATUS2_CONFUSION))
{
gBattleMons[battlerId].status2 &= ~STATUS2_CONFUSION;
retVal = FALSE;
}
break;
// EV, HP, and PP raising effects
// Handle ITEM4 effects (Change HP/Atk EVs, HP heal, PP heal, PP up, Revive, and evolution stones)
case 4:
r10 = itemEffect[cmdIndex];
if (r10 & ITEM4_PP_UP)
effectFlags = itemEffect[i];
// PP Up
if (effectFlags & ITEM4_PP_UP)
{
r10 &= ~ITEM4_PP_UP;
effectFlags &= ~ITEM4_PP_UP;
dataUnsigned = (GetMonData(mon, MON_DATA_PP_BONUSES, NULL) & gPPUpGetMask[moveIndex]) >> (moveIndex * 2);
var_38 = CalculatePPWithBonus(GetMonData(mon, MON_DATA_MOVE1 + moveIndex, NULL), GetMonData(mon, MON_DATA_PP_BONUSES, NULL), moveIndex);
if (dataUnsigned <= 2 && var_38 > 4)
temp1 = CalculatePPWithBonus(GetMonData(mon, MON_DATA_MOVE1 + moveIndex, NULL), GetMonData(mon, MON_DATA_PP_BONUSES, NULL), moveIndex);
if (dataUnsigned <= 2 && temp1 > 4)
{
dataUnsigned = GetMonData(mon, MON_DATA_PP_BONUSES, NULL) + gPPUpAddMask[moveIndex];
SetMonData(mon, MON_DATA_PP_BONUSES, &dataUnsigned);
dataUnsigned = CalculatePPWithBonus(GetMonData(mon, MON_DATA_MOVE1 + moveIndex, NULL), dataUnsigned, moveIndex) - var_38;
dataUnsigned = CalculatePPWithBonus(GetMonData(mon, MON_DATA_MOVE1 + moveIndex, NULL), dataUnsigned, moveIndex) - temp1;
dataUnsigned = GetMonData(mon, MON_DATA_PP1 + moveIndex, NULL) + dataUnsigned;
SetMonData(mon, MON_DATA_PP1 + moveIndex, &dataUnsigned);
retVal = FALSE;
}
}
var_38 = 0;
while (r10 != 0)
temp1 = 0;
// Loop through and try each of the remaining ITEM4 effects
while (effectFlags != 0)
{
if (r10 & 1)
if (effectFlags & 1)
{
switch (var_38)
switch (temp1)
{
case 0:
case 1:
// ev raise
case 0: // ITEM4_EV_HP
case 1: // ITEM4_EV_ATK
evCount = GetMonEVCount(mon);
r5 = itemEffect[var_3C];
dataSigned = GetMonData(mon, sGetMonDataEVConstants[var_38], NULL);
r2 = r5;
if (r2 > 0)
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?
if (evCount >= MAX_TOTAL_EVS)
return TRUE;
if (dataSigned >= EV_ITEM_RAISE_LIMIT)
break;
if (dataSigned + r2 > EV_ITEM_RAISE_LIMIT)
r5 = EV_ITEM_RAISE_LIMIT - (dataSigned + r2) + r2;
// Limit the increase
if (dataSigned + evChange > EV_ITEM_RAISE_LIMIT)
temp2 = EV_ITEM_RAISE_LIMIT - (dataSigned + evChange) + evChange;
else
r5 = r2;
temp2 = evChange;
if (evCount + r5 > MAX_TOTAL_EVS)
r5 += MAX_TOTAL_EVS - (evCount + r5);
dataSigned += r5;
if (evCount + temp2 > MAX_TOTAL_EVS)
temp2 += MAX_TOTAL_EVS - (evCount + temp2);
dataSigned += temp2;
}
else
else // Decreasing EV (HP or Atk)
{
if (dataSigned == 0)
{
var_28 = 1;
var_3C++;
// No EVs to lose, but make sure friendship updates anyway
friendshipOnly = TRUE;
itemEffectParam++;
break;
}
dataSigned += r2;
dataSigned += evChange;
if (dataSigned < 0)
dataSigned = 0;
}
SetMonData(mon, sGetMonDataEVConstants[var_38], &dataSigned);
// Update EVs and stats
SetMonData(mon, sGetMonDataEVConstants[temp1], &dataSigned);
CalculateMonStats(mon);
var_3C++;
itemEffectParam++;
retVal = FALSE;
break;
case 2:
// revive
if (r10 & 0x10)
case 2: // ITEM4_HEAL_HP
// If Revive, update number of times revive has been used
if (effectFlags & (ITEM4_REVIVE >> 2))
{
if (GetMonData(mon, MON_DATA_HP, NULL) != 0)
{
var_3C++;
itemEffectParam++;
break;
}
if (gMain.inBattle)
{
if (battlerId != 4)
if (battlerId != MAX_BATTLERS_COUNT)
{
gAbsentBattlerFlags &= ~gBitTable[battlerId];
CopyPlayerPartyMonToBattleData(battlerId, GetPartyIdFromBattlePartyId(gBattlerPartyIndexes[battlerId]));
@@ -4912,48 +4975,53 @@ bool8 PokemonUseItemEffects(struct Pokemon *mon, u16 item, u8 partyIndex, u8 mov
{
if (GetMonData(mon, MON_DATA_HP, NULL) == 0)
{
var_3C++;
itemEffectParam++;
break;
}
}
// Get amount of HP to restore
dataUnsigned = itemEffect[var_3C++];
dataUnsigned = itemEffect[itemEffectParam++];
switch (dataUnsigned)
{
case ITEM6_HEAL_FULL:
case ITEM6_HEAL_HP_FULL:
dataUnsigned = GetMonData(mon, MON_DATA_MAX_HP, NULL) - GetMonData(mon, MON_DATA_HP, NULL);
break;
case ITEM6_HEAL_HALF:
case ITEM6_HEAL_HP_HALF:
dataUnsigned = GetMonData(mon, MON_DATA_MAX_HP, NULL) / 2;
if (dataUnsigned == 0)
dataUnsigned = 1;
break;
case ITEM6_HEAL_LVL_UP:
case ITEM6_HEAL_HP_LVL_UP:
dataUnsigned = gBattleScripting.levelUpHP;
break;
}
// Only restore HP if not at max health
if (GetMonData(mon, MON_DATA_MAX_HP, NULL) != GetMonData(mon, MON_DATA_HP, NULL))
{
if (e == 0)
if (!usedByAI)
{
// 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);
SetMonData(mon, MON_DATA_HP, &dataUnsigned);
if (gMain.inBattle && battlerId != 4)
// Update battler (if applicable)
if (gMain.inBattle && battlerId != MAX_BATTLERS_COUNT)
{
gBattleMons[battlerId].hp = dataUnsigned;
if (!(r10 & 0x10) && GetBattlerSide(gActiveBattler) == B_SIDE_PLAYER)
if (!(effectFlags & (ITEM4_REVIVE >> 2)) && GetBattlerSide(gActiveBattler) == B_SIDE_PLAYER)
{
if (gBattleResults.numHealingItemsUsed < 255)
gBattleResults.numHealingItemsUsed++;
// I have to re-use this variable to match.
r5 = gActiveBattler;
temp2 = gActiveBattler;
gActiveBattler = battlerId;
BtlController_EmitGetMonData(0, REQUEST_ALL_BATTLE, 0);
MarkBattlerForControllerExec(gActiveBattler);
gActiveBattler = r5;
gActiveBattler = temp2;
}
}
}
@@ -4963,227 +5031,197 @@ bool8 PokemonUseItemEffects(struct Pokemon *mon, u16 item, u8 partyIndex, u8 mov
}
retVal = FALSE;
}
r10 &= 0xEF;
effectFlags &= ~(ITEM4_REVIVE >> 2);
break;
case 3:
// Heal pp in all moves.
if (!(r10 & 2))
case 3: // ITEM4_HEAL_PP
if (!(effectFlags & (ITEM4_HEAL_PP_ONE >> 3)))
{
for (r5 = 0; (signed)(r5) < (signed)(4); r5++)
// Heal PP for all moves
for (temp2 = 0; (signed)(temp2) < (signed)(MAX_MON_MOVES); temp2++)
{
u16 moveId;
dataUnsigned = GetMonData(mon, MON_DATA_PP1 + r5, NULL);
moveId = GetMonData(mon, MON_DATA_MOVE1 + r5, NULL);
if (dataUnsigned != CalculatePPWithBonus(moveId, GetMonData(mon, MON_DATA_PP_BONUSES, NULL), r5))
dataUnsigned = GetMonData(mon, MON_DATA_PP1 + temp2, NULL);
moveId = GetMonData(mon, MON_DATA_MOVE1 + temp2, NULL);
if (dataUnsigned != CalculatePPWithBonus(moveId, GetMonData(mon, MON_DATA_PP_BONUSES, NULL), temp2))
{
dataUnsigned += itemEffect[var_3C];
moveId = GetMonData(mon, MON_DATA_MOVE1 + r5, NULL);
if (dataUnsigned > CalculatePPWithBonus(moveId, GetMonData(mon, MON_DATA_PP_BONUSES, NULL), r5))
dataUnsigned += itemEffect[itemEffectParam];
moveId = GetMonData(mon, MON_DATA_MOVE1 + temp2, NULL); // Redundant
if (dataUnsigned > CalculatePPWithBonus(moveId, GetMonData(mon, MON_DATA_PP_BONUSES, NULL), temp2))
{
moveId = GetMonData(mon, MON_DATA_MOVE1 + r5, NULL);
dataUnsigned = CalculatePPWithBonus(moveId, GetMonData(mon, MON_DATA_PP_BONUSES, NULL), r5);
moveId = GetMonData(mon, MON_DATA_MOVE1 + temp2, NULL); // Redundant
dataUnsigned = CalculatePPWithBonus(moveId, GetMonData(mon, MON_DATA_PP_BONUSES, NULL), temp2);
}
SetMonData(mon, MON_DATA_PP1 + r5, &dataUnsigned);
SetMonData(mon, MON_DATA_PP1 + temp2, &dataUnsigned);
// Heal battler PP too (if applicable)
if (gMain.inBattle
&& battlerId != MAX_BATTLERS_COUNT && !(gBattleMons[battlerId].status2 & STATUS2_TRANSFORMED)
&& !(gDisableStructs[battlerId].mimickedMoves & gBitTable[r5]))
gBattleMons[battlerId].pp[r5] = dataUnsigned;
&& !(gDisableStructs[battlerId].mimickedMoves & gBitTable[temp2]))
gBattleMons[battlerId].pp[temp2] = dataUnsigned;
retVal = FALSE;
}
}
var_3C++;
itemEffectParam++;
}
// Heal pp in one move.
else
{
// Heal PP for one move
u16 moveId;
dataUnsigned = GetMonData(mon, MON_DATA_PP1 + moveIndex, NULL);
moveId = GetMonData(mon, MON_DATA_MOVE1 + moveIndex, NULL);
if (dataUnsigned != CalculatePPWithBonus(moveId, GetMonData(mon, MON_DATA_PP_BONUSES, NULL), moveIndex))
{
dataUnsigned += itemEffect[var_3C++];
moveId = GetMonData(mon, MON_DATA_MOVE1 + moveIndex, NULL);
dataUnsigned += itemEffect[itemEffectParam++];
moveId = GetMonData(mon, MON_DATA_MOVE1 + moveIndex, NULL); // Redundant
if (dataUnsigned > CalculatePPWithBonus(moveId, GetMonData(mon, MON_DATA_PP_BONUSES, NULL), moveIndex))
{
moveId = GetMonData(mon, MON_DATA_MOVE1 + moveIndex, NULL);
moveId = GetMonData(mon, MON_DATA_MOVE1 + moveIndex, NULL); // Redundant
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 != 4 && !(gBattleMons[battlerId].status2 & STATUS2_TRANSFORMED)
&& battlerId != MAX_BATTLERS_COUNT && !(gBattleMons[battlerId].status2 & STATUS2_TRANSFORMED)
&& !(gDisableStructs[battlerId].mimickedMoves & gBitTable[moveIndex]))
gBattleMons[battlerId].pp[moveIndex] = dataUnsigned;
retVal = FALSE;
}
}
break;
// Evolution stone
case 7:
// cases 4-6 are ITEM4_HEAL_PP_ONE, ITEM4_PP_UP, and ITEM4_REVIVE, which
// are already handled above by other cases or before the loop
case 7: // ITEM4_EVO_STONE
{
u16 targetSpecies = GetEvolutionTargetSpecies(mon, 2, item);
u16 targetSpecies = GetEvolutionTargetSpecies(mon, EVO_MODE_ITEM_USE, item);
if (targetSpecies != SPECIES_NONE)
{
BeginEvolutionScene(mon, targetSpecies, 0, partyIndex);
BeginEvolutionScene(mon, targetSpecies, FALSE, partyIndex);
return FALSE;
}
}
break;
}
}
var_38++;
r10 >>= 1;
temp1++;
effectFlags >>= 1;
}
break;
// EV and friendship
// Handle ITEM5 effects (Change Def/SpDef/SpAtk/Speed EVs, PP Max, and friendship changes)
case 5:
r10 = itemEffect[cmdIndex];
var_38 = 0;
while (r10 != 0)
effectFlags = itemEffect[i];
temp1 = 0;
// Loop through and try each of the ITEM5 effects
while (effectFlags != 0)
{
if (r10 & 1)
if (effectFlags & 1)
{
switch (var_38)
switch (temp1)
{
case 0:
case 1:
case 2:
case 3:
case 0: // ITEM5_EV_DEF
case 1: // ITEM5_EV_SPEED
case 2: // ITEM5_EV_SPDEF
case 3: // ITEM5_EV_SPATK
evCount = GetMonEVCount(mon);
r5 = itemEffect[var_3C];
dataSigned = GetMonData(mon, sGetMonDataEVConstants[var_38 + 2], NULL);
r2 = r5;
if (r2 > 0)
temp2 = itemEffect[itemEffectParam];
dataSigned = GetMonData(mon, sGetMonDataEVConstants[temp1 + 2], NULL);
evChange = temp2;
if (evChange > 0) // Increasing EV
{
// Has EV increase limit already been reached?
if (evCount >= MAX_TOTAL_EVS)
return TRUE;
if (dataSigned >= EV_ITEM_RAISE_LIMIT)
break;
if (dataSigned + r2 > EV_ITEM_RAISE_LIMIT)
r5 = EV_ITEM_RAISE_LIMIT - (dataSigned + r2) + r2;
// Limit the increase
if (dataSigned + evChange > EV_ITEM_RAISE_LIMIT)
temp2 = EV_ITEM_RAISE_LIMIT - (dataSigned + evChange) + evChange;
else
r5 = r2;
temp2 = evChange;
if (evCount + r5 > MAX_TOTAL_EVS)
r5 += MAX_TOTAL_EVS - (evCount + r5);
dataSigned += r5;
if (evCount + temp2 > MAX_TOTAL_EVS)
temp2 += MAX_TOTAL_EVS - (evCount + temp2);
dataSigned += temp2;
}
else
else // Decreasing EV
{
if (dataSigned == 0)
{
var_28 = 1;
var_3C++;
// No EVs to lose, but make sure friendship updates anyway
friendshipOnly = TRUE;
itemEffectParam++;
break;
}
dataSigned += r2;
dataSigned += evChange;
if (dataSigned < 0)
dataSigned = 0;
}
SetMonData(mon, sGetMonDataEVConstants[var_38 + 2], &dataSigned);
// Update EVs and stats
SetMonData(mon, sGetMonDataEVConstants[temp1 + 2], &dataSigned);
CalculateMonStats(mon);
retVal = FALSE;
var_3C++;
itemEffectParam++;
break;
case 4:
case 4: // ITEM5_PP_MAX
dataUnsigned = (GetMonData(mon, MON_DATA_PP_BONUSES, NULL) & gPPUpGetMask[moveIndex]) >> (moveIndex * 2);
r5 = CalculatePPWithBonus(GetMonData(mon, MON_DATA_MOVE1 + moveIndex, NULL), GetMonData(mon, MON_DATA_PP_BONUSES, NULL), moveIndex);
if (dataUnsigned < 3 && r5 > 4)
temp2 = CalculatePPWithBonus(GetMonData(mon, MON_DATA_MOVE1 + moveIndex, NULL), GetMonData(mon, MON_DATA_PP_BONUSES, NULL), moveIndex);
if (dataUnsigned < 3 && temp2 > 4)
{
dataUnsigned = GetMonData(mon, MON_DATA_PP_BONUSES, NULL);
dataUnsigned &= gPPUpSetMask[moveIndex];
dataUnsigned += gPPUpAddMask[moveIndex] * 3;
SetMonData(mon, MON_DATA_PP_BONUSES, &dataUnsigned);
dataUnsigned = CalculatePPWithBonus(GetMonData(mon, MON_DATA_MOVE1 + moveIndex, NULL), dataUnsigned, moveIndex) - r5;
dataUnsigned = CalculatePPWithBonus(GetMonData(mon, MON_DATA_MOVE1 + moveIndex, NULL), dataUnsigned, moveIndex) - temp2;
dataUnsigned = GetMonData(mon, MON_DATA_PP1 + moveIndex, NULL) + dataUnsigned;
SetMonData(mon, MON_DATA_PP1 + moveIndex, &dataUnsigned);
retVal = FALSE;
}
break;
case 5:
if (GetMonData(mon, MON_DATA_FRIENDSHIP, NULL) < 100 && (retVal == 0 || var_28 != 0) && !ShouldSkipFriendshipChange() && var_34 == 0)
case 5: // ITEM5_FRIENDSHIP_LOW
// Changes to friendship are given differently depending on
// how much friendship the Pokémon already has.
// In general, Pokémon with lower friendship receive more,
// and Pokémon with higher friendship receive less.
if (GetMonData(mon, MON_DATA_FRIENDSHIP, NULL) < 100)
{
var_34 = itemEffect[var_3C];
friendship = GetMonData(mon, MON_DATA_FRIENDSHIP, NULL);
if (var_34 > 0 && holdEffect == HOLD_EFFECT_HAPPINESS_UP)
friendship += 150 * var_34 / 100;
else
friendship += var_34;
if (var_34 > 0)
{
if (GetMonData(mon, MON_DATA_POKEBALL, NULL) == ITEM_LUXURY_BALL)
friendship++;
if (GetMonData(mon, MON_DATA_MET_LOCATION, NULL) == GetCurrentRegionMapSectionId())
friendship++;
}
if (friendship < 0)
friendship = 0;
if (friendship > MAX_FRIENDSHIP)
friendship = MAX_FRIENDSHIP;
SetMonData(mon, MON_DATA_FRIENDSHIP, &friendship);
retVal = FALSE;
UPDATE_FRIENDSHIP_FROM_ITEM;
}
var_3C++;
itemEffectParam++;
break;
case 6:
if (GetMonData(mon, MON_DATA_FRIENDSHIP, NULL) >= 100 && GetMonData(mon, MON_DATA_FRIENDSHIP, NULL) < 200
&& (retVal == 0 || var_28 != 0) && !ShouldSkipFriendshipChange() && var_34 == 0)
case 6: // ITEM5_FRIENDSHIP_MID
if (GetMonData(mon, MON_DATA_FRIENDSHIP, NULL) >= 100 && GetMonData(mon, MON_DATA_FRIENDSHIP, NULL) < 200)
{
var_34 = itemEffect[var_3C];
friendship = GetMonData(mon, MON_DATA_FRIENDSHIP, NULL);
if ((s8)(var_34) > 0 && holdEffect == HOLD_EFFECT_HAPPINESS_UP)
friendship += 150 * var_34 / 100;
else
friendship += var_34;
if (var_34 > 0)
{
if (GetMonData(mon, MON_DATA_POKEBALL, NULL) == ITEM_LUXURY_BALL)
friendship++;
if (GetMonData(mon, MON_DATA_MET_LOCATION, NULL) == GetCurrentRegionMapSectionId())
friendship++;
}
if (friendship < 0)
friendship = 0;
if (friendship > MAX_FRIENDSHIP)
friendship = MAX_FRIENDSHIP;
SetMonData(mon, MON_DATA_FRIENDSHIP, &friendship);
retVal = FALSE;
UPDATE_FRIENDSHIP_FROM_ITEM;
}
var_3C++;
itemEffectParam++;
break;
case 7:
if (GetMonData(mon, MON_DATA_FRIENDSHIP, NULL) >= 200 && (retVal == 0 || var_28 != 0) && !ShouldSkipFriendshipChange() && var_34 == 0)
case 7: // ITEM5_FRIENDSHIP_HIGH
if (GetMonData(mon, MON_DATA_FRIENDSHIP, NULL) >= 200)
{
var_34 = itemEffect[var_3C];
friendship = GetMonData(mon, MON_DATA_FRIENDSHIP, NULL);
if ((s8)(var_34) > 0 && holdEffect == HOLD_EFFECT_HAPPINESS_UP)
friendship += 150 * var_34 / 100;
else
friendship += var_34;
if (var_34 > 0)
{
if (GetMonData(mon, MON_DATA_POKEBALL, NULL) == ITEM_LUXURY_BALL)
friendship++;
if (GetMonData(mon, MON_DATA_MET_LOCATION, NULL) == GetCurrentRegionMapSectionId())
friendship++;
}
if (friendship < 0)
friendship = 0;
if (friendship > MAX_FRIENDSHIP)
friendship = MAX_FRIENDSHIP;
SetMonData(mon, MON_DATA_FRIENDSHIP, &friendship);
retVal = FALSE;
UPDATE_FRIENDSHIP_FROM_ITEM;
}
var_3C++;
itemEffectParam++;
break;
}
}
var_38++;
r10 >>= 1;
temp1++;
effectFlags >>= 1;
}
break;
}
@@ -5216,9 +5254,9 @@ u8 GetItemEffectParamOffset(u16 itemId, u8 effectByte, u8 effectBit)
u8 offset;
int i;
u8 j;
u8 val;
u8 effectFlags;
offset = 6;
offset = ITEM_EFFECT_ARG_START;
temp = gItemEffectTable[itemId - ITEM_POTION];
@@ -5232,7 +5270,7 @@ u8 GetItemEffectParamOffset(u16 itemId, u8 effectByte, u8 effectBit)
itemEffect = temp;
for (i = 0; i < 6; i++)
for (i = 0; i < ITEM_EFFECT_ARG_START; i++)
{
switch (i)
{
@@ -5244,74 +5282,75 @@ u8 GetItemEffectParamOffset(u16 itemId, u8 effectByte, u8 effectBit)
return 0;
break;
case 4:
val = itemEffect[4];
if (val & ITEM4_PP_UP)
val &= ~(ITEM4_PP_UP);
effectFlags = itemEffect[4];
if (effectFlags & ITEM4_PP_UP)
effectFlags &= ~(ITEM4_PP_UP);
j = 0;
while (val)
while (effectFlags)
{
if (val & 1)
if (effectFlags & 1)
{
switch (j)
{
case 2:
if (val & 0x10)
val &= 0xEF;
case 0:
if (i == effectByte && (val & effectBit))
case 2: // ITEM4_HEAL_HP
if (effectFlags & (ITEM4_REVIVE >> 2))
effectFlags &= ~(ITEM4_REVIVE >> 2);
// fallthrough
case 0: // ITEM4_EV_HP
if (i == effectByte && (effectFlags & effectBit))
return offset;
offset++;
break;
case 1:
if (i == effectByte && (val & effectBit))
case 1: // ITEM4_EV_ATK
if (i == effectByte && (effectFlags & effectBit))
return offset;
offset++;
break;
case 3:
if (i == effectByte && (val & effectBit))
case 3: // ITEM4_HEAL_PP
if (i == effectByte && (effectFlags & effectBit))
return offset;
offset++;
break;
case 7:
case 7: // ITEM4_EVO_STONE
if (i == effectByte)
return 0;
break;
}
}
j++;
val >>= 1;
effectFlags >>= 1;
if (i == effectByte)
effectBit >>= 1;
}
break;
case 5:
val = itemEffect[5];
effectFlags = itemEffect[5];
j = 0;
while (val)
while (effectFlags)
{
if (val & 1)
if (effectFlags & 1)
{
switch (j)
{
case 0:
case 1:
case 2:
case 3:
case 4:
case 5:
case 6:
if (i == effectByte && (val & effectBit))
case 0: // ITEM5_EV_DEF
case 1: // ITEM5_EV_SPEED
case 2: // ITEM5_EV_SPDEF
case 3: // ITEM5_EV_SPATK
case 4: // ITEM5_PP_MAX
case 5: // ITEM5_FRIENDSHIP_LOW
case 6: // ITEM5_FRIENDSHIP_MID
if (i == effectByte && (effectFlags & effectBit))
return offset;
offset++;
break;
case 7:
case 7: // ITEM5_FRIENDSHIP_HIGH
if (i == effectByte)
return 0;
break;
}
}
j++;
val >>= 1;
effectFlags >>= 1;
if (i == effectByte)
effectBit >>= 1;
}
@@ -5387,7 +5426,7 @@ u8 GetNatureFromPersonality(u32 personality)
return personality % NUM_NATURES;
}
u16 GetEvolutionTargetSpecies(struct Pokemon *mon, u8 type, u16 evolutionItem)
u16 GetEvolutionTargetSpecies(struct Pokemon *mon, u8 mode, u16 evolutionItem)
{
int i;
u16 targetSpecies = 0;
@@ -5405,12 +5444,13 @@ u16 GetEvolutionTargetSpecies(struct Pokemon *mon, u8 type, u16 evolutionItem)
else
holdEffect = ItemId_GetHoldEffect(heldItem);
if (holdEffect == HOLD_EFFECT_PREVENT_EVOLVE && type != 3)
// Prevent evolution with Everstone, unless we're just viewing the party menu with an evolution item
if (holdEffect == HOLD_EFFECT_PREVENT_EVOLVE && mode != EVO_MODE_ITEM_CHECK)
return SPECIES_NONE;
switch (type)
switch (mode)
{
case 0:
case EVO_MODE_NORMAL:
level = GetMonData(mon, MON_DATA_LEVEL, 0);
friendship = GetMonData(mon, MON_DATA_FRIENDSHIP, 0);
@@ -5470,7 +5510,7 @@ u16 GetEvolutionTargetSpecies(struct Pokemon *mon, u8 type, u16 evolutionItem)
}
}
break;
case 1:
case EVO_MODE_TRADE:
for (i = 0; i < EVOS_PER_MON; i++)
{
switch (gEvolutionTable[species][i].method)
@@ -5489,8 +5529,8 @@ u16 GetEvolutionTargetSpecies(struct Pokemon *mon, u8 type, u16 evolutionItem)
}
}
break;
case 2:
case 3:
case EVO_MODE_ITEM_USE:
case EVO_MODE_ITEM_CHECK:
for (i = 0; i < EVOS_PER_MON; i++)
{
if (gEvolutionTable[species][i].method == EVO_ITEM