diff --git a/include/battle.h b/include/battle.h index 965ea5dbac..f009cbe7fb 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 4d057fab29..1cedf81fa4 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 a729efcee1..89f73ffc41 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 fe597d7303..2fc7cac075 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 4c95ecb63d..0c1b188ada 100644 --- a/src/battle_script_commands.c +++ b/src/battle_script_commands.c @@ -3688,7 +3688,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]; @@ -3715,7 +3715,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++) @@ -3822,9 +3822,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])) @@ -3866,7 +3871,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); @@ -3912,7 +3917,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