diff --git a/data/battle_anim_scripts.s b/data/battle_anim_scripts.s index 157920bfe..b2dddb17b 100644 --- a/data/battle_anim_scripts.s +++ b/data/battle_anim_scripts.s @@ -786,6 +786,7 @@ gBattleAnims_Special:: .4byte Special_SafariBallThrow @ B_ANIM_SAFARI_BALL_THROW .4byte Special_SubstituteToMon @ B_ANIM_SUBSTITUTE_TO_MON .4byte Special_MonToSubstitute @ B_ANIM_MON_TO_SUBSTITUTE + .4byte Special_CriticalCaptureBallThrow @ B_ANIM_CRITICAL_CAPTURE_THROW Move_ROOST: loadspritegfx ANIM_TAG_WHITE_FEATHER @@ -24449,3 +24450,13 @@ Special_SubstituteToMon: Special_MonToSubstitute: createvisualtask AnimTask_SwapMonSpriteToFromSubstitute, 2, FALSE end + +Special_CriticalCaptureBallThrow: + createvisualtask AnimTask_LoadBallGfx, 2 + delay 0 + playsewithpan SE_RU_HYUU, 0 + createvisualtask AnimTask_ThrowBall, 2 + createvisualtask AnimTask_IsBallBlockedByTrainer, 2 + jumpreteq -1, BallThrowTrainerBlock + goto BallThrowEnd + diff --git a/include/battle.h b/include/battle.h index cb514f764..8d3acf954 100644 --- a/include/battle.h +++ b/include/battle.h @@ -642,7 +642,9 @@ struct BattleAnimationInfo u8 field_5; u8 field_6; u8 field_7; - u8 ballThrowCaseId; + u8 ballThrowCaseId:6; + u8 isCriticalCapture:1; + u8 criticalCaptureSuccess:1; u8 field_9_x1:1; u8 field_9_x2:1; u8 field_9_x1C:3; diff --git a/include/battle_anim.h b/include/battle_anim.h index 149e2e5d3..178458185 100644 --- a/include/battle_anim.h +++ b/include/battle_anim.h @@ -219,6 +219,7 @@ void sub_8172EF0(u8 battler, struct Pokemon *mon); u8 ItemIdToBallId(u16 itemId); u8 AnimateBallOpenParticles(u8 x, u8 y, u8 priority, u8 subpriority, u8 ballId); u8 LaunchBallFadeMonTask(bool8 unFadeLater, u8 battlerId, u32 selectedPalettes, u8 ballId); +bool32 IsCriticalCapture(void); // battle_anim_utility_funcs.c void sub_8116EB4(u8); diff --git a/include/constants/battle_anim.h b/include/constants/battle_anim.h index c320eeb67..b93cf65ca 100644 --- a/include/constants/battle_anim.h +++ b/include/constants/battle_anim.h @@ -533,6 +533,7 @@ #define B_ANIM_SAFARI_BALL_THROW 0x4 #define B_ANIM_SUBSTITUTE_TO_MON 0x5 #define B_ANIM_MON_TO_SUBSTITUTE 0x6 +#define B_ANIM_CRITICAL_CAPTURE_THROW 0x7 // status animation table #define B_ANIM_STATUS_PSN 0x0 diff --git a/src/battle_anim_special.c b/src/battle_anim_special.c index bf0d1d438..547d546d4 100755 --- a/src/battle_anim_special.c +++ b/src/battle_anim_special.c @@ -77,6 +77,7 @@ static void RepeatBallOpenParticleAnimation(u8); static void TimerBallOpenParticleAnimation(u8); static void PremierBallOpenParticleAnimation(u8); static void sub_817330C(struct Sprite *); +static void CB_CriticalCaptureThrownBallMovement(struct Sprite *sprite); struct BallCaptureSuccessStarData { @@ -923,7 +924,10 @@ static void sub_817138C(struct Sprite *sprite) angle = 0; sprite->pos1.y += Cos(angle, 40); sprite->pos2.y = -Cos(angle, sprite->data[4]); - sprite->callback = sub_81713D0; + if (IsCriticalCapture()) + sprite->callback = CB_CriticalCaptureThrownBallMovement; + else + sprite->callback = sub_81713D0; } } @@ -1116,22 +1120,38 @@ static void sub_8171520(struct Sprite *sprite) case 5: sprite->data[3] += 0x100; state = sprite->data[3] >> 8; - if (state == gBattleSpritesDataPtr->animationData->ballThrowCaseId) + if (IsCriticalCapture()) { - sprite->affineAnimPaused = 1; - sprite->callback = sub_81717B4; - } - else - { - if (gBattleSpritesDataPtr->animationData->ballThrowCaseId == BALL_3_SHAKES_SUCCESS && state == 3) + if (gBattleSpritesDataPtr->animationData->criticalCaptureSuccess) { sprite->callback = sub_81717D8; sprite->affineAnimPaused = 1; } else { - sprite->data[3]++; sprite->affineAnimPaused = 1; + sprite->callback = sub_81717B4; + } + } + else + { + if (state == gBattleSpritesDataPtr->animationData->ballThrowCaseId) + { + sprite->affineAnimPaused = 1; + sprite->callback = sub_81717B4; + } + else + { + if (gBattleSpritesDataPtr->animationData->ballThrowCaseId == BALL_3_SHAKES_SUCCESS && state == 3) + { + sprite->callback = sub_81717D8; + sprite->affineAnimPaused = 1; + } + else + { + sprite->data[3]++; + sprite->affineAnimPaused = 1; + } } } break; @@ -2271,3 +2291,53 @@ void AnimTask_GetBattlersFromArg(u8 taskId) gBattleAnimTarget = gBattleSpritesDataPtr->animationData->animArg >> 8; DestroyAnimVisualTask(taskId); } + +bool8 IsCriticalCapture(void) +{ + return gBattleSpritesDataPtr->animationData->isCriticalCapture; +} + +static void CB_CriticalCaptureThrownBallMovement(struct Sprite *sprite) +{ + bool8 lastBounce = FALSE; + u8 maxBounces = 6; + int bounceCount = sprite->data[3] >> 8; + + if (bounceCount == 0) + PlaySE(SE_BOWA); + + switch (sprite->data[3] & 0xFF) + { + case 0: + if (bounceCount < 3) + sprite->pos2.x++; + + if (++sprite->data[5] >= 3) + sprite->data[3] += 257; + + break; + case 1: + if (bounceCount < 3 || sprite->pos2.x != 0) + sprite->pos2.x--; + + if (--sprite->data[5] <= 0) + { + sprite->data[5] = 0; + sprite->data[3] &= -0x100; + } + + if (bounceCount >= maxBounces) + lastBounce = TRUE; + + break; + } + + if (lastBounce) + { + sprite->data[3] = 0; + sprite->data[4] = 40; //starting max height + sprite->data[5] = 0; + sprite->callback = sub_81713D0; + } +} + diff --git a/src/battle_controller_player.c b/src/battle_controller_player.c index 751e1ae6d..303932c18 100644 --- a/src/battle_controller_player.c +++ b/src/battle_controller_player.c @@ -2512,7 +2512,11 @@ static void PlayerHandleSuccessBallThrowAnim(void) { gBattleSpritesDataPtr->animationData->ballThrowCaseId = BALL_3_SHAKES_SUCCESS; gDoingBattleAnim = TRUE; - InitAndLaunchSpecialAnimation(gActiveBattler, gActiveBattler, gBattlerTarget, B_ANIM_BALL_THROW); + if (IsCriticalCapture()) + InitAndLaunchSpecialAnimation(gActiveBattler, gActiveBattler, gBattlerTarget, B_ANIM_CRITICAL_CAPTURE_THROW); + else + InitAndLaunchSpecialAnimation(gActiveBattler, gActiveBattler, gBattlerTarget, B_ANIM_BALL_THROW); + gBattlerControllerFuncs[gActiveBattler] = CompleteOnSpecialAnimDone; } @@ -2522,7 +2526,11 @@ static void PlayerHandleBallThrowAnim(void) gBattleSpritesDataPtr->animationData->ballThrowCaseId = ballThrowCaseId; gDoingBattleAnim = TRUE; - InitAndLaunchSpecialAnimation(gActiveBattler, gActiveBattler, gBattlerTarget, B_ANIM_BALL_THROW); + if (IsCriticalCapture()) + InitAndLaunchSpecialAnimation(gActiveBattler, gActiveBattler, gBattlerTarget, B_ANIM_CRITICAL_CAPTURE_THROW); + else + InitAndLaunchSpecialAnimation(gActiveBattler, gActiveBattler, gBattlerTarget, B_ANIM_BALL_THROW); + gBattlerControllerFuncs[gActiveBattler] = CompleteOnSpecialAnimDone; } diff --git a/src/battle_script_commands.c b/src/battle_script_commands.c index 296fde372..f4d60f51f 100644 --- a/src/battle_script_commands.c +++ b/src/battle_script_commands.c @@ -75,6 +75,7 @@ static void DrawLevelUpWindow2(void); static bool8 sub_804F344(void); static void PutMonIconOnLvlUpBox(void); static void PutLevelAndGenderOnLvlUpBox(void); +static bool32 CriticalCapture(u32 odds); static void SpriteCB_MonIconOnLvlUpBox(struct Sprite* sprite); @@ -11668,23 +11669,41 @@ static void Cmd_handleballthrow(void) else // mon may be caught, calculate shakes { u8 shakes; + u8 maxShakes; - odds = Sqrt(Sqrt(16711680 / odds)); - odds = 1048560 / odds; - - for (shakes = 0; shakes < BALL_3_SHAKES_SUCCESS && Random() < odds; shakes++); + gBattleSpritesDataPtr->animationData->isCriticalCapture = 0; //initialize + gBattleSpritesDataPtr->animationData->criticalCaptureSuccess = 0; + if (CriticalCapture(odds)) + { + maxShakes = 1; //critical capture doesn't gauarantee capture + gBattleSpritesDataPtr->animationData->isCriticalCapture = 1; + } + else + { + maxShakes = 4; + } if (gLastUsedItem == ITEM_MASTER_BALL) - shakes = BALL_3_SHAKES_SUCCESS; // why calculate the shakes before that check? + { + shakes = maxShakes; + } + else + { + odds = Sqrt(Sqrt(16711680 / odds)); + odds = 1048560 / odds; + for (shakes = 0; shakes < maxShakes && Random() < odds; shakes++); + } BtlController_EmitBallThrowAnim(0, shakes); MarkBattlerForControllerExec(gActiveBattler); - if (shakes == BALL_3_SHAKES_SUCCESS) // mon caught, copy of the code above + if (shakes == maxShakes) // mon caught, copy of the code above { + if (IsCriticalCapture()) + gBattleSpritesDataPtr->animationData->criticalCaptureSuccess = 1; + gBattlescriptCurrInstr = BattleScript_SuccessBallThrow; SetMonData(&gEnemyParty[gBattlerPartyIndexes[gBattlerTarget]], MON_DATA_POKEBALL, &gLastUsedItem); - if (CalculatePlayerPartyCount() == PARTY_SIZE) gBattleCommunication[MULTISTRING_CHOOSER] = 0; else @@ -11692,7 +11711,11 @@ static void Cmd_handleballthrow(void) } else // not caught { - gBattleCommunication[MULTISTRING_CHOOSER] = shakes; + if (IsCriticalCapture()) + gBattleCommunication[MULTISTRING_CHOOSER] = shakes + 3; + else + gBattleCommunication[MULTISTRING_CHOOSER] = shakes; + gBattlescriptCurrInstr = BattleScript_ShakeBallThrow; } } @@ -12094,3 +12117,28 @@ static void Cmd_metalburstdamagecalculator(void) gBattlescriptCurrInstr = T1_READ_PTR(gBattlescriptCurrInstr + 1); } } + +static bool32 CriticalCapture(u32 odds) +{ + u16 numCaught = GetNationalPokedexCount(FLAG_GET_CAUGHT); + + if (numCaught <= 30) + odds = 0; + else if (numCaught <= 150) + odds /= 2; + else if (numCaught <= 300) + ; + else if (numCaught <= 450) + odds = (odds * 150) / 100; + else if (numCaught <= 600) + odds *= 2; + else + odds = (odds * 250) / 100; + + odds /= 6; + if ((Random() % 255) < odds) + return TRUE; + + return FALSE; +} +