diff --git a/include/battle.h b/include/battle.h index 965ea5dba..f009cbe7f 100644 --- a/include/battle.h +++ b/include/battle.h @@ -489,7 +489,7 @@ struct BattleStruct u8 switchInAbilitiesCounter; u8 faintedActionsState; u8 faintedActionsBattlerId; - u16 expValue; + u32 expValue; u8 field_52; u8 sentInPokes; bool8 selectionScriptFinished[MAX_BATTLERS_COUNT]; diff --git a/include/battle_controllers.h b/include/battle_controllers.h index 4d057fab2..1cedf81fa 100644 --- a/include/battle_controllers.h +++ b/include/battle_controllers.h @@ -214,7 +214,7 @@ void BtlController_EmitChooseItem(u8 bufferId, u8* arg1); void BtlController_EmitChoosePokemon(u8 bufferId, u8 caseId, u8 arg2, u16 abilityId, u8* arg4); void BtlController_EmitCmd23(u8 bufferId); // unused void BtlController_EmitHealthBarUpdate(u8 bufferId, u16 hpValue); -void BtlController_EmitExpUpdate(u8 bufferId, u8 partyId, u16 expPoints); +void BtlController_EmitExpUpdate(u8 bufferId, u8 partyId, s32 expPoints); void BtlController_EmitStatusIconUpdate(u8 bufferId, u32 status1, u32 status2); void BtlController_EmitStatusAnimation(u8 bufferId, bool8 status2, u32 status); void BtlController_EmitStatusXor(u8 bufferId, u8 b); // unused @@ -222,7 +222,7 @@ void BtlController_EmitDataTransfer(u8 bufferId, u16 size, void *data); void BtlController_EmitDMA3Transfer(u8 bufferId, void *dst, u16 size, void *data); // unused void BtlController_EmitPlayBGM(u8 bufferId, u16 songId, void *unusedDumbDataParameter); // unused void BtlController_EmitCmd32(u8 bufferId, u16 size, void *c); // unused -void BtlController_EmitTwoReturnValues(u8 bufferId, u8 arg1, u16 arg2); +void BtlController_EmitTwoReturnValues(u8 bufferId, u8 arg1, u32 arg2); void BtlController_EmitChosenMonReturnValue(u8 bufferId, u8 b, u8 *c); void BtlController_EmitOneReturnValue(u8 bufferId, u16 arg1); void BtlController_EmitOneReturnValue_Duplicate(u8 bufferId, u16 b); diff --git a/src/battle_controller_player.c b/src/battle_controller_player.c index a729efcee..89f73ffc4 100644 --- a/src/battle_controller_player.c +++ b/src/battle_controller_player.c @@ -1079,7 +1079,7 @@ static void Intro_TryShinyAnimShowHealthbox(void) bool32 battlerAnimsDone = FALSE; // Start shiny animation if applicable for 1st pokemon - if (!gBattleSpritesDataPtr->healthBoxesData[gActiveBattler].triedShinyMonAnim + if (!gBattleSpritesDataPtr->healthBoxesData[gActiveBattler].triedShinyMonAnim && !gBattleSpritesDataPtr->healthBoxesData[gActiveBattler].ballAnimActive) TryShinyAnimation(gActiveBattler, &gPlayerParty[gBattlerPartyIndexes[gActiveBattler]]); @@ -1089,7 +1089,7 @@ static void Intro_TryShinyAnimShowHealthbox(void) TryShinyAnimation(gActiveBattler ^ BIT_FLANK, &gPlayerParty[gBattlerPartyIndexes[gActiveBattler ^ BIT_FLANK]]); // Show healthbox after ball anim - if (!gBattleSpritesDataPtr->healthBoxesData[gActiveBattler].ballAnimActive + if (!gBattleSpritesDataPtr->healthBoxesData[gActiveBattler].ballAnimActive && !gBattleSpritesDataPtr->healthBoxesData[gActiveBattler ^ BIT_FLANK].ballAnimActive) { if (!gBattleSpritesDataPtr->healthBoxesData[gActiveBattler].healthboxSlideInStarted) @@ -1166,7 +1166,7 @@ static void SwitchIn_CleanShinyAnimShowSubstitute(void) && gSprites[gBattlerSpriteIds[gActiveBattler]].callback == SpriteCallbackDummy) { CopyBattleSpriteInvisibility(gActiveBattler); - + // Reset shiny anim (even if it didn't occur) gBattleSpritesDataPtr->healthBoxesData[gActiveBattler].triedShinyMonAnim = FALSE; gBattleSpritesDataPtr->healthBoxesData[gActiveBattler].finishedShinyMonAnim = FALSE; @@ -1243,16 +1243,22 @@ static void CompleteOnInactiveTextPrinter(void) PlayerBufferExecCompleted(); } -#define tExpTask_monId data[0] -#define tExpTask_gainedExp data[1] -#define tExpTask_battler data[2] -#define tExpTask_frames data[10] +#define tExpTask_monId data[0] +#define tExpTask_battler data[2] +#define tExpTask_gainedExp_1 data[3] +#define tExpTask_gainedExp_2 data[4] // Stored as two half-words containing a word. +#define tExpTask_frames data[10] + +static s32 GetTaskExpValue(u8 taskId) +{ + return (u16)(gTasks[taskId].tExpTask_gainedExp_1) | (gTasks[taskId].tExpTask_gainedExp_2 << 16); +} static void Task_GiveExpToMon(u8 taskId) { u32 monId = (u8)(gTasks[taskId].tExpTask_monId); u8 battlerId = gTasks[taskId].tExpTask_battler; - s16 gainedExp = gTasks[taskId].tExpTask_gainedExp; + s32 gainedExp = GetTaskExpValue(taskId); if (IsDoubleBattle() == TRUE || monId != gBattlerPartyIndexes[battlerId]) // Give exp without moving the expbar. { @@ -1297,7 +1303,7 @@ static void Task_GiveExpToMon(u8 taskId) static void Task_PrepareToGiveExpWithExpBar(u8 taskId) { u8 monIndex = gTasks[taskId].tExpTask_monId; - s32 gainedExp = gTasks[taskId].tExpTask_gainedExp; + s32 gainedExp = GetTaskExpValue(taskId); u8 battlerId = gTasks[taskId].tExpTask_battler; struct Pokemon *mon = &gPlayerParty[monIndex]; u8 level = GetMonData(mon, MON_DATA_LEVEL); @@ -1322,9 +1328,9 @@ static void Task_GiveExpWithExpBar(u8 taskId) else { u8 monId = gTasks[taskId].tExpTask_monId; - s16 gainedExp = gTasks[taskId].tExpTask_gainedExp; + s32 gainedExp = GetTaskExpValue(taskId); u8 battlerId = gTasks[taskId].tExpTask_battler; - s16 newExpPoints; + s32 newExpPoints; newExpPoints = MoveBattleBar(battlerId, gHealthboxSpriteIds[battlerId], EXP_BAR, 0); SetHealthboxSpriteVisible(gHealthboxSpriteIds[battlerId]); @@ -2833,6 +2839,7 @@ static void PlayerHandleHealthBarUpdate(void) static void PlayerHandleExpUpdate(void) { u8 monId = gBattleResources->bufferA[gActiveBattler][1]; + s32 taskId, expPointsToGive; if (GetMonData(&gPlayerParty[monId], MON_DATA_LEVEL) >= MAX_LEVEL) { @@ -2840,23 +2847,22 @@ static void PlayerHandleExpUpdate(void) } else { - s16 expPointsToGive; - u8 taskId; - LoadBattleBarGfx(1); GetMonData(&gPlayerParty[monId], MON_DATA_SPECIES); // Unused return value. - expPointsToGive = T1_READ_16(&gBattleResources->bufferA[gActiveBattler][2]); + expPointsToGive = T1_READ_32(&gBattleResources->bufferA[gActiveBattler][2]); taskId = CreateTask(Task_GiveExpToMon, 10); gTasks[taskId].tExpTask_monId = monId; - gTasks[taskId].tExpTask_gainedExp = expPointsToGive; + gTasks[taskId].tExpTask_gainedExp_1 = expPointsToGive; + gTasks[taskId].tExpTask_gainedExp_2 = expPointsToGive >> 16; gTasks[taskId].tExpTask_battler = gActiveBattler; gBattlerControllerFuncs[gActiveBattler] = BattleControllerDummy; } } #undef tExpTask_monId -#undef tExpTask_gainedExp #undef tExpTask_battler +#undef tExpTask_gainedExp_1 +#undef tExpTask_gainedExp_2 #undef tExpTask_frames static void PlayerHandleStatusIconUpdate(void) diff --git a/src/battle_controllers.c b/src/battle_controllers.c index fe597d730..2fc7cac07 100644 --- a/src/battle_controllers.c +++ b/src/battle_controllers.c @@ -1220,14 +1220,15 @@ void BtlController_EmitHealthBarUpdate(u8 bufferId, u16 hpValue) PrepareBufferDataTransfer(bufferId, sBattleBuffersTransferData, 4); } -// why is the argument u16 if it's being cast to s16 anyway? -void BtlController_EmitExpUpdate(u8 bufferId, u8 partyId, u16 expPoints) +void BtlController_EmitExpUpdate(u8 bufferId, u8 partyId, s32 expPoints) { sBattleBuffersTransferData[0] = CONTROLLER_EXPUPDATE; sBattleBuffersTransferData[1] = partyId; - sBattleBuffersTransferData[2] = (s16)expPoints; - sBattleBuffersTransferData[3] = ((s16)expPoints & 0xFF00) >> 8; - PrepareBufferDataTransfer(bufferId, sBattleBuffersTransferData, 4); + sBattleBuffersTransferData[2] = expPoints; + sBattleBuffersTransferData[3] = (expPoints & 0x0000FF00) >> 8; + sBattleBuffersTransferData[4] = (expPoints & 0x00FF0000) >> 16; + sBattleBuffersTransferData[5] = (expPoints & 0xFF000000) >> 24; + PrepareBufferDataTransfer(bufferId, sBattleBuffersTransferData, 6); } void BtlController_EmitStatusIconUpdate(u8 bufferId, u32 status1, u32 status2) @@ -1315,13 +1316,15 @@ void BtlController_EmitCmd32(u8 bufferId, u16 size, void *data) PrepareBufferDataTransfer(bufferId, sBattleBuffersTransferData, size + 3); } -void BtlController_EmitTwoReturnValues(u8 bufferId, u8 arg1, u16 arg2) +void BtlController_EmitTwoReturnValues(u8 bufferId, u8 arg1, u32 arg2) { sBattleBuffersTransferData[0] = CONTROLLER_TWORETURNVALUES; sBattleBuffersTransferData[1] = arg1; sBattleBuffersTransferData[2] = arg2; - sBattleBuffersTransferData[3] = (arg2 & 0xFF00) >> 8; - PrepareBufferDataTransfer(bufferId, sBattleBuffersTransferData, 4); + sBattleBuffersTransferData[3] = (arg2 & 0x0000FF00) >> 8; + sBattleBuffersTransferData[4] = (arg2 & 0x00FF0000) >> 16; + sBattleBuffersTransferData[5] = (arg2 & 0xFF000000) >> 24; + PrepareBufferDataTransfer(bufferId, sBattleBuffersTransferData, 6); } void BtlController_EmitChosenMonReturnValue(u8 bufferId, u8 partyId, u8 *battlePartyOrder) diff --git a/src/battle_script_commands.c b/src/battle_script_commands.c index 33691db5d..3b5941743 100644 --- a/src/battle_script_commands.c +++ b/src/battle_script_commands.c @@ -1633,7 +1633,7 @@ u32 GetTotalAccuracy(u32 battlerAtk, u32 battlerDef, u32 move) calc = (calc * (100 + atkParam)) / 100; else if (atkHoldEffect == HOLD_EFFECT_ZOOM_LENS && GetBattlerTurnOrderNum(battlerAtk) > GetBattlerTurnOrderNum(battlerDef)); calc = (calc * (100 + atkParam)) / 100; - + if (gProtectStructs[battlerAtk].micle) { gProtectStructs[battlerAtk].micle = FALSE; @@ -3687,7 +3687,7 @@ static void Cmd_getexp(void) u8 holdEffect; s32 sentIn; s32 viaExpShare = 0; - u16 *exp = &gBattleStruct->expValue; + u32 *exp = &gBattleStruct->expValue; gBattlerFainted = GetBattlerForBattleScript(gBattlescriptCurrInstr[1]); sentIn = gSentPokesToOpponent[(gBattlerFainted & 2) >> 1]; @@ -3714,7 +3714,7 @@ static void Cmd_getexp(void) break; case 1: // calculate experience points to redistribute { - u16 calculatedExp; + u32 calculatedExp; s32 viaSentIn; for (viaSentIn = 0, i = 0; i < PARTY_SIZE; i++) @@ -3821,9 +3821,14 @@ static void Cmd_getexp(void) if (gBattleTypeFlags & BATTLE_TYPE_TRAINER && B_TRAINER_EXP_MULTIPLIER <= GEN_7) gBattleMoveDamage = (gBattleMoveDamage * 150) / 100; #if (B_SCALED_EXP >= GEN_5) && (B_SCALED_EXP != GEN_6) - gBattleMoveDamage *= sExperienceScalingFactors[(gBattleMons[gBattlerFainted].level * 2) + 10]; - gBattleMoveDamage /= sExperienceScalingFactors[gBattleMons[gBattlerFainted].level + GetMonData(&gPlayerParty[gBattleStruct->expGetterMonId], MON_DATA_LEVEL) + 10]; - gBattleMoveDamage++; + { + // Note: There is an edge case where if a pokemon receives a large amount of exp, it wouldn't be properly calculated + // because of multiplying by scaling factor(the value would simply be larger than an u32 can hold). Hence u64 is needed. + u64 value = gBattleMoveDamage; + value *= sExperienceScalingFactors[(gBattleMons[gBattlerFainted].level * 2) + 10]; + value /= sExperienceScalingFactors[gBattleMons[gBattlerFainted].level + GetMonData(&gPlayerParty[gBattleStruct->expGetterMonId], MON_DATA_LEVEL) + 10]; + gBattleMoveDamage = value + 1; + } #endif if (IsTradedMon(&gPlayerParty[gBattleStruct->expGetterMonId])) @@ -3865,7 +3870,7 @@ static void Cmd_getexp(void) PREPARE_MON_NICK_WITH_PREFIX_BUFFER(gBattleTextBuff1, gBattleStruct->expGetterBattlerId, gBattleStruct->expGetterMonId); // buffer 'gained' or 'gained a boosted' PREPARE_STRING_BUFFER(gBattleTextBuff2, i); - PREPARE_WORD_NUMBER_BUFFER(gBattleTextBuff3, 5, gBattleMoveDamage); + PREPARE_WORD_NUMBER_BUFFER(gBattleTextBuff3, 6, gBattleMoveDamage); PrepareStringBattle(STRINGID_PKMNGAINEDEXP, gBattleStruct->expGetterBattlerId); MonGainEVs(&gPlayerParty[gBattleStruct->expGetterMonId], gBattleMons[gBattlerFainted].species); @@ -3911,7 +3916,7 @@ static void Cmd_getexp(void) BattleScriptPushCursor(); gLeveledUpInBattle |= gBitTable[gBattleStruct->expGetterMonId]; gBattlescriptCurrInstr = BattleScript_LevelUp; - gBattleMoveDamage = (gBattleResources->bufferB[gActiveBattler][2] | (gBattleResources->bufferB[gActiveBattler][3] << 8)); + gBattleMoveDamage = T1_READ_32(&gBattleResources->bufferB[gActiveBattler][2]); AdjustFriendship(&gPlayerParty[gBattleStruct->expGetterMonId], FRIENDSHIP_EVENT_GROW_LEVEL); // update battle mon structure after level up @@ -4485,7 +4490,7 @@ static void Cmd_playanimation(void) gActiveBattler = GetBattlerForBattleScript(gBattlescriptCurrInstr[1]); argumentPtr = T2_READ_PTR(gBattlescriptCurrInstr + 3); - + #if B_TERRAIN_BG_CHANGE == FALSE if (gBattlescriptCurrInstr[2] == B_ANIM_RESTORE_BG) { @@ -7777,7 +7782,7 @@ static void Cmd_various(void) gBattlescriptCurrInstr = T1_READ_PTR(gBattlescriptCurrInstr + 3); return; } - + if (gBattleMons[gBattlerTarget].ability == gBattleMons[gBattlerAttacker].ability) { gBattlescriptCurrInstr = T1_READ_PTR(gBattlescriptCurrInstr + 3); @@ -9184,17 +9189,17 @@ bool32 TryResetBattlerStatChanges(u8 battler) { u32 j; bool32 ret = FALSE; - + gDisableStructs[battler].stockpileDef = 0; gDisableStructs[battler].stockpileSpDef = 0; for (j = 0; j < NUM_BATTLE_STATS; j++) { if (gBattleMons[battler].statStages[j] != DEFAULT_STAT_STAGE) ret = TRUE; // returns TRUE if any stat was reset - + gBattleMons[battler].statStages[j] = DEFAULT_STAT_STAGE; } - + return ret; } @@ -11280,7 +11285,7 @@ static void Cmd_tryswapitems(void) // trick static void Cmd_trycopyability(void) // role play { u16 defAbility = gBattleMons[gBattlerTarget].ability; - + if (gBattleMons[gBattlerAttacker].ability == defAbility || defAbility == ABILITY_NONE || IsRolePlayBannedAbilityAtk(gBattleMons[gBattlerAttacker].ability) @@ -11428,7 +11433,7 @@ static void Cmd_tryswapabilities(void) // skill swap gBattlescriptCurrInstr = T1_READ_PTR(gBattlescriptCurrInstr + 1); return; } - + if (gMoveResultFlags & MOVE_RESULT_NO_EFFECT) { gBattlescriptCurrInstr = T1_READ_PTR(gBattlescriptCurrInstr + 1); @@ -12448,7 +12453,7 @@ static const u16 sTelekinesisBanList[] = bool32 IsTelekinesisBannedSpecies(u16 species) { u32 i; - + for (i = 0; i < ARRAY_COUNT(sTelekinesisBanList); i++) { if (species == sTelekinesisBanList[i])