From 4b29ae39ad6830f8aa0e31d1182a991b338bd722 Mon Sep 17 00:00:00 2001 From: DizzyEggg Date: Tue, 8 Jun 2021 16:40:47 +0200 Subject: [PATCH 01/18] Allow one mon double battles --- include/battle_controllers.h | 1 + src/battle_controller_player.c | 49 +++++++++++++++++++--------------- src/battle_controllers.c | 36 ++++++++++++------------- src/battle_main.c | 15 ++++++++--- src/battle_message.c | 2 +- 5 files changed, 58 insertions(+), 45 deletions(-) diff --git a/include/battle_controllers.h b/include/battle_controllers.h index 1cedf81fa..71ca64097 100644 --- a/include/battle_controllers.h +++ b/include/battle_controllers.h @@ -185,6 +185,7 @@ extern struct UnusedControllerStruct gUnusedControllerStruct; void HandleLinkBattleSetup(void); void SetUpBattleVarsAndBirchZigzagoon(void); void InitBattleControllers(void); +bool32 IsValidForBattle(struct Pokemon *mon); void TryReceiveLinkBattleData(void); void PrepareBufferDataTransferLink(u8 bufferId, u16 size, u8 *data); diff --git a/src/battle_controller_player.c b/src/battle_controller_player.c index 89f73ffc4..59827ca80 100644 --- a/src/battle_controller_player.c +++ b/src/battle_controller_player.c @@ -1034,20 +1034,25 @@ static void Intro_DelayAndEnd(void) } } +static bool32 TwoIntroMons(u32 battlerId) // Double battle with both player pokemon active. +{ + return (IsDoubleBattle() && IsValidForBattle(&gPlayerParty[gBattlerPartyIndexes[battlerId ^ BIT_FLANK]])); +} + static void Intro_WaitForShinyAnimAndHealthbox(void) { bool8 healthboxAnimDone = FALSE; // Check if healthbox has finished sliding in - if (!IsDoubleBattle() || (IsDoubleBattle() && (gBattleTypeFlags & BATTLE_TYPE_MULTI))) + if (TwoIntroMons(gActiveBattler) && !(gBattleTypeFlags & BATTLE_TYPE_MULTI)) { - if (gSprites[gHealthboxSpriteIds[gActiveBattler]].callback == SpriteCallbackDummy) + if (gSprites[gHealthboxSpriteIds[gActiveBattler]].callback == SpriteCallbackDummy + && gSprites[gHealthboxSpriteIds[gActiveBattler ^ BIT_FLANK]].callback == SpriteCallbackDummy) healthboxAnimDone = TRUE; } else { - if (gSprites[gHealthboxSpriteIds[gActiveBattler]].callback == SpriteCallbackDummy - && gSprites[gHealthboxSpriteIds[gActiveBattler ^ BIT_FLANK]].callback == SpriteCallbackDummy) + if (gSprites[gHealthboxSpriteIds[gActiveBattler]].callback == SpriteCallbackDummy) healthboxAnimDone = TRUE; } @@ -1065,7 +1070,7 @@ static void Intro_WaitForShinyAnimAndHealthbox(void) HandleLowHpMusicChange(&gPlayerParty[gBattlerPartyIndexes[gActiveBattler]], gActiveBattler); - if (IsDoubleBattle()) + if (TwoIntroMons(gActiveBattler)) HandleLowHpMusicChange(&gPlayerParty[gBattlerPartyIndexes[gActiveBattler ^ BIT_FLANK]], gActiveBattler ^ BIT_FLANK); gBattleSpritesDataPtr->healthBoxesData[gActiveBattler].introEndDelay = 3; @@ -1094,7 +1099,7 @@ static void Intro_TryShinyAnimShowHealthbox(void) { if (!gBattleSpritesDataPtr->healthBoxesData[gActiveBattler].healthboxSlideInStarted) { - if (IsDoubleBattle() && !(gBattleTypeFlags & BATTLE_TYPE_MULTI)) + if (TwoIntroMons(gActiveBattler) && !(gBattleTypeFlags & BATTLE_TYPE_MULTI)) { UpdateHealthboxAttribute(gHealthboxSpriteIds[gActiveBattler ^ BIT_FLANK], &gPlayerParty[gBattlerPartyIndexes[gActiveBattler ^ BIT_FLANK]], HEALTHBOX_ALL); StartHealthboxSlideIn(gActiveBattler ^ BIT_FLANK); @@ -1125,15 +1130,7 @@ static void Intro_TryShinyAnimShowHealthbox(void) } // Wait for battler anims - if (!IsDoubleBattle() || (IsDoubleBattle() && (gBattleTypeFlags & BATTLE_TYPE_MULTI))) - { - if (gSprites[gBattleControllerData[gActiveBattler]].callback == SpriteCallbackDummy - && gSprites[gBattlerSpriteIds[gActiveBattler]].callback == SpriteCallbackDummy) - { - battlerAnimsDone = TRUE; - } - } - else + if (TwoIntroMons(gActiveBattler) && !(gBattleTypeFlags & BATTLE_TYPE_MULTI)) { if (gSprites[gBattleControllerData[gActiveBattler]].callback == SpriteCallbackDummy && gSprites[gBattlerSpriteIds[gActiveBattler]].callback == SpriteCallbackDummy @@ -1143,11 +1140,19 @@ static void Intro_TryShinyAnimShowHealthbox(void) battlerAnimsDone = TRUE; } } + else + { + if (gSprites[gBattleControllerData[gActiveBattler]].callback == SpriteCallbackDummy + && gSprites[gBattlerSpriteIds[gActiveBattler]].callback == SpriteCallbackDummy) + { + battlerAnimsDone = TRUE; + } + } // Clean up if (bgmRestored && battlerAnimsDone) { - if (IsDoubleBattle() && !(gBattleTypeFlags & BATTLE_TYPE_MULTI)) + if (TwoIntroMons(gActiveBattler) && !(gBattleTypeFlags & BATTLE_TYPE_MULTI)) DestroySprite(&gSprites[gBattleControllerData[gActiveBattler ^ BIT_FLANK]]); DestroySprite(&gSprites[gBattleControllerData[gActiveBattler]]); @@ -3114,12 +3119,7 @@ static void Task_StartSendOutAnim(u8 taskId) u8 savedActiveBattler = gActiveBattler; gActiveBattler = gTasks[taskId].tBattlerId; - if (!IsDoubleBattle() || (gBattleTypeFlags & BATTLE_TYPE_MULTI)) - { - gBattleResources->bufferA[gActiveBattler][1] = gBattlerPartyIndexes[gActiveBattler]; - StartSendOutAnim(gActiveBattler, FALSE); - } - else + if (TwoIntroMons(gActiveBattler) && !(gBattleTypeFlags & BATTLE_TYPE_MULTI)) { gBattleResources->bufferA[gActiveBattler][1] = gBattlerPartyIndexes[gActiveBattler]; StartSendOutAnim(gActiveBattler, FALSE); @@ -3129,6 +3129,11 @@ static void Task_StartSendOutAnim(u8 taskId) StartSendOutAnim(gActiveBattler, FALSE); gActiveBattler ^= BIT_FLANK; } + else + { + gBattleResources->bufferA[gActiveBattler][1] = gBattlerPartyIndexes[gActiveBattler]; + StartSendOutAnim(gActiveBattler, FALSE); + } gBattlerControllerFuncs[gActiveBattler] = Intro_TryShinyAnimShowHealthbox; gActiveBattler = savedActiveBattler; DestroyTask(taskId); diff --git a/src/battle_controllers.c b/src/battle_controllers.c index 2fc7cac07..033a12122 100644 --- a/src/battle_controllers.c +++ b/src/battle_controllers.c @@ -588,6 +588,14 @@ static void InitLinkBtlControllers(void) } } +bool32 IsValidForBattle(struct Pokemon *mon) +{ + u32 species = GetMonData(mon, MON_DATA_SPECIES2); + return (species != SPECIES_NONE && species != SPECIES_EGG + && GetMonData(mon, MON_DATA_HP) != 0 + && GetMonData(mon, MON_DATA_IS_EGG) == 0); +} + static void SetBattlePartyIds(void) { s32 i, j; @@ -602,10 +610,7 @@ static void SetBattlePartyIds(void) { if (GET_BATTLER_SIDE2(i) == B_SIDE_PLAYER) { - if (GetMonData(&gPlayerParty[j], MON_DATA_HP) != 0 - && GetMonData(&gPlayerParty[j], MON_DATA_SPECIES2) != SPECIES_NONE - && GetMonData(&gPlayerParty[j], MON_DATA_SPECIES2) != SPECIES_EGG - && GetMonData(&gPlayerParty[j], MON_DATA_IS_EGG) == 0) + if (IsValidForBattle(&gPlayerParty[j])) { gBattlerPartyIndexes[i] = j; break; @@ -613,10 +618,7 @@ static void SetBattlePartyIds(void) } else { - if (GetMonData(&gEnemyParty[j], MON_DATA_HP) != 0 - && GetMonData(&gEnemyParty[j], MON_DATA_SPECIES2) != SPECIES_NONE - && GetMonData(&gEnemyParty[j], MON_DATA_SPECIES2) != SPECIES_EGG - && GetMonData(&gEnemyParty[j], MON_DATA_IS_EGG) == 0) + if (IsValidForBattle(&gEnemyParty[j])) { gBattlerPartyIndexes[i] = j; break; @@ -627,11 +629,7 @@ static void SetBattlePartyIds(void) { if (GET_BATTLER_SIDE2(i) == B_SIDE_PLAYER) { - if (GetMonData(&gPlayerParty[j], MON_DATA_HP) != 0 - && GetMonData(&gPlayerParty[j], MON_DATA_SPECIES) != SPECIES_NONE // Probably a typo by Game Freak. The rest use SPECIES2. - && GetMonData(&gPlayerParty[j], MON_DATA_SPECIES2) != SPECIES_EGG - && GetMonData(&gPlayerParty[j], MON_DATA_IS_EGG) == 0 - && gBattlerPartyIndexes[i - 2] != j) + if (IsValidForBattle(&gPlayerParty[j]) && gBattlerPartyIndexes[i - 2] != j) { gBattlerPartyIndexes[i] = j; break; @@ -639,16 +637,18 @@ static void SetBattlePartyIds(void) } else { - if (GetMonData(&gEnemyParty[j], MON_DATA_HP) != 0 - && GetMonData(&gEnemyParty[j], MON_DATA_SPECIES2) != SPECIES_NONE - && GetMonData(&gEnemyParty[j], MON_DATA_SPECIES2) != SPECIES_EGG - && GetMonData(&gEnemyParty[j], MON_DATA_IS_EGG) == 0 - && gBattlerPartyIndexes[i - 2] != j) + if (IsValidForBattle(&gEnemyParty[j]) && gBattlerPartyIndexes[i - 2] != j) { gBattlerPartyIndexes[i] = j; break; } } + + // No valid mons were found. Add the empty slot. + if (gBattlerPartyIndexes[i - 2] == 0) + gBattlerPartyIndexes[i] = 1; + else + gBattlerPartyIndexes[i] = 0; } } } diff --git a/src/battle_main.c b/src/battle_main.c index 9cf56abf7..f6d1cbe62 100644 --- a/src/battle_main.c +++ b/src/battle_main.c @@ -3454,6 +3454,13 @@ static void TryDoEventsBeforeFirstTurn(void) if (gBattleControllerExecFlags) return; + // Set invalid mons as absent(for example when starting a double battle with only one pokemon). + for (i = 0; i < gBattlersCount; i++) + { + if (gBattleMons[i].hp == 0 || gBattleMons[i].species == SPECIES_NONE) + gAbsentBattlerFlags |= gBitTable[i]; + } + if (gBattleStruct->switchInAbilitiesCounter == 0) { for (i = 0; i < gBattlersCount; i++) @@ -4080,10 +4087,10 @@ static void HandleTurnActionSelectionState(void) } break; case STATE_WAIT_ACTION_CONFIRMED_STANDBY: - if (!(gBattleControllerExecFlags & ((gBitTable[gActiveBattler]) + if (!(gBattleControllerExecFlags & ((gBitTable[gActiveBattler]) | (0xF << 28) - | (gBitTable[gActiveBattler] << 4) - | (gBitTable[gActiveBattler] << 8) + | (gBitTable[gActiveBattler] << 4) + | (gBitTable[gActiveBattler] << 8) | (gBitTable[gActiveBattler] << 12)))) { if (AllAtActionConfirmed()) @@ -4646,7 +4653,7 @@ static void CheckQuickClaw_CustapBerryActivation(void) } } } - + // setup stuff before turns/actions TryClearRageAndFuryCutter(); gCurrentTurnActionNumber = 0; diff --git a/src/battle_message.c b/src/battle_message.c index c4f043e15..8ef2e4702 100644 --- a/src/battle_message.c +++ b/src/battle_message.c @@ -2560,7 +2560,7 @@ void BufferStringBattle(u16 stringID) case STRINGID_INTROSENDOUT: // poke first send-out if (GetBattlerSide(gActiveBattler) == B_SIDE_PLAYER) { - if (gBattleTypeFlags & BATTLE_TYPE_DOUBLE) + if (gBattleTypeFlags & BATTLE_TYPE_DOUBLE && IsValidForBattle(&gPlayerParty[gBattlerPartyIndexes[gActiveBattler ^ BIT_FLANK]])) { if (gBattleTypeFlags & BATTLE_TYPE_INGAME_PARTNER) stringPtr = sText_InGamePartnerSentOutZGoN; From 540719c48323dd5fe56f585ba5b613f769a78276 Mon Sep 17 00:00:00 2001 From: DizzyEggg Date: Tue, 8 Jun 2021 22:50:37 +0200 Subject: [PATCH 02/18] Add support for only one enemy pokemon in double battles start --- src/battle_controller_opponent.c | 22 +++++++++++++++------- src/battle_message.c | 4 ++-- 2 files changed, 17 insertions(+), 9 deletions(-) diff --git a/src/battle_controller_opponent.c b/src/battle_controller_opponent.c index 4eeef55c3..928fc310f 100644 --- a/src/battle_controller_opponent.c +++ b/src/battle_controller_opponent.c @@ -225,12 +225,18 @@ static void Intro_DelayAndEnd(void) } } +static bool32 TwoIntroMons(u32 battlerId) // Double battle with both player pokemon active. +{ + return (IsDoubleBattle() && IsValidForBattle(&gEnemyParty[gBattlerPartyIndexes[battlerId ^ BIT_FLANK]])); +} + static void Intro_WaitForShinyAnimAndHealthbox(void) { bool8 healthboxAnimDone = FALSE; bool8 twoMons; - if (!IsDoubleBattle() || ((IsDoubleBattle() && (gBattleTypeFlags & BATTLE_TYPE_MULTI) && !BATTLE_TWO_VS_ONE_OPPONENT) || (gBattleTypeFlags & BATTLE_TYPE_TWO_OPPONENTS))) + twoMons = TwoIntroMons(gActiveBattler); + if (!twoMons || ((twoMons && (gBattleTypeFlags & BATTLE_TYPE_MULTI) && !BATTLE_TWO_VS_ONE_OPPONENT) || (gBattleTypeFlags & BATTLE_TYPE_TWO_OPPONENTS))) { if (gSprites[gHealthboxSpriteIds[gActiveBattler]].callback == SpriteCallbackDummy) healthboxAnimDone = TRUE; @@ -292,15 +298,17 @@ static void Intro_TryShinyAnimShowHealthbox(void) { bool32 bgmRestored = FALSE; bool32 battlerAnimsDone = FALSE; + bool32 twoMons; if (!gBattleSpritesDataPtr->healthBoxesData[gActiveBattler].triedShinyMonAnim && !gBattleSpritesDataPtr->healthBoxesData[gActiveBattler].ballAnimActive && !gBattleSpritesDataPtr->healthBoxesData[gActiveBattler].finishedShinyMonAnim) TryShinyAnimation(gActiveBattler, &gEnemyParty[gBattlerPartyIndexes[gActiveBattler]]); + twoMons = TwoIntroMons(gActiveBattler); if (!(gBattleTypeFlags & BATTLE_TYPE_TWO_OPPONENTS) && (!(gBattleTypeFlags & BATTLE_TYPE_MULTI) || BATTLE_TWO_VS_ONE_OPPONENT) - && IsDoubleBattle() + && twoMons && !gBattleSpritesDataPtr->healthBoxesData[gActiveBattler ^ BIT_FLANK].triedShinyMonAnim && !gBattleSpritesDataPtr->healthBoxesData[gActiveBattler ^ BIT_FLANK].ballAnimActive && !gBattleSpritesDataPtr->healthBoxesData[gActiveBattler ^ BIT_FLANK].finishedShinyMonAnim) @@ -310,7 +318,7 @@ static void Intro_TryShinyAnimShowHealthbox(void) { if (!gBattleSpritesDataPtr->healthBoxesData[gActiveBattler].healthboxSlideInStarted) { - if (IsDoubleBattle() && (!(gBattleTypeFlags & BATTLE_TYPE_MULTI) || BATTLE_TWO_VS_ONE_OPPONENT)) + if (twoMons && (!(gBattleTypeFlags & BATTLE_TYPE_MULTI) || BATTLE_TWO_VS_ONE_OPPONENT)) { UpdateHealthboxAttribute(gHealthboxSpriteIds[gActiveBattler ^ BIT_FLANK], &gEnemyParty[gBattlerPartyIndexes[gActiveBattler ^ BIT_FLANK]], HEALTHBOX_ALL); StartHealthboxSlideIn(gActiveBattler ^ BIT_FLANK); @@ -342,7 +350,7 @@ static void Intro_TryShinyAnimShowHealthbox(void) bgmRestored = TRUE; } - if (!IsDoubleBattle() || (IsDoubleBattle() && gBattleTypeFlags & BATTLE_TYPE_MULTI && !BATTLE_TWO_VS_ONE_OPPONENT)) + if (!twoMons || (twoMons && gBattleTypeFlags & BATTLE_TYPE_MULTI && !BATTLE_TWO_VS_ONE_OPPONENT)) { if (gSprites[gBattleControllerData[gActiveBattler]].callback == SpriteCallbackDummy && gSprites[gBattlerSpriteIds[gActiveBattler]].callback == SpriteCallbackDummy) @@ -363,7 +371,7 @@ static void Intro_TryShinyAnimShowHealthbox(void) if (bgmRestored && battlerAnimsDone) { - if (IsDoubleBattle() && (!(gBattleTypeFlags & BATTLE_TYPE_MULTI) || BATTLE_TWO_VS_ONE_OPPONENT)) + if (twoMons && (!(gBattleTypeFlags & BATTLE_TYPE_MULTI) || BATTLE_TWO_VS_ONE_OPPONENT)) { DestroySprite(&gSprites[gBattleControllerData[gActiveBattler ^ BIT_FLANK]]); SetBattlerShadowSpriteCallback(gActiveBattler ^ BIT_FLANK, GetMonData(&gEnemyParty[gBattlerPartyIndexes[gActiveBattler ^ BIT_FLANK]], MON_DATA_SPECIES)); @@ -469,7 +477,7 @@ static void SwitchIn_HandleSoundAndEnd(void) { if (!gBattleSpritesDataPtr->healthBoxesData[gActiveBattler].specialAnimActive && !IsCryPlayingOrClearCrySongs()) { - if (gSprites[gBattlerSpriteIds[gActiveBattler]].callback == SpriteCallbackDummy + if (gSprites[gBattlerSpriteIds[gActiveBattler]].callback == SpriteCallbackDummy || gSprites[gBattlerSpriteIds[gActiveBattler]].callback == SpriteCallbackDummy_2) { m4aMPlayVolumeControl(&gMPlayInfo_BGM, 0xFFFF, 0x100); @@ -1892,7 +1900,7 @@ static void Task_StartSendOutAnim(u8 taskId) u8 savedActiveBank = gActiveBattler; gActiveBattler = gTasks[taskId].data[0]; - if ((!IsDoubleBattle() || (gBattleTypeFlags & BATTLE_TYPE_MULTI)) && !BATTLE_TWO_VS_ONE_OPPONENT) + if ((!TwoIntroMons(gActiveBattler) || (gBattleTypeFlags & BATTLE_TYPE_MULTI)) && !BATTLE_TWO_VS_ONE_OPPONENT) { gBattleResources->bufferA[gActiveBattler][1] = gBattlerPartyIndexes[gActiveBattler]; StartSendOutAnim(gActiveBattler, FALSE); diff --git a/src/battle_message.c b/src/battle_message.c index 8ef2e4702..e77a5e0b4 100644 --- a/src/battle_message.c +++ b/src/battle_message.c @@ -2549,7 +2549,7 @@ void BufferStringBattle(u16 stringID) { if (gBattleTypeFlags & BATTLE_TYPE_LEGENDARY) stringPtr = sText_LegendaryPkmnAppeared; - else if (gBattleTypeFlags & BATTLE_TYPE_DOUBLE) // interesting, looks like they had something planned for wild double battles + else if (gBattleTypeFlags & BATTLE_TYPE_DOUBLE && IsValidForBattle(&gEnemyParty[gBattlerPartyIndexes[gActiveBattler ^ BIT_FLANK]])) // interesting, looks like they had something planned for wild double battles stringPtr = sText_TwoWildPkmnAppeared; else if (gBattleTypeFlags & BATTLE_TYPE_WALLY_TUTORIAL) stringPtr = sText_WildPkmnAppearedPause; @@ -2578,7 +2578,7 @@ void BufferStringBattle(u16 stringID) } else { - if (gBattleTypeFlags & BATTLE_TYPE_DOUBLE) + if (gBattleTypeFlags & BATTLE_TYPE_DOUBLE && IsValidForBattle(&gEnemyParty[gBattlerPartyIndexes[gActiveBattler ^ BIT_FLANK]])) { if (BATTLE_TWO_VS_ONE_OPPONENT) stringPtr = sText_Trainer1SentOutTwoPkmn; From 2d41f08312e2b1e39b5c964ab2a644a3ee1392ec Mon Sep 17 00:00:00 2001 From: StubbornOne Date: Mon, 21 Jun 2021 14:18:33 +0800 Subject: [PATCH 03/18] Add Telepathy's damage prevention effect --- src/battle_util.c | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/src/battle_util.c b/src/battle_util.c index 87f9003e0..06ccae3b6 100644 --- a/src/battle_util.c +++ b/src/battle_util.c @@ -8105,6 +8105,17 @@ static u16 CalcTypeEffectivenessMultiplierInternal(u16 move, u8 moveType, u8 bat RecordAbilityBattle(battlerDef, ABILITY_WONDER_GUARD); } } + if (GetBattlerAbility(battlerDef) == ABILITY_TELEPATHY && battlerDef == BATTLE_PARTNER(battlerAtk)) { + modifier = UQ_4_12(0.0); + if (recordAbilities) + { + gLastUsedAbility = ABILITY_TELEPATHY; + gMoveResultFlags |= MOVE_RESULT_MISSED; + gLastLandedMoves[battlerDef] = 0; + gBattleCommunication[6] = B_MSG_AVOIDED_DMG; + RecordAbilityBattle(battlerDef, ABILITY_TELEPATHY); + } + } return modifier; } From f0cb6190e00b002d00f8ac921a22943de0e3d757 Mon Sep 17 00:00:00 2001 From: BuffelSaft Date: Sat, 7 Aug 2021 20:56:10 +1200 Subject: [PATCH 04/18] Implement Burmy form change Burmy changes form based on where it last battled. --- include/battle_util.h | 1 + src/battle_main.c | 1 + src/battle_util.c | 40 ++++++++++++++++++++++++++++++++++++++++ 3 files changed, 42 insertions(+) diff --git a/include/battle_util.h b/include/battle_util.h index e10305e56..521bb2693 100644 --- a/include/battle_util.h +++ b/include/battle_util.h @@ -138,6 +138,7 @@ bool32 CanFling(u8 battlerId); bool32 IsTelekinesisBannedSpecies(u16 species); bool32 IsHealBlockPreventingMove(u32 battler, u32 move); bool32 HasEnoughHpToEatBerry(u32 battlerId, u32 hpFraction, u32 itemId); +void DoBurmyFormChange(u32 monId); // ability checks bool32 IsRolePlayBannedAbilityAtk(u16 ability); diff --git a/src/battle_main.c b/src/battle_main.c index 94700bf96..d7fb7f697 100644 --- a/src/battle_main.c +++ b/src/battle_main.c @@ -4879,6 +4879,7 @@ static void HandleEndTurn_FinishBattle(void) { UndoMegaEvolution(i); UndoFormChange(i, B_SIDE_PLAYER, FALSE); + DoBurmyFormChange(i); } gBattleMainFunc = FreeResetData_ReturnToOvOrDoEvolutions; gCB2_AfterEvolution = BattleMainCB2; diff --git a/src/battle_util.c b/src/battle_util.c index 7deebd25e..19913781d 100644 --- a/src/battle_util.c +++ b/src/battle_util.c @@ -8659,3 +8659,43 @@ bool32 IsEntrainmentTargetOrSimpleBeamBannedAbility(u16 ability) } return FALSE; } + ++void DoBurmyFormChange(u32 monId) +{ + u16 newSpecies, currSpecies; + s32 sentIn; + struct Pokemon *party = gPlayerParty; + + sentIn = gSentPokesToOpponent[(gBattlerFainted & 2) >> 1]; + currSpecies = GetMonData(&party[monId], MON_DATA_SPECIES, NULL); + + if ((gSpeciesToNationalPokedexNum[currSpecies - 1] == SPECIES_BURMY) && (gBitTable[monId] & sentIn)) + { + switch (gBattleTerrain) + { + case BATTLE_TERRAIN_GRASS: + case BATTLE_TERRAIN_LONG_GRASS: + case BATTLE_TERRAIN_POND: + case BATTLE_TERRAIN_MOUNTAIN: + case BATTLE_TERRAIN_PLAIN: + newSpecies = SPECIES_BURMY; + break; + case BATTLE_TERRAIN_CAVE: + case BATTLE_TERRAIN_SAND: + newSpecies = SPECIES_BURMY_SANDY_CLOAK; + break; + case BATTLE_TERRAIN_BUILDING: + newSpecies = SPECIES_BURMY_TRASH_CLOAK; + break; + default: // Don't change form if last battle was water-related + newSpecies = SPECIES_NONE; + break; + } + + if (newSpecies != SPECIES_NONE) + { + SetMonData(&party[monId], MON_DATA_SPECIES, &newSpecies); + CalculateMonStats(&party[monId]); + } + } +} From 6c6363f7cf67a9f12a28a0126e3b4d84dafd6935 Mon Sep 17 00:00:00 2001 From: BuffelSaft Date: Sat, 7 Aug 2021 20:59:04 +1200 Subject: [PATCH 05/18] Use form table in species check --- src/battle_util.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/battle_util.c b/src/battle_util.c index 19913781d..fa5cb5456 100644 --- a/src/battle_util.c +++ b/src/battle_util.c @@ -8669,7 +8669,7 @@ bool32 IsEntrainmentTargetOrSimpleBeamBannedAbility(u16 ability) sentIn = gSentPokesToOpponent[(gBattlerFainted & 2) >> 1]; currSpecies = GetMonData(&party[monId], MON_DATA_SPECIES, NULL); - if ((gSpeciesToNationalPokedexNum[currSpecies - 1] == SPECIES_BURMY) && (gBitTable[monId] & sentIn)) + if ((GET_BASE_SPECIES_ID(currSpecies) == SPECIES_BURMY) && (gBitTable[monId] & sentIn)) { switch (gBattleTerrain) { From e1ca2af8b3732df237f1025d8eca28c442bdd7f1 Mon Sep 17 00:00:00 2001 From: BuffelSaft Date: Sun, 8 Aug 2021 00:52:27 +1200 Subject: [PATCH 06/18] Update src/battle_util.c Fix copy/paste typo Co-authored-by: LOuroboros --- src/battle_util.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/battle_util.c b/src/battle_util.c index fa5cb5456..50a2a6223 100644 --- a/src/battle_util.c +++ b/src/battle_util.c @@ -8660,7 +8660,7 @@ bool32 IsEntrainmentTargetOrSimpleBeamBannedAbility(u16 ability) return FALSE; } -+void DoBurmyFormChange(u32 monId) +void DoBurmyFormChange(u32 monId) { u16 newSpecies, currSpecies; s32 sentIn; From 30c983d9cac69431e5865ed6559c8c0541ad217f Mon Sep 17 00:00:00 2001 From: BuffelSaft Date: Sun, 8 Aug 2021 13:14:51 +1200 Subject: [PATCH 07/18] Define Burmy forms in battle engine Title. BE needs these to compile on its own. --- include/constants/battle_config.h | 3 +++ 1 file changed, 3 insertions(+) diff --git a/include/constants/battle_config.h b/include/constants/battle_config.h index bf31c77b3..cdeadfbf7 100644 --- a/include/constants/battle_config.h +++ b/include/constants/battle_config.h @@ -37,6 +37,9 @@ #define SPECIES_ZYGARDE 0 // 50% #define SPECIES_ZYGARDE_10 10011 // 10 % #define SPECIES_ZYGARDE_COMPLETE 10012 // 100 % + #define SPECIES_BURMY 0 + #define SPECIES_BURMY_SANDY_CLOAK 10013 + #define SPECIES_BURMY_TRASH_CLOAK 10014 #endif // Items with peculiar battle effects. From 7d3980de589d0c645c9f31838937b0da463a8611 Mon Sep 17 00:00:00 2001 From: BuffelSaft Date: Sun, 12 Sep 2021 18:33:04 +1200 Subject: [PATCH 08/18] Fix multihit moves vs damage reducing berries The extra waitmessage command is ugly but appears to be needed - if it's moved before the printstring, the message isn't displayed long enough on single hit moves. --- data/battle_scripts_1.s | 1 + src/battle_script_commands.c | 9 +++++++++ 2 files changed, 10 insertions(+) diff --git a/data/battle_scripts_1.s b/data/battle_scripts_1.s index 2d6db5a28..7c37be63c 100644 --- a/data/battle_scripts_1.s +++ b/data/battle_scripts_1.s @@ -7519,6 +7519,7 @@ BattleScript_BerryReduceDmg:: BattleScript_PrintBerryReduceString:: waitmessage B_WAIT_TIME_LONG printstring STRINGID_BERRYDMGREDUCES + waitmessage B_WAIT_TIME_LONG return BattleScript_BerryCureConfusionEnd2:: diff --git a/src/battle_script_commands.c b/src/battle_script_commands.c index 05702aec6..c7df3406e 100644 --- a/src/battle_script_commands.c +++ b/src/battle_script_commands.c @@ -1967,6 +1967,15 @@ static void Cmd_multihitresultmessage(void) } } gBattlescriptCurrInstr++; + + // Print berry reducing message after result message. + if (gSpecialStatuses[gBattlerTarget].berryReduced + && !(gMoveResultFlags & MOVE_RESULT_NO_EFFECT)) + { + gSpecialStatuses[gBattlerTarget].berryReduced = 0; + BattleScriptPushCursor(); + gBattlescriptCurrInstr = BattleScript_PrintBerryReduceString; + } } static void Cmd_attackanimation(void) From e037cb927652e3e432dce3828d3a3239d1b129a9 Mon Sep 17 00:00:00 2001 From: Ariel Antonitis Date: Mon, 20 Sep 2021 18:28:26 -0400 Subject: [PATCH 09/18] Fixed Nature Power move on different terrains. --- src/battle_script_commands.c | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/src/battle_script_commands.c b/src/battle_script_commands.c index 00a52d6c5..e1d51bd05 100644 --- a/src/battle_script_commands.c +++ b/src/battle_script_commands.c @@ -11301,7 +11301,7 @@ static void Cmd_setcharge(void) static void Cmd_callterrainattack(void) // nature power { gHitMarker &= ~(HITMARKER_ATTACKSTRING_PRINTED); - gCurrentMove = sNaturePowerMoves[gBattleTerrain]; + gCurrentMove = GetNaturePowerMove(); gBattlerTarget = GetMoveTarget(gCurrentMove, 0); BattleScriptPush(gBattleScriptsForMoveEffects[gBattleMoves[gCurrentMove].effect]); gBattlescriptCurrInstr++; @@ -11309,7 +11309,14 @@ static void Cmd_callterrainattack(void) // nature power u16 GetNaturePowerMove(void) { - //TODO terrain + if (gFieldStatuses & STATUS_FIELD_MISTY_TERRAIN) + return MOVE_MOONBLAST; + else if (gFieldStatuses & STATUS_FIELD_ELECTRIC_TERRAIN) + return MOVE_THUNDERBOLT; + else if (gFieldStatuses & STATUS_FIELD_GRASSY_TERRAIN) + return MOVE_ENERGY_BALL; + else if (gFieldStatuses & STATUS_FIELD_PSYCHIC_TERRAIN) + return MOVE_PSYCHIC; return sNaturePowerMoves[gBattleTerrain]; } From b38761e6a8561622b1f3424735f63ad2f723488e Mon Sep 17 00:00:00 2001 From: ghoulslash Date: Mon, 20 Sep 2021 22:26:02 -0400 Subject: [PATCH 10/18] add fairy to dex search --- src/pokedex.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/pokedex.c b/src/pokedex.c index cc469b32f..9de5e0c74 100644 --- a/src/pokedex.c +++ b/src/pokedex.c @@ -1393,6 +1393,7 @@ static const struct SearchOptionText sDexSearchTypeOptions[NUMBER_OF_MON_TYPES + {gText_DexEmptyString, gTypeNames[TYPE_ICE]}, {gText_DexEmptyString, gTypeNames[TYPE_DRAGON]}, {gText_DexEmptyString, gTypeNames[TYPE_DARK]}, + {gText_DexEmptyString, gTypeNames[TYPE_FAIRY]}, {}, }; @@ -1427,6 +1428,7 @@ static const u8 sDexSearchTypeIds[NUMBER_OF_MON_TYPES] = TYPE_ICE, TYPE_DRAGON, TYPE_DARK, + TYPE_FAIRY, }; // Number pairs are the task data for tracking the cursor pos and scroll offset of each option list From 6eced3f7a439010ce03e600b028d8c11fb7c2929 Mon Sep 17 00:00:00 2001 From: BuffelSaft Date: Wed, 22 Sep 2021 11:53:44 +1200 Subject: [PATCH 11/18] Fix Download Raises user's stat instead of a random target's, and determines stat boosts using the opposing Pokemon's stats. --- src/battle_util.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/battle_util.c b/src/battle_util.c index ccc0c558a..fdc4277d8 100644 --- a/src/battle_util.c +++ b/src/battle_util.c @@ -4045,7 +4045,7 @@ u8 AbilityBattleEffects(u8 caseID, u8 battler, u16 ability, u8 special, u16 move u32 opposingDef = 0, opposingSpDef = 0; opposingBattler = BATTLE_OPPOSITE(battler); - for (i = 0; i < 2; opposingBattler ^= BIT_SIDE, i++) + for (i = 0; i < 2; opposingBattler ^= BIT_FLANK, i++) { if (IsBattlerAlive(opposingBattler)) { @@ -4069,6 +4069,7 @@ u8 AbilityBattleEffects(u8 caseID, u8 battler, u16 ability, u8 special, u16 move { gBattleMons[battler].statStages[statId]++; SET_STATCHANGER(statId, 1, FALSE); + gBattlerAttacker = battler; PREPARE_STAT_BUFFER(gBattleTextBuff1, statId); BattleScriptPushCursorAndCallback(BattleScript_AttackerAbilityStatRaiseEnd3); effect++; From 7571d56140d71869a6959a20dfc0ef7bb5dd1f39 Mon Sep 17 00:00:00 2001 From: Eduardo Quezada D'Ottone Date: Tue, 21 Sep 2021 22:57:05 -0300 Subject: [PATCH 12/18] Applied Egg's implementation of telepathy --- src/battle_util.c | 19 +++++-------------- 1 file changed, 5 insertions(+), 14 deletions(-) diff --git a/src/battle_util.c b/src/battle_util.c index 591f0bac0..bbc045e39 100644 --- a/src/battle_util.c +++ b/src/battle_util.c @@ -8129,27 +8129,18 @@ static u16 CalcTypeEffectivenessMultiplierInternal(u16 move, u8 moveType, u8 bat modifier = UQ_4_12(1.0); } - if (GetBattlerAbility(battlerDef) == ABILITY_WONDER_GUARD && modifier <= UQ_4_12(1.0) && gBattleMoves[move].power) + if (((GetBattlerAbility(battlerDef) == ABILITY_WONDER_GUARD && modifier <= UQ_4_12(1.0)) + || (GetBattlerAbility(battlerDef) == ABILITY_TELEPATHY && battlerDef == BATTLE_PARTNER(battlerAtk))) + && gBattleMoves[move].power) { modifier = UQ_4_12(0.0); if (recordAbilities) { - gLastUsedAbility = ABILITY_WONDER_GUARD; + gLastUsedAbility = gBattleMons[battlerDef].ability; gMoveResultFlags |= MOVE_RESULT_MISSED; gLastLandedMoves[battlerDef] = 0; gBattleCommunication[MISS_TYPE] = B_MSG_AVOIDED_DMG; - RecordAbilityBattle(battlerDef, ABILITY_WONDER_GUARD); - } - } - if (GetBattlerAbility(battlerDef) == ABILITY_TELEPATHY && battlerDef == BATTLE_PARTNER(battlerAtk)) { - modifier = UQ_4_12(0.0); - if (recordAbilities) - { - gLastUsedAbility = ABILITY_TELEPATHY; - gMoveResultFlags |= MOVE_RESULT_MISSED; - gLastLandedMoves[battlerDef] = 0; - gBattleCommunication[6] = B_MSG_AVOIDED_DMG; - RecordAbilityBattle(battlerDef, ABILITY_TELEPATHY); + RecordAbilityBattle(battlerDef, gBattleMons[battlerDef].ability); } } From b65b27c0dd7dc1e51a80ee5dca14db43288d60a2 Mon Sep 17 00:00:00 2001 From: Eduardo Quezada D'Ottone Date: Wed, 22 Sep 2021 00:20:34 -0300 Subject: [PATCH 13/18] Adds missing check for Egg in Illusion --- src/battle_util.c | 1 + 1 file changed, 1 insertion(+) diff --git a/src/battle_util.c b/src/battle_util.c index 0f18efb9a..fce7c1eeb 100644 --- a/src/battle_util.c +++ b/src/battle_util.c @@ -8746,6 +8746,7 @@ bool32 SetIllusionMon(struct Pokemon *mon, u32 battlerId) id = i; if (GetMonData(&party[id], MON_DATA_SANITY_HAS_SPECIES) && GetMonData(&party[id], MON_DATA_HP) + && !GetMonData(&party[id], MON_DATA_IS_EGG) && &party[id] != mon && &party[id] != partnerMon) { From 35af7a9cc1b614d12578ff87cea699ee9dd76aa9 Mon Sep 17 00:00:00 2001 From: BuffelSaft Date: Wed, 22 Sep 2021 15:44:13 +1200 Subject: [PATCH 14/18] Fix Foul Play if != else if. Pretty sure I broke this when adding Body Press. --- src/battle_util.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/battle_util.c b/src/battle_util.c index 0f18efb9a..fa38778ab 100644 --- a/src/battle_util.c +++ b/src/battle_util.c @@ -7791,7 +7791,7 @@ static u32 CalcAttackStat(u16 move, u8 battlerAtk, u8 battlerDef, u8 moveType, b atkStage = gBattleMons[battlerDef].statStages[STAT_SPATK]; } } - if (gBattleMoves[move].effect == EFFECT_BODY_PRESS) + else if (gBattleMoves[move].effect == EFFECT_BODY_PRESS) { atkStat = gBattleMons[battlerAtk].defense; atkStage = gBattleMons[battlerAtk].statStages[STAT_DEF]; From 0ee13418dc5c8867673fecab6e61c40fc6b78243 Mon Sep 17 00:00:00 2001 From: sbird Date: Wed, 22 Sep 2021 13:15:43 +0200 Subject: [PATCH 15/18] [ai] use expected value move dmg calculation --- include/battle_script_commands.h | 1 + src/battle_ai_util.c | 16 +++++++++++++--- src/battle_script_commands.c | 9 +++++++++ 3 files changed, 23 insertions(+), 3 deletions(-) diff --git a/include/battle_script_commands.h b/include/battle_script_commands.h index 2e6a0aa4e..dec1596f2 100644 --- a/include/battle_script_commands.h +++ b/include/battle_script_commands.h @@ -13,6 +13,7 @@ struct StatFractions }; s32 CalcCritChanceStage(u8 battlerAtk, u8 battlerDef, u32 move, bool32 recordAbility); +s8 GetInverseCritChance(u8 battlerAtk, u8 battlerDef, u32 move); u32 GetTotalAccuracy(u32 battlerAtk, u32 battlerDef, u32 move); u8 GetBattlerTurnOrderNum(u8 battlerId); bool32 NoAliveMonsForEitherParty(void); diff --git a/src/battle_ai_util.c b/src/battle_ai_util.c index 48d3817c8..b68c68e1b 100644 --- a/src/battle_ai_util.c +++ b/src/battle_ai_util.c @@ -711,7 +711,8 @@ static bool32 AI_GetIfCrit(u32 move, u8 battlerAtk, u8 battlerDef) s32 AI_CalcDamage(u16 move, u8 battlerAtk, u8 battlerDef) { - s32 dmg, moveType; + s32 dmg, moveType, critDmg, normalDmg; + s8 critChance; SaveBattlerData(battlerAtk); SaveBattlerData(battlerDef); @@ -722,7 +723,15 @@ s32 AI_CalcDamage(u16 move, u8 battlerAtk, u8 battlerDef) gBattleStruct->dynamicMoveType = 0; SetTypeBeforeUsingMove(move, battlerAtk); GET_MOVE_TYPE(move, moveType); - dmg = CalculateMoveDamage(move, battlerAtk, battlerDef, moveType, 0, AI_GetIfCrit(move, battlerAtk, battlerDef), FALSE, FALSE); + + critChance = GetInverseCritChance(battlerAtk, battlerDef, move); + normalDmg = CalculateMoveDamage(move, battlerAtk, battlerDef, moveType, 0, FALSE, FALSE, FALSE); + critDmg = CalculateMoveDamage(move, battlerAtk, battlerDef, moveType, 0, TRUE, FALSE, FALSE); + + if(critChance == -1) + dmg = normalDmg; + else + dmg = (critDmg + normalDmg * (critChance - 1)) / critChance; // handle dynamic move damage switch (gBattleMoves[move].effect) @@ -749,7 +758,8 @@ s32 AI_CalcDamage(u16 move, u8 battlerAtk, u8 battlerDef) //case EFFECT_METAL_BURST: //case EFFECT_COUNTER: default: - dmg *= (100 - (Random() % 10)) / 100; // add random factor + //do not add the random factor, it's an average case analysis + //dmg *= (100 - (Random() % 10)) / 100; // add random factor break; } diff --git a/src/battle_script_commands.c b/src/battle_script_commands.c index e1d51bd05..a10635e1a 100644 --- a/src/battle_script_commands.c +++ b/src/battle_script_commands.c @@ -1822,6 +1822,15 @@ s32 CalcCritChanceStage(u8 battlerAtk, u8 battlerDef, u32 move, bool32 recordAbi return critChance; } +s8 GetInverseCritChance(u8 battlerAtk, u8 battlerDef, u32 move) +{ + s32 critChanceIndex = CalcCritChanceStage(battlerAtk, battlerDef, move, FALSE); + if(critChanceIndex < 0) + return -1; + else + return sCriticalHitChance[critChanceIndex]; +} + static void Cmd_critcalc(void) { s32 critChance = CalcCritChanceStage(gBattlerAttacker, gBattlerTarget, gCurrentMove, TRUE); From 53d985eeddafe743711e2b92c6be514ce95df768 Mon Sep 17 00:00:00 2001 From: Eduardo Quezada D'Ottone Date: Wed, 22 Sep 2021 10:29:32 -0300 Subject: [PATCH 16/18] Scald thaw flag config for = GEN_6 + .flags = FLAG_PROTECT_AFFECTED | FLAG_MIRROR_MOVE_AFFECTED | FLAG_KINGS_ROCK_AFFECTED | FLAG_SHEER_FORCE_BOOST | FLAG_THAW_USER, + #else + .flags = FLAG_PROTECT_AFFECTED | FLAG_MIRROR_MOVE_AFFECTED | FLAG_KINGS_ROCK_AFFECTED | FLAG_SHEER_FORCE_BOOST, + #endif .effect = EFFECT_SCALD, .power = 80, .type = TYPE_WATER, @@ -7943,7 +7948,6 @@ const struct BattleMove gBattleMoves[MOVES_COUNT] = .secondaryEffectChance = 30, .target = MOVE_TARGET_SELECTED, .priority = 0, - .flags = FLAG_PROTECT_AFFECTED | FLAG_MIRROR_MOVE_AFFECTED | FLAG_KINGS_ROCK_AFFECTED | FLAG_SHEER_FORCE_BOOST | FLAG_THAW_USER, .split = SPLIT_SPECIAL, }, From 707d49242e8f733f5bc797b219364cdc9d73b394 Mon Sep 17 00:00:00 2001 From: Eduardo Quezada D'Ottone Date: Wed, 22 Sep 2021 12:31:23 -0300 Subject: [PATCH 17/18] Revert "Scald thaw flag config for Gen 5" --- src/data/battle_moves.h | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/src/data/battle_moves.h b/src/data/battle_moves.h index 0fc3b3e42..657341b5e 100644 --- a/src/data/battle_moves.h +++ b/src/data/battle_moves.h @@ -7935,11 +7935,6 @@ const struct BattleMove gBattleMoves[MOVES_COUNT] = [MOVE_SCALD] = { - #if B_UPDATED_MOVE_DATA >= GEN_6 - .flags = FLAG_PROTECT_AFFECTED | FLAG_MIRROR_MOVE_AFFECTED | FLAG_KINGS_ROCK_AFFECTED | FLAG_SHEER_FORCE_BOOST | FLAG_THAW_USER, - #else - .flags = FLAG_PROTECT_AFFECTED | FLAG_MIRROR_MOVE_AFFECTED | FLAG_KINGS_ROCK_AFFECTED | FLAG_SHEER_FORCE_BOOST, - #endif .effect = EFFECT_SCALD, .power = 80, .type = TYPE_WATER, @@ -7948,6 +7943,7 @@ const struct BattleMove gBattleMoves[MOVES_COUNT] = .secondaryEffectChance = 30, .target = MOVE_TARGET_SELECTED, .priority = 0, + .flags = FLAG_PROTECT_AFFECTED | FLAG_MIRROR_MOVE_AFFECTED | FLAG_KINGS_ROCK_AFFECTED | FLAG_SHEER_FORCE_BOOST | FLAG_THAW_USER, .split = SPLIT_SPECIAL, }, From 303e31ae251b289fc46774314140617228d9e44e Mon Sep 17 00:00:00 2001 From: sbird Date: Wed, 22 Sep 2021 18:05:58 +0200 Subject: [PATCH 18/18] [ai] use expected value to simulate EFFECT_PSYWAVE --- src/battle_ai_util.c | 12 ++---------- 1 file changed, 2 insertions(+), 10 deletions(-) diff --git a/src/battle_ai_util.c b/src/battle_ai_util.c index b68c68e1b..95d979e94 100644 --- a/src/battle_ai_util.c +++ b/src/battle_ai_util.c @@ -737,6 +737,8 @@ s32 AI_CalcDamage(u16 move, u8 battlerAtk, u8 battlerDef) switch (gBattleMoves[move].effect) { case EFFECT_LEVEL_DAMAGE: + case EFFECT_PSYWAVE: + //psywave's expected damage is equal to the user's level dmg = gBattleMons[battlerAtk].level; break; case EFFECT_DRAGON_RAGE: @@ -745,16 +747,6 @@ s32 AI_CalcDamage(u16 move, u8 battlerAtk, u8 battlerDef) case EFFECT_SONICBOOM: dmg = 20; break; - case EFFECT_PSYWAVE: - { - u32 randDamage; - if (B_PSYWAVE_DMG >= GEN_6) - randDamage = (Random() % 101); - else - randDamage = (Random() % 11) * 10; - dmg = gBattleMons[battlerAtk].level * (randDamage + 50) / 100; - } - break; //case EFFECT_METAL_BURST: //case EFFECT_COUNTER: default: