diff --git a/include/battle.h b/include/battle.h index 56061557a..c9eeb3b0b 100644 --- a/include/battle.h +++ b/include/battle.h @@ -873,6 +873,9 @@ struct BattleHealthboxInfo u8 triedShinyMonAnim : 1; // x80 u8 finishedShinyMonAnim : 1; // x1 u8 opponentDrawPartyStatusSummaryDelay : 5; // x2 + u8 bgmRestored:1; + u8 waitForCry:1; + u8 healthboxSlideInStarted:1; u8 healthboxBounceSpriteId; u8 battlerBounceSpriteId; u8 animationState; @@ -914,7 +917,7 @@ extern u8 *gLinkBattleRecvBuffer; struct MonSpritesGfx { void *firstDecompressed; // ptr to the decompressed sprite of the first pokemon - void *sprites[MAX_BATTLERS_COUNT]; + u8 *sprites[MAX_BATTLERS_COUNT]; struct SpriteTemplate templates[MAX_BATTLERS_COUNT]; struct SpriteFrameImage images[MAX_BATTLERS_COUNT][4]; u8 field_F4[0x80 - (4 * MAX_BATTLERS_COUNT)]; // unused, original - spritesGfx @@ -978,10 +981,7 @@ extern bool8 gTransformedShininess[MAX_BATTLERS_COUNT]; extern u8 gBattlerPositions[MAX_BATTLERS_COUNT]; extern u8 gHealthboxSpriteIds[MAX_BATTLERS_COUNT]; extern u8 gBattleOutcome; -extern u8 gBattleMonForms[MAX_BATTLERS_COUNT]; extern u32 gBattleControllerExecFlags; -// extern u8 gBattleBufferA[MAX_BATTLERS_COUNT][0x200]; -// extern u8 gBattleBufferB[MAX_BATTLERS_COUNT][0x200]; extern u8 gActionSelectionCursor[MAX_BATTLERS_COUNT]; extern void (*gPreBattleCallback1)(void); extern bool8 gDoingBattleAnim; diff --git a/include/battle_setup.h b/include/battle_setup.h index 9a93cd55a..6937d2e9a 100644 --- a/include/battle_setup.h +++ b/include/battle_setup.h @@ -6,6 +6,7 @@ extern u16 gPartnerTrainerId; void StartWildBattle(void); +void StartDoubleWildBattle(void); void StartRoamerBattle(void); void StartOldManTutorialBattle(void); void StartScriptedWildBattle(void); diff --git a/include/pokemon.h b/include/pokemon.h index 4fbdb690c..95266cf8e 100644 --- a/include/pokemon.h +++ b/include/pokemon.h @@ -779,7 +779,6 @@ u8 GetMoveRelearnerMoves(struct Pokemon *mon, u16 *moves); u8 GetLevelUpMovesBySpecies(u16 species, u16 *moves); u8 GetNumberOfRelearnableMoves(struct Pokemon *mon); u16 SpeciesToPokedexNum(u16 species); -void ClearBattleMonForms(void); void PlayBattleBGM(void); void PlayMapChosenOrBattleBGM(u16 songId); const u32 *GetMonFrontSpritePal(struct Pokemon *mon); diff --git a/include/text.h b/include/text.h index 7090a029b..76419b0b7 100644 --- a/include/text.h +++ b/include/text.h @@ -178,7 +178,7 @@ void DrawDownArrow(u8 windowId, u16 x, u16 y, u8 bgColor, bool8 drawArrow, u8 *c u16 RenderText(struct TextPrinter *textPrinter); s32 (*GetFontWidthFunc(u8 glyphId))(u16, bool32); s32 GetStringWidth(u8 fontId, const u8 *str, s16 letterSpacing); -u8 RenderTextHandleBold(u8 *pixels, u8 fontId, u8 *str, int a3, int a4, int a5, int a6, int a7); +u8 RenderTextHandleBold(u8 *pixels, u8 fontId, u8 *str); u8 DrawKeypadIcon(u8 windowId, u8 keypadIconId, u16 x, u16 y); u8 GetKeypadIconTileOffset(u8 keypadIconId); u8 GetKeypadIconWidth(u8 keypadIconId); diff --git a/include/wild_encounter.h b/include/wild_encounter.h index 066366dc7..2e6804e21 100644 --- a/include/wild_encounter.h +++ b/include/wild_encounter.h @@ -51,5 +51,6 @@ bool8 SweetScentWildEncounter(void); void SeedWildEncounterRng(u16 randVal); void ResetEncounterRateModifiers(void); bool8 TryStandardWildEncounter(u32 currMetatileAttrs); +bool8 TryDoDoubleWildBattle(void); #endif // GUARD_WILD_ENCOUNTER_H diff --git a/src/battle_anim_effects_3.c b/src/battle_anim_effects_3.c index e5911cbf8..fc5376aa0 100644 --- a/src/battle_anim_effects_3.c +++ b/src/battle_anim_effects_3.c @@ -2300,7 +2300,7 @@ void AnimTask_TransformMon(u8 taskId) else position = GetBattlerPosition(gBattleAnimAttacker); - src = gMonSpritesGfxPtr->sprites[position] + (gBattleMonForms[gBattleAnimAttacker] << 11); + src = gMonSpritesGfxPtr->sprites[position]; dest = animBg.bgTiles; CpuCopy32(src, dest, MON_PIC_SIZE); LoadBgTiles(1, animBg.bgTiles, 0x800, animBg.tilesOffset); diff --git a/src/battle_anim_mons.c b/src/battle_anim_mons.c index 8dd3aca48..3244a3deb 100644 --- a/src/battle_anim_mons.c +++ b/src/battle_anim_mons.c @@ -1,6 +1,7 @@ #include "global.h" #include "gflib.h" #include "battle_anim.h" +#include "battle_interface.h" #include "data.h" #include "decompress.h" #include "pokemon_icon.h" @@ -86,10 +87,10 @@ u8 GetBattlerSpriteCoord(u8 battlerId, u8 coordType) { case BATTLER_COORD_X: case BATTLER_COORD_X_2: - retVal = sBattlerCoords[IS_DOUBLE_BATTLE()][GetBattlerPosition(battlerId)].x; + retVal = sBattlerCoords[WhichBattleCoords(battlerId)][GetBattlerPosition(battlerId)].x; break; case BATTLER_COORD_Y: - retVal = sBattlerCoords[IS_DOUBLE_BATTLE()][GetBattlerPosition(battlerId)].y; + retVal = sBattlerCoords[WhichBattleCoords(battlerId)][GetBattlerPosition(battlerId)].y; break; case BATTLER_COORD_Y_PIC_OFFSET: case BATTLER_COORD_Y_PIC_OFFSET_DEFAULT: @@ -208,7 +209,7 @@ static u8 GetBattlerSpriteFinal_Y(u8 battlerId, u16 species, bool8 a3) offset = GetBattlerYDelta(battlerId, species); offset -= GetBattlerElevation(battlerId, species); } - y = offset + sBattlerCoords[IS_DOUBLE_BATTLE()][GetBattlerPosition(battlerId)].y; + y = offset + sBattlerCoords[WhichBattleCoords(battlerId)][GetBattlerPosition(battlerId)].y; if (a3) { if (GetBattlerSide(battlerId) == B_SIDE_PLAYER) @@ -782,16 +783,6 @@ void InitSpritePosToAnimAttacker(struct Sprite *sprite, bool8 respectMonPicOffse sprite->y += gBattleAnimArgs[1]; } -// u8 GetBattlerSide(u8 battlerId) -// { -// return GET_BATTLER_SIDE2(battlerId); -// } - -// u8 GetBattlerPosition(u8 battlerId) -// { -// return GET_BATTLER_POSITION(battlerId); -// } - u8 GetBattlerAtPosition(u8 position) { u8 i; diff --git a/src/battle_anim_special.c b/src/battle_anim_special.c index 183f86196..a1afa7cd7 100644 --- a/src/battle_anim_special.c +++ b/src/battle_anim_special.c @@ -2040,21 +2040,17 @@ void AnimTask_SetTargetToEffectBattler(u8 taskId) void TryShinyAnimation(u8 battler, struct Pokemon *mon) { bool32 isShiny; - u32 otId, personality; - u32 shinyValue; u8 taskId1, taskId2; + struct Pokemon* illusionMon; - isShiny = FALSE; + isShiny = GetMonData(mon, MON_DATA_IS_SHINY); gBattleSpritesDataPtr->healthBoxesData[battler].triedShinyMonAnim = TRUE; - otId = GetMonData(mon, MON_DATA_OT_ID); - personality = GetMonData(mon, MON_DATA_PERSONALITY); + illusionMon = GetIllusionMonPtr(battler); + if (illusionMon != NULL) + mon = illusionMon; - if (IsBattlerSpriteVisible(battler)) + if (IsBattlerSpriteVisible(battler) && IsValidForBattle(mon)) { - shinyValue = HIHALF(otId) ^ LOHALF(otId) ^ HIHALF(personality) ^ LOHALF(personality); - if (shinyValue < SHINY_ODDS) - isShiny = TRUE; - if (isShiny) { if (GetSpriteTileStartByTag(ANIM_TAG_GOLD_STARS) == 0xFFFF) diff --git a/src/battle_controller_player.c b/src/battle_controller_player.c index ed812768d..367e35f9c 100644 --- a/src/battle_controller_player.c +++ b/src/battle_controller_player.c @@ -1002,37 +1002,38 @@ static void Intro_DelayAndEnd(u32 battler) static void Intro_WaitForShinyAnimAndHealthbox(u32 battler) { - bool8 var = FALSE; + bool8 healthboxAnimDone = FALSE; - if (!IsDoubleBattle() || (IsDoubleBattle() && (gBattleTypeFlags & BATTLE_TYPE_MULTI))) - { - if (gSprites[gHealthboxSpriteIds[battler]].callback == SpriteCallbackDummy) - var = TRUE; - } - else + // Check if healthbox has finished sliding in + if (TwoPlayerIntroMons(battler) && !(gBattleTypeFlags & BATTLE_TYPE_MULTI)) { if (gSprites[gHealthboxSpriteIds[battler]].callback == SpriteCallbackDummy && gSprites[gHealthboxSpriteIds[BATTLE_PARTNER(battler)]].callback == SpriteCallbackDummy) - var = TRUE; + healthboxAnimDone = TRUE; } - if (IsCryPlayingOrClearCrySongs()) - var = FALSE; - if (var && gBattleSpritesDataPtr->healthBoxesData[battler].finishedShinyMonAnim + else + { + if (gSprites[gHealthboxSpriteIds[battler]].callback == SpriteCallbackDummy) + healthboxAnimDone = TRUE; + } + + // If healthbox and shiny anim are done + if (healthboxAnimDone && gBattleSpritesDataPtr->healthBoxesData[battler].finishedShinyMonAnim && gBattleSpritesDataPtr->healthBoxesData[BATTLE_PARTNER(battler)].finishedShinyMonAnim) { + // Reset shiny anim (even if it didn't occur) gBattleSpritesDataPtr->healthBoxesData[battler].triedShinyMonAnim = FALSE; gBattleSpritesDataPtr->healthBoxesData[battler].finishedShinyMonAnim = FALSE; gBattleSpritesDataPtr->healthBoxesData[BATTLE_PARTNER(battler)].triedShinyMonAnim = FALSE; gBattleSpritesDataPtr->healthBoxesData[BATTLE_PARTNER(battler)].finishedShinyMonAnim = FALSE; FreeSpriteTilesByTag(ANIM_TAG_GOLD_STARS); FreeSpritePaletteByTag(ANIM_TAG_GOLD_STARS); - if (gBattleTypeFlags & BATTLE_TYPE_MULTI) - m4aMPlayContinue(&gMPlayInfo_BGM); - else - m4aMPlayVolumeControl(&gMPlayInfo_BGM, TRACKS_ALL, 256); + HandleLowHpMusicChange(&gPlayerParty[gBattlerPartyIndexes[battler]], battler); - if (IsDoubleBattle()) + + if (TwoPlayerIntroMons(battler)) HandleLowHpMusicChange(&gPlayerParty[gBattlerPartyIndexes[BATTLE_PARTNER(battler)]], BATTLE_PARTNER(battler)); + gBattleSpritesDataPtr->healthBoxesData[battler].introEndDelay = 3; gBattlerControllerFuncs[battler] = Intro_DelayAndEnd; } @@ -1040,28 +1041,86 @@ static void Intro_WaitForShinyAnimAndHealthbox(u32 battler) static void Intro_TryShinyAnimShowHealthbox(u32 battler) { - if (!gBattleSpritesDataPtr->healthBoxesData[battler].ballAnimActive && !gBattleSpritesDataPtr->healthBoxesData[BATTLE_PARTNER(battler)].ballAnimActive) + bool32 bgmRestored = FALSE; + bool32 battlerAnimsDone = FALSE; + + // Start shiny animation if applicable for 1st Pokémon + if (!gBattleSpritesDataPtr->healthBoxesData[battler].triedShinyMonAnim + && !gBattleSpritesDataPtr->healthBoxesData[battler].ballAnimActive) + TryShinyAnimation(battler, &gPlayerParty[gBattlerPartyIndexes[battler]]); + + // Start shiny animation if applicable for 2nd Pokémon + if (!gBattleSpritesDataPtr->healthBoxesData[BATTLE_PARTNER(battler)].triedShinyMonAnim + && !gBattleSpritesDataPtr->healthBoxesData[BATTLE_PARTNER(battler)].ballAnimActive) + TryShinyAnimation(BATTLE_PARTNER(battler), &gPlayerParty[gBattlerPartyIndexes[BATTLE_PARTNER(battler)]]); + + // Show healthbox after ball anim + if (!gBattleSpritesDataPtr->healthBoxesData[battler].ballAnimActive + && !gBattleSpritesDataPtr->healthBoxesData[BATTLE_PARTNER(battler)].ballAnimActive) { - if (!gBattleSpritesDataPtr->healthBoxesData[battler].triedShinyMonAnim) - TryShinyAnimation(battler, &gPlayerParty[gBattlerPartyIndexes[battler]]); - if (!gBattleSpritesDataPtr->healthBoxesData[BATTLE_PARTNER(battler)].triedShinyMonAnim) - TryShinyAnimation(BATTLE_PARTNER(battler), &gPlayerParty[gBattlerPartyIndexes[BATTLE_PARTNER(battler)]]); - if (IsDoubleBattle() && !(gBattleTypeFlags & BATTLE_TYPE_MULTI)) + if (!gBattleSpritesDataPtr->healthBoxesData[battler].healthboxSlideInStarted) { - DestroySprite(&gSprites[gBattleControllerData[BATTLE_PARTNER(battler)]]); - UpdateHealthboxAttribute(gHealthboxSpriteIds[BATTLE_PARTNER(battler)], - &gPlayerParty[gBattlerPartyIndexes[BATTLE_PARTNER(battler)]], - HEALTHBOX_ALL); - StartHealthboxSlideIn(BATTLE_PARTNER(battler)); - SetHealthboxSpriteVisible(gHealthboxSpriteIds[BATTLE_PARTNER(battler)]); + if (TwoPlayerIntroMons(battler) && !(gBattleTypeFlags & BATTLE_TYPE_MULTI)) + { + UpdateHealthboxAttribute(gHealthboxSpriteIds[BATTLE_PARTNER(battler)], &gPlayerParty[gBattlerPartyIndexes[BATTLE_PARTNER(battler)]], HEALTHBOX_ALL); + StartHealthboxSlideIn(BATTLE_PARTNER(battler)); + SetHealthboxSpriteVisible(gHealthboxSpriteIds[BATTLE_PARTNER(battler)]); + } + UpdateHealthboxAttribute(gHealthboxSpriteIds[battler], &gPlayerParty[gBattlerPartyIndexes[battler]], HEALTHBOX_ALL); + StartHealthboxSlideIn(battler); + SetHealthboxSpriteVisible(gHealthboxSpriteIds[battler]); } + gBattleSpritesDataPtr->healthBoxesData[battler].healthboxSlideInStarted = TRUE; + } + + // Restore bgm after cry has played and healthbox anim is started + if (!gBattleSpritesDataPtr->healthBoxesData[battler].waitForCry + && gBattleSpritesDataPtr->healthBoxesData[battler].healthboxSlideInStarted + && !gBattleSpritesDataPtr->healthBoxesData[BATTLE_PARTNER(battler)].waitForCry + && !IsCryPlayingOrClearCrySongs()) + { + if (!gBattleSpritesDataPtr->healthBoxesData[battler].bgmRestored) + { + if (gBattleTypeFlags & BATTLE_TYPE_MULTI && gBattleTypeFlags & BATTLE_TYPE_LINK) + m4aMPlayContinue(&gMPlayInfo_BGM); + else + m4aMPlayVolumeControl(&gMPlayInfo_BGM, TRACKS_ALL, 0x100); + } + gBattleSpritesDataPtr->healthBoxesData[battler].bgmRestored = TRUE; + bgmRestored = TRUE; + } + + // Wait for battler anims + if (TwoPlayerIntroMons(battler) && !(gBattleTypeFlags & BATTLE_TYPE_MULTI)) + { + if (gSprites[gBattleControllerData[battler]].callback == SpriteCallbackDummy + && gSprites[gBattlerSpriteIds[battler]].callback == SpriteCallbackDummy + && gSprites[gBattleControllerData[BATTLE_PARTNER(battler)]].callback == SpriteCallbackDummy + && gSprites[gBattlerSpriteIds[BATTLE_PARTNER(battler)]].callback == SpriteCallbackDummy) + { + battlerAnimsDone = TRUE; + } + } + else + { + if (gSprites[gBattleControllerData[battler]].callback == SpriteCallbackDummy + && gSprites[gBattlerSpriteIds[battler]].callback == SpriteCallbackDummy) + { + battlerAnimsDone = TRUE; + } + } + + // Clean up + if (bgmRestored && battlerAnimsDone) + { + if (TwoPlayerIntroMons(battler) && !(gBattleTypeFlags & BATTLE_TYPE_MULTI)) + DestroySprite(&gSprites[gBattleControllerData[BATTLE_PARTNER(battler)]]); DestroySprite(&gSprites[gBattleControllerData[battler]]); - UpdateHealthboxAttribute(gHealthboxSpriteIds[battler], - &gPlayerParty[gBattlerPartyIndexes[battler]], - HEALTHBOX_ALL); - StartHealthboxSlideIn(battler); - SetHealthboxSpriteVisible(gHealthboxSpriteIds[battler]); + gBattleSpritesDataPtr->animationData->introAnimActive = FALSE; + gBattleSpritesDataPtr->healthBoxesData[battler].bgmRestored = FALSE; + gBattleSpritesDataPtr->healthBoxesData[battler].healthboxSlideInStarted = FALSE; + gBattlerControllerFuncs[battler] = Intro_WaitForShinyAnimAndHealthbox; } } diff --git a/src/battle_controllers.c b/src/battle_controllers.c index 87d76dfae..195623461 100644 --- a/src/battle_controllers.c +++ b/src/battle_controllers.c @@ -70,7 +70,6 @@ void SetUpBattleVars(void) HandleLinkBattleSetup(); gBattleControllerExecFlags = 0; ClearBattleAnimationVars(); - ClearBattleMonForms(); BattleAI_SetupItems(); BattleAI_SetupFlags(); } @@ -315,16 +314,13 @@ static void SetBattlePartyIds(void) { for (i = 0; i < gBattlersCount; i++) { - for (j = 0; j < PARTY_SIZE; ++j) + for (j = 0; j < PARTY_SIZE; j++) { if (i < 2) { - if (GET_BATTLER_SIDE2(i) == B_SIDE_PLAYER) + if (GetBattlerSide(i) == B_SIDE_PLAYER) { - if (GetMonData(&gPlayerParty[j], MON_DATA_HP) != 0 - && GetMonData(&gPlayerParty[j], MON_DATA_SPECIES_OR_EGG) != SPECIES_NONE - && GetMonData(&gPlayerParty[j], MON_DATA_SPECIES_OR_EGG) != SPECIES_EGG - && !GetMonData(&gPlayerParty[j], MON_DATA_IS_EGG)) + if (IsValidForBattle(&gPlayerParty[j])) { gBattlerPartyIndexes[i] = j; break; @@ -332,10 +328,7 @@ static void SetBattlePartyIds(void) } else { - if (GetMonData(&gEnemyParty[j], MON_DATA_HP) != 0 - && GetMonData(&gEnemyParty[j], MON_DATA_SPECIES_OR_EGG) != SPECIES_NONE - && GetMonData(&gEnemyParty[j], MON_DATA_SPECIES_OR_EGG) != SPECIES_EGG - && !GetMonData(&gEnemyParty[j], MON_DATA_IS_EGG)) + if (IsValidForBattle(&gEnemyParty[j])) { gBattlerPartyIndexes[i] = j; break; @@ -344,13 +337,9 @@ static void SetBattlePartyIds(void) } else { - if (GET_BATTLER_SIDE2(i) == B_SIDE_PLAYER) + if (GetBattlerSide(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_SPECIES_OR_EGG) != SPECIES_EGG - && !GetMonData(&gPlayerParty[j], MON_DATA_IS_EGG) - && gBattlerPartyIndexes[i - 2] != j) + if (IsValidForBattle(&gPlayerParty[j]) && gBattlerPartyIndexes[i - 2] != j) { gBattlerPartyIndexes[i] = j; break; @@ -358,19 +347,24 @@ static void SetBattlePartyIds(void) } else { - if (GetMonData(&gEnemyParty[j], MON_DATA_HP) != 0 - && GetMonData(&gEnemyParty[j], MON_DATA_SPECIES_OR_EGG) != SPECIES_NONE - && GetMonData(&gEnemyParty[j], MON_DATA_SPECIES_OR_EGG) != SPECIES_EGG - && !GetMonData(&gEnemyParty[j], MON_DATA_IS_EGG) - && 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; } } } + + if (gBattleTypeFlags & BATTLE_TYPE_TWO_OPPONENTS) + gBattlerPartyIndexes[1] = 0, gBattlerPartyIndexes[3] = 3; } } @@ -839,7 +833,7 @@ void BtlController_EmitChooseAction(u32 battler, u32 bufferId, u8 action, u16 it } // Unused -static void BtlController_EmitUnknownYesNoBox(u32 battler, u32 bufferId, u32 arg1) // TODO: Does the function name make sense for pokefirered? +static void BtlController_EmitUnknownYesNoBox(u32 battler, u32 bufferId, u32 arg1) { gBattleResources->transferBuffer[0] = CONTROLLER_UNKNOWNYESNOBOX; gBattleResources->transferBuffer[1] = arg1; diff --git a/src/battle_gfx_sfx_util.c b/src/battle_gfx_sfx_util.c index 1a18a74f5..a86348899 100644 --- a/src/battle_gfx_sfx_util.c +++ b/src/battle_gfx_sfx_util.c @@ -139,11 +139,7 @@ void SpriteCB_WaitForBattlerBallReleaseAnim(struct Sprite *sprite) if (gSprites[spriteId].animPaused) gSprites[spriteId].animPaused = 0; else if (gSprites[spriteId].animEnded) - { - gSprites[spriteId].callback = SetIdleSpriteCallback; - StartSpriteAffineAnim(&gSprites[spriteId], 0); sprite->callback = SpriteCallbackDummy; - } } // Unused @@ -211,7 +207,6 @@ bool8 TryHandleLaunchBattleTableAnimation(u8 activeBattler, u8 atkBattler, u8 de if (tableId == B_ANIM_FORM_CHANGE && (argument & 0x80)) { - gBattleMonForms[activeBattler] = (argument & ~(0x80)); return TRUE; } else if (gBattleSpritesDataPtr->battlerData[activeBattler].behindSubstitute @@ -313,6 +308,8 @@ void BattleLoadMonSpriteGfx(struct Pokemon *mon, u32 battler) u32 monsPersonality, currentPersonality, isShiny, species, paletteOffset, position; const void *lzPaletteData; struct Pokemon *illusionMon = GetIllusionMonPtr(battler); + if (illusionMon != NULL) + mon = illusionMon; if (GetMonData(mon, MON_DATA_IS_EGG) || GetMonData(mon, MON_DATA_SPECIES) == SPECIES_NONE) // Don't load GFX of egg pokemon. return; @@ -497,7 +494,12 @@ bool8 BattleLoadAllHealthBoxesGfx(u8 state) else { if (state == 2) - LoadCompressedSpriteSheetUsingHeap(&sSpriteSheets_DoublesPlayerHealthbox[0]); + { + if (WhichBattleCoords(0)) + LoadCompressedSpriteSheetUsingHeap(&sSpriteSheets_DoublesPlayerHealthbox[0]); + else + LoadCompressedSpriteSheetUsingHeap(&sSpriteSheet_SinglesPlayerHealthbox); + } else if (state == 3) LoadCompressedSpriteSheetUsingHeap(&sSpriteSheets_DoublesPlayerHealthbox[1]); else if (state == 4) @@ -697,7 +699,7 @@ void HandleSpeciesGfxDataChange(u8 battlerAtk, u8 battlerDef, bool32 megaEvo, bo CpuCopy32(&gPlttBufferFaded[paletteOffset], &gPlttBufferUnfaded[paletteOffset], PLTT_SIZEOF(16)); gBattleSpritesDataPtr->battlerData[battlerAtk].transformSpecies = targetSpecies; } - gBattleMonForms[battlerAtk] = gBattleMonForms[battlerDef]; + gSprites[gBattlerSpriteIds[battlerAtk]].y = GetBattlerSpriteDefault_Y(battlerAtk); StartSpriteAnim(&gSprites[gBattlerSpriteIds[battlerAtk]], 0); } @@ -717,11 +719,7 @@ void BattleLoadSubstituteOrMonSpriteGfx(u8 battlerId, bool8 loadMonSprite) LZDecompressVram(gSubstituteDollTilemap, gMonSpritesGfxPtr->sprites[position]); for (i = 1; i < 4; ++i) { - u8 (*ptr)[4][0x800] = gMonSpritesGfxPtr->sprites[position]; - - ++ptr; - --ptr; - DmaCopy32Defvars(3, (*ptr)[0], (*ptr)[i], 0x800); + Dma3CopyLarge32_(gMonSpritesGfxPtr->sprites[position], &gMonSpritesGfxPtr->sprites[position][MON_PIC_SIZE * i], MON_PIC_SIZE); } palOffset = OBJ_PLTT_ID(battlerId); LoadCompressedPalette(gSubstituteDollPal, palOffset, PLTT_SIZE_4BPP); @@ -738,7 +736,7 @@ void BattleLoadSubstituteOrMonSpriteGfx(u8 battlerId, bool8 loadMonSprite) void LoadBattleMonGfxAndAnimate(u8 battlerId, bool8 loadMonSprite, u8 spriteId) { BattleLoadSubstituteOrMonSpriteGfx(battlerId, loadMonSprite); - StartSpriteAnim(&gSprites[spriteId], gBattleMonForms[battlerId]); + StartSpriteAnim(&gSprites[spriteId], 0); if (!loadMonSprite) gSprites[spriteId].y = GetSubstituteSpriteDefault_Y(battlerId); else @@ -946,7 +944,6 @@ void BattleInterfaceSetWindowPals(void) void ClearTemporarySpeciesSpriteData(u8 battlerId, bool8 dontClearSubstitute) { gBattleSpritesDataPtr->battlerData[battlerId].transformSpecies = SPECIES_NONE; - gBattleMonForms[battlerId] = 0; if (!dontClearSubstitute) ClearBehindSubstituteBit(battlerId); } diff --git a/src/battle_interface.c b/src/battle_interface.c index d17b31e2e..5124036ab 100644 --- a/src/battle_interface.c +++ b/src/battle_interface.c @@ -67,7 +67,7 @@ struct TestingBar static void SpriteCB_HealthBoxOther(struct Sprite *sprite); static void SpriteCB_HealthBar(struct Sprite *sprite); static const u8 *GetBattleInterfaceGfxPtr(u8 which); -static void UpdateHpTextInHealthboxInDoubles(u8 healthboxSpriteId, s16 value, u8 maxOrCurrent); +static void UpdateHpTextInHealthboxInDoubles(u8 healthboxSpriteId, u32 maxOrCurrent, s16 currHp, s16 maxHp); static void Task_HidePartyStatusSummary_BattleStart_1(u8 taskId); static void Task_HidePartyStatusSummary_BattleStart_2(u8 taskId); static void SpriteCB_PartySummaryBar_Exit(struct Sprite *sprite); @@ -682,7 +682,7 @@ u8 CreateBattlerHealthboxSprites(u8 battlerId) u8 healthbarSpriteId; struct Sprite *healthbarSprite; - if (!IsDoubleBattle()) + if (WhichBattleCoords(battlerId) == 0) { if (GetBattlerSide(battlerId) == B_SIDE_PLAYER) { @@ -864,7 +864,7 @@ void InitBattlerHealthboxCoords(u8 battler) { s16 x = 0, y = 0; - if (!IsDoubleBattle()) + if (!WhichBattleCoords(battler)) { if (GetBattlerSide(battler) != B_SIDE_PLAYER) x = 44, y = 30; @@ -910,7 +910,7 @@ static void UpdateLvlInHealthbox(u8 healthboxSpriteId, u8 lvl) if (GetBattlerSide(gSprites[healthboxSpriteId].sBattlerId) == B_SIDE_PLAYER) { objVram = (void *)(OBJ_VRAM0); - if (!IsDoubleBattle()) + if (!WhichBattleCoords(gSprites[healthboxSpriteId].sBattlerId)) objVram += spriteTileNum + 0x820; else objVram += spriteTileNum + 0x420; @@ -929,7 +929,7 @@ void UpdateHpTextInHealthbox(u32 healthboxSpriteId, u32 maxOrCurrent, s16 currHp u32 windowId, spriteTileNum; u8 *windowTileData; - if (GetBattlerSide(gSprites[healthboxSpriteId].sBattlerId) == B_SIDE_PLAYER && !IsDoubleBattle()) + if (GetBattlerSide(gSprites[healthboxSpriteId].sBattlerId) == B_SIDE_PLAYER && !WhichBattleCoords(gSprites[healthboxSpriteId].sBattlerId)) { // Only in the Japanese release can HP be displayed as text outside of double battles u8 text[8]; @@ -960,12 +960,12 @@ void UpdateHpTextInHealthbox(u32 healthboxSpriteId, u32 maxOrCurrent, s16 currHp u8 text[20] = __("{COLOR 01}{HIGHLIGHT 02}"); battler = gSprites[healthboxSpriteId].sBattlerId; - if (IsDoubleBattle() == TRUE || GetBattlerSide(battler) == B_SIDE_OPPONENT) + if (WhichBattleCoords(battler) == TRUE || GetBattlerSide(battler) == B_SIDE_OPPONENT) { if (maxOrCurrent != HP_CURRENT) - UpdateHpTextInHealthboxInDoubles(healthboxSpriteId, maxHp, maxOrCurrent); + UpdateHpTextInHealthboxInDoubles(healthboxSpriteId, maxOrCurrent, currHp, maxHp); else - UpdateHpTextInHealthboxInDoubles(healthboxSpriteId, currHp, maxOrCurrent); + UpdateHpTextInHealthboxInDoubles(healthboxSpriteId, maxOrCurrent, currHp, maxHp); } else { @@ -992,7 +992,7 @@ void UpdateHpTextInHealthbox(u32 healthboxSpriteId, u32 maxOrCurrent, s16 currHp ConvertIntToDecimalStringN(text + 6, maxHp, STR_CONV_MODE_RIGHT_ALIGN, 3); else ConvertIntToDecimalStringN(text + 6, currHp, STR_CONV_MODE_RIGHT_ALIGN, 3); - RenderTextHandleBold(gMonSpritesGfxPtr->barFontGfx, 0, text, 0, 0, 0, 0, 0); + RenderTextHandleBold(gMonSpritesGfxPtr->barFontGfx, 0, text); for (i = 0; i < 3; i++) { @@ -1006,7 +1006,7 @@ void UpdateHpTextInHealthbox(u32 healthboxSpriteId, u32 maxOrCurrent, s16 currHp static const u8 sText_Slash[] = _("/"); -static void UpdateHpTextInHealthboxInDoubles(u8 healthboxSpriteId, s16 value, u8 maxOrCurrent) +static void UpdateHpTextInHealthboxInDoubles(u8 healthboxSpriteId, u32 maxOrCurrent, s16 currHp, s16 maxHp) { u32 windowId, spriteTileNum; u8 *windowTileData; @@ -1027,11 +1027,15 @@ static void UpdateHpTextInHealthboxInDoubles(u8 healthboxSpriteId, s16 value, u8 var = 0; healthBarSpriteId = gSprites[healthboxSpriteId].sHealthBarSpriteId; - txtPtr = ConvertIntToDecimalStringN(text + 6, value, STR_CONV_MODE_RIGHT_ALIGN, 3); + if (maxOrCurrent == HP_CURRENT) + txtPtr = ConvertIntToDecimalStringN(text + 6, currHp, STR_CONV_MODE_RIGHT_ALIGN, 3); + else + txtPtr = ConvertIntToDecimalStringN(text + 6, maxHp, STR_CONV_MODE_RIGHT_ALIGN, 3); + if (maxOrCurrent == HP_CURRENT) StringCopy(txtPtr, sText_Slash); - RenderTextHandleBold(gMonSpritesGfxPtr->barFontGfx, 0, text, 0, 0, 0, 0, 0); + RenderTextHandleBold(gMonSpritesGfxPtr->barFontGfx, 0, text); for (i = var; i < var + 3; i++) { @@ -1082,7 +1086,7 @@ static void PrintSafariMonInfo(u8 healthboxSpriteId, struct Pokemon *mon) var = 5; nature = GetNature(mon); StringCopy(text + 6, gNatureNamePointers[nature]); - RenderTextHandleBold(barFontGfx, 0, text, 0, 0, 0, 0, 0); + RenderTextHandleBold(barFontGfx, 0, text); for (j = 6, i = 0; i < var; i++, j++) { @@ -1114,7 +1118,7 @@ static void PrintSafariMonInfo(u8 healthboxSpriteId, struct Pokemon *mon) ConvertIntToDecimalStringN(text + 9, gBattleStruct->safariEscapeFactor, STR_CONV_MODE_RIGHT_ALIGN, 2); text[5] = CHAR_SPACE; text[8] = CHAR_SLASH; - RenderTextHandleBold(gMonSpritesGfxPtr->barFontGfx, 0, text, 0, 0, 0, 0, 0); + RenderTextHandleBold(gMonSpritesGfxPtr->barFontGfx, 0, text); j = healthBarSpriteId; // Needed to match for some reason. for (j = 0; j < 5; j++) @@ -1143,7 +1147,7 @@ void SwapHpBarsWithHpText(void) { if (gSprites[gHealthboxSpriteIds[i]].callback == SpriteCallbackDummy && GetBattlerSide(i) != B_SIDE_OPPONENT - && (IsDoubleBattle() || GetBattlerSide(i) != B_SIDE_PLAYER)) + && (WhichBattleCoords(i) || GetBattlerSide(i) != B_SIDE_PLAYER)) { bool8 noBars; @@ -1151,18 +1155,20 @@ void SwapHpBarsWithHpText(void) noBars = gBattleSpritesDataPtr->battlerData[i].hpNumbersNoBars; if (GetBattlerSide(i) == B_SIDE_PLAYER) { - if (!IsDoubleBattle()) + if (!WhichBattleCoords(i)) continue; if (gBattleTypeFlags & BATTLE_TYPE_SAFARI) continue; if (noBars == TRUE) // bars to text { + s16 currHp = GetMonData(&gPlayerParty[gBattlerPartyIndexes[i]], MON_DATA_HP); + s16 maxHp = GetMonData(&gPlayerParty[gBattlerPartyIndexes[i]], MON_DATA_MAX_HP); healthBarSpriteId = gSprites[gHealthboxSpriteIds[i]].sHealthBarSpriteId; CpuFill32(0, (void *)(OBJ_VRAM0 + gSprites[healthBarSpriteId].oam.tileNum * TILE_SIZE_4BPP), 8 * TILE_SIZE_4BPP); - UpdateHpTextInHealthboxInDoubles(gHealthboxSpriteIds[i], GetMonData(&gPlayerParty[gBattlerPartyIndexes[i]], MON_DATA_HP), HP_CURRENT); - UpdateHpTextInHealthboxInDoubles(gHealthboxSpriteIds[i], GetMonData(&gPlayerParty[gBattlerPartyIndexes[i]], MON_DATA_MAX_HP), HP_MAX); + UpdateHpTextInHealthboxInDoubles(gHealthboxSpriteIds[i], HP_CURRENT, currHp, maxHp); + UpdateHpTextInHealthboxInDoubles(gHealthboxSpriteIds[i], HP_MAX, currHp, maxHp); } else // text to bars { @@ -1184,11 +1190,13 @@ void SwapHpBarsWithHpText(void) } else { + s16 currHp = GetMonData(&gEnemyParty[gBattlerPartyIndexes[i]], MON_DATA_HP); + s16 maxHp = GetMonData(&gEnemyParty[gBattlerPartyIndexes[i]], MON_DATA_MAX_HP); healthBarSpriteId = gSprites[gHealthboxSpriteIds[i]].sHealthBarSpriteId; CpuFill32(0, (void *)(OBJ_VRAM0 + gSprites[healthBarSpriteId].oam.tileNum * 32), 8 * TILE_SIZE_4BPP); - UpdateHpTextInHealthboxInDoubles(gHealthboxSpriteIds[i], GetMonData(&gEnemyParty[gBattlerPartyIndexes[i]], MON_DATA_HP), HP_CURRENT); - UpdateHpTextInHealthboxInDoubles(gHealthboxSpriteIds[i], GetMonData(&gEnemyParty[gBattlerPartyIndexes[i]], MON_DATA_MAX_HP), HP_MAX); + UpdateHpTextInHealthboxInDoubles(gHealthboxSpriteIds[i], HP_CURRENT, currHp, maxHp); + UpdateHpTextInHealthboxInDoubles(gHealthboxSpriteIds[i], HP_MAX, currHp, maxHp); } } else // text to bars @@ -1496,7 +1504,7 @@ u8 CreatePartyStatusSummarySprites(u8 battlerId, struct HpAndStatus *partyInfo, { isOpponent = TRUE; - if (!isSwitchingMons || !IsDoubleBattle()) + if (!isSwitchingMons || !WhichBattleCoords(battlerId)) x = 104, y = 40; else x = 104, y = 16; @@ -1935,7 +1943,7 @@ void UpdateNickInHealthbox(u8 healthboxSpriteId, struct Pokemon *mon) { TextIntoHealthboxObject((void *)(OBJ_VRAM0 + 0x40 + spriteTileNum), windowTileData, 6); ptr = (void *)(OBJ_VRAM0); - if (!IsDoubleBattle()) + if (!WhichBattleCoords(gSprites[healthboxSpriteId].sBattlerId)) ptr += spriteTileNum + 0x800; else ptr += spriteTileNum + 0x400; @@ -2005,7 +2013,7 @@ static void UpdateStatusIconInHealthbox(u8 healthboxSpriteId) if (GetBattlerSide(battlerId) == B_SIDE_PLAYER) { status = GetMonData(&gPlayerParty[gBattlerPartyIndexes[battlerId]], MON_DATA_STATUS); - if (!IsDoubleBattle()) + if (!WhichBattleCoords(battlerId)) tileNumAdder = 0x1A; else tileNumAdder = 0x12; @@ -2065,7 +2073,7 @@ static void UpdateStatusIconInHealthbox(u8 healthboxSpriteId) FillPalette(sStatusIconColors[statusPalId], pltAdder + OBJ_PLTT_OFFSET, PLTT_SIZEOF(1)); CpuCopy16(&gPlttBufferUnfaded[OBJ_PLTT_OFFSET + pltAdder], (u16 *)OBJ_PLTT + pltAdder, PLTT_SIZEOF(1)); CpuCopy32(statusGfxPtr, (void *)(OBJ_VRAM0 + (gSprites[healthboxSpriteId].oam.tileNum + tileNumAdder) * TILE_SIZE_4BPP), 3 * TILE_SIZE_4BPP); - if (IsDoubleBattle() == TRUE || GetBattlerSide(battlerId) == B_SIDE_OPPONENT) + if (WhichBattleCoords(battlerId) == TRUE || GetBattlerSide(battlerId) == B_SIDE_OPPONENT) { if (!gBattleSpritesDataPtr->battlerData[battlerId].hpNumbersNoBars) { @@ -2171,31 +2179,30 @@ static void UpdateLeftNoOfBallsTextOnHealthbox(u8 healthboxSpriteId) void UpdateHealthboxAttribute(u8 healthboxSpriteId, struct Pokemon *mon, u8 elementId) { - s32 maxHp, currHp; u8 battlerId = gSprites[healthboxSpriteId].sBattlerId; + s32 maxHp = GetMonData(mon, MON_DATA_MAX_HP); + s32 currHp = GetMonData(mon, MON_DATA_HP); - if (elementId == HEALTHBOX_ALL && !IsDoubleBattle()) - GetBattlerSide(battlerId); // Pointless function call. - - if (GetBattlerSide(gSprites[healthboxSpriteId].sBattlerId) == B_SIDE_PLAYER) + if (GetBattlerSide(battlerId) == B_SIDE_PLAYER) { - u8 isDoubles; + u8 isDoubles = WhichBattleCoords(battlerId); - maxHp = GetMonData(mon, MON_DATA_MAX_HP); - currHp = GetMonData(mon, MON_DATA_HP); if (elementId == HEALTHBOX_LEVEL || elementId == HEALTHBOX_ALL) UpdateLvlInHealthbox(healthboxSpriteId, GetMonData(mon, MON_DATA_LEVEL)); + + if (elementId == HEALTHBOX_CURRENT_HP || elementId == HEALTHBOX_ALL) UpdateHpTextInHealthbox(healthboxSpriteId, HP_CURRENT, currHp, maxHp); if (elementId == HEALTHBOX_MAX_HP || elementId == HEALTHBOX_ALL) UpdateHpTextInHealthbox(healthboxSpriteId, HP_MAX, currHp, maxHp); + if (elementId == HEALTHBOX_HEALTH_BAR || elementId == HEALTHBOX_ALL) { LoadBattleBarGfx(0); SetBattleBarStruct(battlerId, healthboxSpriteId, maxHp, currHp, 0); MoveBattleBar(battlerId, healthboxSpriteId, HEALTH_BAR, 0); } - isDoubles = IsDoubleBattle(); + if (!isDoubles && (elementId == HEALTHBOX_EXP_BAR || elementId == HEALTHBOX_ALL)) { u16 species; diff --git a/src/battle_intro.c b/src/battle_intro.c index 0b5b81ef0..bfc2c6fe3 100644 --- a/src/battle_intro.c +++ b/src/battle_intro.c @@ -471,7 +471,7 @@ void CopyBattlerSpriteToBg(s32 bgId, u8 x, u8 y, u8 battlerPosition, u8 palno, u u8 battler = GetBattlerAtPosition(battlerPosition); s32 offset = tilesOffset; - CpuCopy16(gMonSpritesGfxPtr->sprites[battlerPosition] + BG_SCREEN_SIZE * gBattleMonForms[battler], tilesDest, BG_SCREEN_SIZE); + CpuCopy16(gMonSpritesGfxPtr->sprites[battlerPosition], tilesDest, BG_SCREEN_SIZE); LoadBgTiles(bgId, tilesDest, 0x1000, tilesOffset); for (i = y; i < y + 8; ++i) for (j = x; j < x + 8; ++j) diff --git a/src/battle_main.c b/src/battle_main.c index d433eef9b..056f041d3 100644 --- a/src/battle_main.c +++ b/src/battle_main.c @@ -193,7 +193,6 @@ EWRAM_DATA struct BattleSpriteData *gBattleSpritesDataPtr = NULL; EWRAM_DATA struct MonSpritesGfx *gMonSpritesGfxPtr = NULL; EWRAM_DATA u16 gBattleMovePower = 0; EWRAM_DATA u16 gMoveToLearn = 0; -EWRAM_DATA u8 gBattleMonForms[MAX_BATTLERS_COUNT] = {0}; EWRAM_DATA u8 gPartyCriticalHits[PARTY_SIZE] = {0}; EWRAM_DATA u32 gFieldStatuses = 0; EWRAM_DATA u8 gBattlerAbility = 0; @@ -861,7 +860,10 @@ static void CB2_InitBattleInternal(void) if (!(gBattleTypeFlags & BATTLE_TYPE_LINK)) { CreateNPCTrainerParty(&gEnemyParty[0], gTrainerBattleOpponent_A, FALSE); + if (gBattleTypeFlags & BATTLE_TYPE_TWO_OPPONENTS && !BATTLE_TWO_VS_ONE_OPPONENT) + CreateNPCTrainerParty(&gEnemyParty[PARTY_SIZE / 2], gTrainerBattleOpponent_B, FALSE); SetWildMonHeldItem(); + CalculateEnemyPartyCount(); } gMain.inBattle = TRUE; @@ -1627,12 +1629,14 @@ static void CB2_HandleStartMultiBattle(void) break; case 5: case 9: + case 13: ++gBattleCommunication[0]; - gBattleCommunication[SPRITES_INIT_STATE1] = 1; + gBattleCommunication[1] = 1; // fall through case 6: case 10: - if (--gBattleCommunication[SPRITES_INIT_STATE1] == 0) + case 14: + if (--gBattleCommunication[1] == 0) ++gBattleCommunication[0]; break; } @@ -2257,11 +2261,11 @@ static void SpriteCB_AnimFaintOpponent(struct Sprite *sprite) } else // Erase bottom part of the sprite to create a smooth illusion of mon falling down. { - u8 *dst = (u8 *)gMonSpritesGfxPtr->sprites[GetBattlerPosition(sprite->sBattler)] + (gBattleMonForms[sprite->sBattler] << 11) + (sprite->data[3] << 8); + u8 *dst = &gMonSpritesGfxPtr->sprites[GetBattlerPosition(sprite->sBattler)][(sprite->data[3] << 8)]; for (i = 0; i < 0x100; i++) *(dst++) = 0; - StartSpriteAnim(sprite, gBattleMonForms[sprite->sBattler]); + StartSpriteAnim(sprite, 0); } } } @@ -3190,6 +3194,7 @@ static void DoBattleIntro(void) } break; } + } static void TryDoEventsBeforeFirstTurn(void) diff --git a/src/battle_message.c b/src/battle_message.c index f06f37b2f..10731c0fc 100644 --- a/src/battle_message.c +++ b/src/battle_message.c @@ -2478,7 +2478,7 @@ void BufferStringBattle(u32 battler, u16 stringId) } else if (gBattleTypeFlags & BATTLE_TYPE_LEGENDARY) stringPtr = sText_WildPkmnAppeared2; - 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[GetBattlerAtPosition(B_POSITION_OPPONENT_RIGHT)]])) // interesting, looks like they had something planned for wild double battles stringPtr = sText_TwoWildPkmnAppeared; else if (gBattleTypeFlags & BATTLE_TYPE_OLD_MAN_TUTORIAL) stringPtr = sText_WildPkmnAppearedPause; @@ -2489,9 +2489,13 @@ void BufferStringBattle(u32 battler, u16 stringId) case STRINGID_INTROSENDOUT: // poke first send-out if (GetBattlerSide(battler) == B_SIDE_PLAYER) { - if (gBattleTypeFlags & BATTLE_TYPE_DOUBLE) + if (gBattleTypeFlags & BATTLE_TYPE_DOUBLE && IsValidForBattle(&gPlayerParty[gBattlerPartyIndexes[BATTLE_PARTNER(battler)]])) { - if (gBattleTypeFlags & BATTLE_TYPE_MULTI) + if (gBattleTypeFlags & BATTLE_TYPE_INGAME_PARTNER) + stringPtr = sText_InGamePartnerSentOutZGoN; + else if (gBattleTypeFlags & BATTLE_TYPE_TWO_OPPONENTS) + stringPtr = sText_GoTwoPkmn; + else if (gBattleTypeFlags & BATTLE_TYPE_MULTI) stringPtr = sText_LinkPartnerSentOutPkmnGoPkmn; else stringPtr = sText_GoTwoPkmn; @@ -2503,9 +2507,13 @@ void BufferStringBattle(u32 battler, u16 stringId) } else { - if (gBattleTypeFlags & BATTLE_TYPE_DOUBLE) + if (gBattleTypeFlags & BATTLE_TYPE_DOUBLE && IsValidForBattle(&gEnemyParty[gBattlerPartyIndexes[BATTLE_PARTNER(battler)]])) { - if (gBattleTypeFlags & BATTLE_TYPE_MULTI) + if (BATTLE_TWO_VS_ONE_OPPONENT) + stringPtr = sText_Trainer1SentOutTwoPkmn; + else if (gBattleTypeFlags & BATTLE_TYPE_TWO_OPPONENTS) + stringPtr = sText_TwoTrainersSentPkmn; + else if (gBattleTypeFlags & BATTLE_TYPE_MULTI) stringPtr = sText_TwoLinkTrainersSentOutPkmn; else if (gBattleTypeFlags & BATTLE_TYPE_LINK) stringPtr = sText_LinkTrainerSentOutTwoPkmn; diff --git a/src/battle_setup.c b/src/battle_setup.c index 9093f5346..ab67a2699 100644 --- a/src/battle_setup.c +++ b/src/battle_setup.c @@ -59,7 +59,7 @@ struct TrainerBattleParameter static void DoSafariBattle(void); static void DoGhostBattle(void); -static void DoStandardWildBattle(void); +static void DoStandardWildBattle(bool32 isDouble); static void CB2_EndWildBattle(void); static u8 GetWildBattleTransition(void); static u8 GetTrainerBattleTransition(void); @@ -242,16 +242,23 @@ void StartWildBattle(void) else if (CheckSilphScopeInPokemonTower(gSaveBlock1Ptr->location.mapGroup, gSaveBlock1Ptr->location.mapNum)) DoGhostBattle(); else - DoStandardWildBattle(); + DoStandardWildBattle(FALSE); } -static void DoStandardWildBattle(void) +void StartDoubleWildBattle(void) +{ + DoStandardWildBattle(TRUE); +} + +static void DoStandardWildBattle(bool32 isDouble) { LockPlayerFieldControls(); FreezeObjectEvents(); StopPlayerAvatar(); gMain.savedCallback = CB2_EndWildBattle; gBattleTypeFlags = 0; + if (isDouble) + gBattleTypeFlags |= BATTLE_TYPE_DOUBLE; CreateBattleStartTask(GetWildBattleTransition(), 0); IncrementGameStat(GAME_STAT_TOTAL_BATTLES); IncrementGameStat(GAME_STAT_WILD_BATTLES); diff --git a/src/battle_util.c b/src/battle_util.c index d41b84320..6a061fed0 100644 --- a/src/battle_util.c +++ b/src/battle_util.c @@ -366,22 +366,28 @@ void HandleAction_UseItem(void) bool8 TryRunFromBattle(u8 battler) { - bool8 effect = FALSE; + bool32 effect = FALSE; u8 holdEffect; u8 speedVar; - if (gBattleMons[battler].item == ITEM_ENIGMA_BERRY) + if (gBattleMons[battler].item == ITEM_ENIGMA_BERRY_E_READER) holdEffect = gEnigmaBerries[battler].holdEffect; else holdEffect = ItemId_GetHoldEffect(gBattleMons[battler].item); + gPotentialItemEffectBattler = battler; + if (holdEffect == HOLD_EFFECT_CAN_ALWAYS_RUN) { gLastUsedItem = gBattleMons[battler].item; gProtectStructs[battler].fleeType = FLEE_ITEM; effect++; } - else if (gBattleMons[battler].ability == ABILITY_RUN_AWAY) + else if (B_GHOSTS_ESCAPE >= GEN_6 && IS_BATTLER_OF_TYPE(battler, TYPE_GHOST)) + { + effect++; + } + else if (GetBattlerAbility(battler) == ABILITY_RUN_AWAY) { gLastUsedAbility = ABILITY_RUN_AWAY; gProtectStructs[battler].fleeType = FLEE_ABILITY; @@ -394,27 +400,30 @@ bool8 TryRunFromBattle(u8 battler) } else { - if (!(gBattleTypeFlags & BATTLE_TYPE_DOUBLE)) + u8 runningFromBattler = BATTLE_OPPOSITE(battler); + if (!IsBattlerAlive(runningFromBattler)) + runningFromBattler |= BIT_FLANK; + + if (gBattleMons[battler].speed < gBattleMons[runningFromBattler].speed) { - if (gBattleMons[battler].speed < gBattleMons[BATTLE_OPPOSITE(battler)].speed) - { - speedVar = (gBattleMons[battler].speed * 128) / (gBattleMons[BATTLE_OPPOSITE(battler)].speed) + (gBattleStruct->runTries * 30); - if (speedVar > (Random() & 0xFF)) - effect++; - } - else // same speed or faster - { + speedVar = (gBattleMons[battler].speed * 128) / (gBattleMons[runningFromBattler].speed) + (gBattleStruct->runTries * 30); + if (speedVar > (Random() & 0xFF)) effect++; - } + } + else // same speed or faster + { + effect++; } - ++gBattleStruct->runTries; + gBattleStruct->runTries++; } + if (effect != 0) { gCurrentTurnActionNumber = gBattlersCount; gBattleOutcome = B_OUTCOME_RAN; } + return effect; } diff --git a/src/party_menu.c b/src/party_menu.c index 18bdb52be..e4bef1d29 100644 --- a/src/party_menu.c +++ b/src/party_menu.c @@ -6325,10 +6325,10 @@ void ChooseMonForWirelessMinigame(void) static u8 GetPartyLayoutFromBattleType(void) { - if (IsDoubleBattle() == FALSE) - return PARTY_LAYOUT_SINGLE; if (IsMultiBattle() == TRUE) return PARTY_LAYOUT_MULTI; + if (!IsDoubleBattle() || gPlayerPartyCount == 1) + return PARTY_LAYOUT_SINGLE; return PARTY_LAYOUT_DOUBLE; } @@ -6578,12 +6578,12 @@ static void BufferBattlePartyOrderBySide(u8 *partyBattleOrder, u8 flankId, u8 ba { j = 1; partyIndexes[0] = gBattlerPartyIndexes[leftBattler]; - for (i = 0; i < PARTY_SIZE; ++i) + for (i = 0; i < PARTY_SIZE; i++) { if (i != partyIndexes[0]) { partyIndexes[j] = i; - ++j; + j++; } } } @@ -6592,16 +6592,16 @@ static void BufferBattlePartyOrderBySide(u8 *partyBattleOrder, u8 flankId, u8 ba j = 2; partyIndexes[0] = gBattlerPartyIndexes[leftBattler]; partyIndexes[1] = gBattlerPartyIndexes[rightBattler]; - for (i = 0; i < PARTY_SIZE; ++i) + for (i = 0; i < PARTY_SIZE; i++) { if (i != partyIndexes[0] && i != partyIndexes[1]) { partyIndexes[j] = i; - ++j; + j++; } } } - for (i = 0; i < 3; ++i) + for (i = 0; i < 3; i++) partyBattleOrder[i] = (partyIndexes[0 + (i * 2)] << 4) | partyIndexes[1 + (i * 2)]; } diff --git a/src/pokeball.c b/src/pokeball.c index 04e6c2dc6..e9661edd6 100644 --- a/src/pokeball.c +++ b/src/pokeball.c @@ -672,8 +672,10 @@ static void SpriteCB_BallThrow_Shake(struct Sprite *sprite) #define tCryTaskSpecies data[0] #define tCryTaskPan data[1] #define tCryTaskWantedCry data[2] -#define tCryTaskMonPtr1 data[3] -#define tCryTaskMonPtr2 data[4] +#define tCryTaskBattler data[3] +#define tCryTaskMonSpriteId data[4] +#define tCryTaskMonPtr1 data[5] +#define tCryTaskMonPtr2 data[6] #define tCryTaskFrames data[10] #define tCryTaskState data[15] @@ -682,15 +684,15 @@ static void Task_PlayCryWhenReleasedFromBall(u8 taskId) u8 wantedCry = gTasks[taskId].tCryTaskWantedCry; s8 pan = gTasks[taskId].tCryTaskPan; u16 species = gTasks[taskId].tCryTaskSpecies; + u8 battlerId = gTasks[taskId].tCryTaskBattler; + u8 monSpriteId = gTasks[taskId].tCryTaskMonSpriteId; struct Pokemon *mon = (void *)(u32)((gTasks[taskId].tCryTaskMonPtr1 << 16) | (u16)(gTasks[taskId].tCryTaskMonPtr2)); switch (gTasks[taskId].tCryTaskState) { case 0: default: - if (gTasks[taskId].data[8] < 3) - gTasks[taskId].data[8]++; - else + if (gSprites[monSpriteId].affineAnimEnded) gTasks[taskId].tCryTaskState = wantedCry + 1; break; case 1: @@ -699,7 +701,7 @@ static void Task_PlayCryWhenReleasedFromBall(u8 taskId) PlayCry_ByMode(species, pan, CRY_MODE_NORMAL); else PlayCry_ByMode(species, pan, CRY_MODE_WEAK); - + gBattleSpritesDataPtr->healthBoxesData[battlerId].waitForCry = FALSE; DestroyTask(taskId); break; case 2: @@ -716,6 +718,7 @@ static void Task_PlayCryWhenReleasedFromBall(u8 taskId) else PlayCry_ReleaseDouble(species, pan, CRY_MODE_WEAK_DOUBLES); + gBattleSpritesDataPtr->healthBoxesData[battlerId].waitForCry = FALSE; DestroyTask(taskId); } else @@ -755,6 +758,7 @@ static void Task_PlayCryWhenReleasedFromBall(u8 taskId) else PlayCry_ReleaseDouble(species, pan, CRY_MODE_WEAK); + gBattleSpritesDataPtr->healthBoxesData[battlerId].waitForCry = FALSE; DestroyTask(taskId); break; } @@ -773,7 +777,7 @@ static void SpriteCB_ReleaseMonFromBall(struct Sprite *sprite) if (gMain.inBattle) { - struct Pokemon *mon; + struct Pokemon *mon, *illusionMon; u16 species; s8 pan; u16 wantedCryCase; @@ -812,10 +816,20 @@ static void SpriteCB_ReleaseMonFromBall(struct Sprite *sprite) else wantedCryCase = 2; + gBattleSpritesDataPtr->healthBoxesData[battlerId].waitForCry = TRUE; + taskId = CreateTask(Task_PlayCryWhenReleasedFromBall, 3); - gTasks[taskId].tCryTaskSpecies = species; + + illusionMon = GetIllusionMonPtr(battlerId); + if (illusionMon != NULL) + gTasks[taskId].tCryTaskSpecies = GetMonData(illusionMon, MON_DATA_SPECIES); + else + gTasks[taskId].tCryTaskSpecies = GetMonData(mon, MON_DATA_SPECIES); + gTasks[taskId].tCryTaskPan = pan; gTasks[taskId].tCryTaskWantedCry = wantedCryCase; + gTasks[taskId].tCryTaskBattler = battlerId; + gTasks[taskId].tCryTaskMonSpriteId = gBattlerSpriteIds[sprite->sBattler]; gTasks[taskId].tCryTaskMonPtr1 = (u32)(mon) >> 16; gTasks[taskId].tCryTaskMonPtr2 = (u32)(mon); gTasks[taskId].tCryTaskState = 0; @@ -829,6 +843,8 @@ static void SpriteCB_ReleaseMonFromBall(struct Sprite *sprite) #undef tCryTaskSpecies #undef tCryTaskPan #undef tCryTaskWantedCry +#undef tCryTaskBattler +#undef tCryTaskMonSpriteId #undef tCryTaskMonPtr1 #undef tCryTaskMonPtr2 #undef tCryTaskFrames diff --git a/src/pokemon.c b/src/pokemon.c index 0e227783c..074dcabb9 100644 --- a/src/pokemon.c +++ b/src/pokemon.c @@ -5317,13 +5317,6 @@ u16 SpeciesToPokedexNum(u16 species) return species > 0 ? species : 0xFFFF; } -void ClearBattleMonForms(void) -{ - int i; - for (i = 0; i < MAX_BATTLERS_COUNT; i++) - gBattleMonForms[i] = 0; -} - static u16 GetBattleBGM(void) { if (gBattleTypeFlags & BATTLE_TYPE_KYOGRE_GROUDON) diff --git a/src/reshow_battle_screen.c b/src/reshow_battle_screen.c index 7ff849ee9..f43a22daf 100644 --- a/src/reshow_battle_screen.c +++ b/src/reshow_battle_screen.c @@ -236,7 +236,7 @@ static void CreateBattlerSprite(u8 battler) gSprites[gBattlerSpriteIds[battler]].callback = SpriteCallbackDummy; gSprites[gBattlerSpriteIds[battler]].data[0] = battler; gSprites[gBattlerSpriteIds[battler]].data[2] = GetMonData(&gEnemyParty[gBattlerPartyIndexes[battler]], MON_DATA_SPECIES); - StartSpriteAnim(&gSprites[gBattlerSpriteIds[battler]], gBattleMonForms[battler]); + StartSpriteAnim(&gSprites[gBattlerSpriteIds[battler]], 0); } else if (gBattleTypeFlags & BATTLE_TYPE_SAFARI && battler == B_POSITION_PLAYER_LEFT) { @@ -270,7 +270,7 @@ static void CreateBattlerSprite(u8 battler) gSprites[gBattlerSpriteIds[battler]].callback = SpriteCallbackDummy; gSprites[gBattlerSpriteIds[battler]].data[0] = battler; gSprites[gBattlerSpriteIds[battler]].data[2] = GetMonData(&gPlayerParty[gBattlerPartyIndexes[battler]], MON_DATA_SPECIES); - StartSpriteAnim(&gSprites[gBattlerSpriteIds[battler]], gBattleMonForms[battler]); + StartSpriteAnim(&gSprites[gBattlerSpriteIds[battler]], 0); } gSprites[gBattlerSpriteIds[battler]].invisible = gBattleSpritesDataPtr->battlerData[battler].invisible; } @@ -306,9 +306,10 @@ static void CreateHealthboxSprite(u8 battler) if (GetMonData(&gEnemyParty[gBattlerPartyIndexes[battler]], MON_DATA_HP) == 0) SetHealthboxSpriteInvisible(healthboxSpriteId); } - else if (!(gBattleTypeFlags & BATTLE_TYPE_SAFARI) && GetMonData(&gPlayerParty[gBattlerPartyIndexes[battler]], MON_DATA_HP) == 0) - { - SetHealthboxSpriteInvisible(healthboxSpriteId); + else if (!(gBattleTypeFlags & BATTLE_TYPE_SAFARI)) + { + if (!IsValidForBattle(&gPlayerParty[gBattlerPartyIndexes[battler]])) + SetHealthboxSpriteInvisible(healthboxSpriteId); } } } diff --git a/src/text.c b/src/text.c index 76739f8c6..a196ca33d 100644 --- a/src/text.c +++ b/src/text.c @@ -1179,7 +1179,7 @@ s32 GetStringWidth(u8 fontId, const u8 *str, s16 letterSpacing) return width; } -u8 RenderTextHandleBold(u8 *pixels, u8 fontId, u8 *str, int a3, int a4, int a5, int a6, int a7) +u8 RenderTextHandleBold(u8 *pixels, u8 fontId, u8 *str) { u8 shadowColor; u8 *strLocal; diff --git a/src/wild_encounter.c b/src/wild_encounter.c index 15d66cf8e..07e68c198 100644 --- a/src/wild_encounter.c +++ b/src/wild_encounter.c @@ -13,6 +13,7 @@ #include "script.h" #include "link.h" #include "quest_log.h" +#include "safari_zone.h" #include "constants/maps.h" #include "constants/abilities.h" #include "constants/items.h" @@ -394,7 +395,17 @@ bool8 StandardWildEncounter(u32 currMetatileAttrs, u16 previousMetatileBehavior) // try a regular wild land encounter if (TryGenerateWildMon(gWildMonHeaders[headerId].landMonsInfo, WILD_AREA_LAND, WILD_CHECK_REPEL) == TRUE) { - StartWildBattle(); + if (TryDoDoubleWildBattle()) + { + struct Pokemon mon1 = gEnemyParty[0]; + TryGenerateWildMon(gWildMonHeaders[headerId].landMonsInfo, WILD_AREA_LAND, WILD_CHECK_KEEN_EYE); + gEnemyParty[1] = mon1; + StartDoubleWildBattle(); + } + else + { + StartWildBattle(); + } return TRUE; } else @@ -432,7 +443,17 @@ bool8 StandardWildEncounter(u32 currMetatileAttrs, u16 previousMetatileBehavior) if (TryGenerateWildMon(gWildMonHeaders[headerId].waterMonsInfo, WILD_AREA_WATER, WILD_CHECK_REPEL) == TRUE) { gIsSurfingEncounter = TRUE; - StartWildBattle(); + if (TryDoDoubleWildBattle()) + { + struct Pokemon mon1 = gEnemyParty[0]; + TryGenerateWildMon(gWildMonHeaders[headerId].waterMonsInfo, WILD_AREA_WATER, WILD_CHECK_KEEN_EYE); + gEnemyParty[1] = mon1; + StartDoubleWildBattle(); + } + else + { + StartWildBattle(); + } return TRUE; } else @@ -786,3 +807,15 @@ static void AddToWildEncounterRateBuff(u8 encounterRate) else sWildEncounterData.encounterRateBuff = 0; } + +bool8 TryDoDoubleWildBattle(void) +{ + if (GetSafariZoneFlag() + || (B_DOUBLE_WILD_REQUIRE_2_MONS == TRUE && GetMonsStateToDoubles() != PLAYER_HAS_TWO_USABLE_MONS)) + return FALSE; + else if (B_FLAG_FORCE_DOUBLE_WILD != 0 && FlagGet(B_FLAG_FORCE_DOUBLE_WILD)) + return TRUE; + else if (B_DOUBLE_WILD_CHANCE != 0 && ((Random() % 100) + 1 <= B_DOUBLE_WILD_CHANCE)) + return TRUE; + return FALSE; +}