From 6ec21fcd8a4afbe2eb7c4a55746f6b98e69f8328 Mon Sep 17 00:00:00 2001 From: cawtds Date: Thu, 2 May 2024 12:24:35 +0200 Subject: [PATCH] updated up to Cmd_handleballthrow --- data/maps/ViridianCity_Mart/scripts.inc | 1 + include/battle.h | 6 +- include/battle_anim.h | 4 + include/constants/items.h | 85 +++++-- src/battle_anim.c | 9 + src/battle_main.c | 2 + src/battle_script_commands.c | 316 +++++++++++++++++++----- 7 files changed, 336 insertions(+), 87 deletions(-) diff --git a/data/maps/ViridianCity_Mart/scripts.inc b/data/maps/ViridianCity_Mart/scripts.inc index 8d542b227..5c2432f76 100644 --- a/data/maps/ViridianCity_Mart/scripts.inc +++ b/data/maps/ViridianCity_Mart/scripts.inc @@ -97,6 +97,7 @@ ViridianCity_Mart_Items:: .2byte ITEM_ABILITY_PATCH .2byte ITEM_STICKY_BARB .2byte ITEM_ORAN_BERRY + .2byte ITEM_BEAST_BALL .2byte ITEM_NONE release end diff --git a/include/battle.h b/include/battle.h index 0d3b0d9b6..ca7ef3f70 100644 --- a/include/battle.h +++ b/include/battle.h @@ -842,7 +842,9 @@ struct BattleAnimationInfo u8 field_5; u8 field_6; u8 field_7; - u8 ballThrowCaseId; + u8 ballThrowCaseId:6; + u8 isCriticalCapture:1; + u8 criticalCaptureSuccess:1; u8 introAnimActive:1; u8 wildMonInvisible:1; u8 field_9_x1C:3; @@ -1042,6 +1044,8 @@ extern u8 gIsCriticalHit; extern struct FieldTimer gFieldTimers; extern bool8 gHasFetchedBall; extern u8 gLastUsedBall; +extern u16 gLastThrownBall; +extern u16 gBallToDisplay; extern struct QueuedStatBoost gQueuedStatBoosts[MAX_BATTLERS_COUNT]; static inline u32 GetBattlerPosition(u32 battler) diff --git a/include/battle_anim.h b/include/battle_anim.h index 2b6ae290d..b535704fe 100644 --- a/include/battle_anim.h +++ b/include/battle_anim.h @@ -187,6 +187,10 @@ extern const u16 gMovesWithQuietBGM[]; u8 GetAnimBattlerId(u8 wantedBattler); +// battle_anim_throw.c +bool32 IsCriticalCapture(void); + +// void MoveBattlerSpriteToBG(u8 battlerId, u8); void ResetBattleAnimBg(u8); void ClearBattleAnimationVars(void); diff --git a/include/constants/items.h b/include/constants/items.h index ff11e1c4d..b41ef1248 100644 --- a/include/constants/items.h +++ b/include/constants/items.h @@ -4,39 +4,74 @@ #define ITEM_NONE 0 // Poké Balls -#define ITEM_MASTER_BALL 1 -#define ITEM_ULTRA_BALL 2 -#define ITEM_GREAT_BALL 3 -#define ITEM_POKE_BALL 4 -#define ITEM_SAFARI_BALL 5 -#define ITEM_NET_BALL 6 -#define ITEM_DIVE_BALL 7 +#define ITEM_POKE_BALL 1 +#define ITEM_GREAT_BALL 2 +#define ITEM_ULTRA_BALL 3 +#define ITEM_MASTER_BALL 4 +#define ITEM_PREMIER_BALL 5 +#define ITEM_HEAL_BALL 6 +#define ITEM_NET_BALL 7 #define ITEM_NEST_BALL 8 -#define ITEM_REPEAT_BALL 9 -#define ITEM_TIMER_BALL 10 -#define ITEM_LUXURY_BALL 11 -#define ITEM_PREMIER_BALL 12 -#define ITEM_HEAL_BALL 13 -#define ITEM_DUSK_BALL 14 -#define ITEM_QUICK_BALL 15 -#define ITEM_LEVEL_BALL 16 -#define ITEM_LURE_BALL 17 -#define ITEM_MOON_BALL 18 -#define ITEM_FRIEND_BALL 19 -#define ITEM_LOVE_BALL 20 -#define ITEM_FAST_BALL 21 -#define ITEM_HEAVY_BALL 22 -#define ITEM_DREAM_BALL 23 +#define ITEM_DIVE_BALL 9 +#define ITEM_DUSK_BALL 10 +#define ITEM_TIMER_BALL 11 +#define ITEM_QUICK_BALL 12 +#define ITEM_REPEAT_BALL 13 +#define ITEM_LUXURY_BALL 14 +#define ITEM_LEVEL_BALL 15 +#define ITEM_LURE_BALL 16 +#define ITEM_MOON_BALL 17 +#define ITEM_FRIEND_BALL 18 +#define ITEM_LOVE_BALL 19 +#define ITEM_FAST_BALL 20 +#define ITEM_HEAVY_BALL 21 +#define ITEM_DREAM_BALL 22 +#define ITEM_SAFARI_BALL 23 #define ITEM_SPORT_BALL 24 #define ITEM_PARK_BALL 25 #define ITEM_BEAST_BALL 26 -#define ITEM_CHERISH_BALL 27 +#define ITEM_CHERISH_BALL 27 // TODO: throwing CHERISH_BALL restarts game, intended? // Note: If moving ball IDs around, updating FIRST_BALL/LAST_BALL is not sufficient // Several places expect the ball IDs to be first and contiguous (e.g. MON_DATA_POKEBALL) // If adding new balls, it's easiest to insert them after the last ball and increment the below IDs (and removing ITEM_034 for example) -#define FIRST_BALL ITEM_MASTER_BALL -#define LAST_BALL ITEM_PREMIER_BALL +#define FIRST_BALL ITEM_POKE_BALL +#define LAST_BALL ITEM_CHERISH_BALL + +// // Poké Balls +// #define ITEM_MASTER_BALL 1 +// #define ITEM_ULTRA_BALL 2 +// #define ITEM_GREAT_BALL 3 +// #define ITEM_POKE_BALL 4 +// #define ITEM_SAFARI_BALL 5 +// #define ITEM_NET_BALL 6 +// #define ITEM_DIVE_BALL 7 +// #define ITEM_NEST_BALL 8 +// #define ITEM_REPEAT_BALL 9 +// #define ITEM_TIMER_BALL 10 +// #define ITEM_LUXURY_BALL 11 +// #define ITEM_PREMIER_BALL 12 +// #define ITEM_HEAL_BALL 13 +// #define ITEM_DUSK_BALL 14 +// #define ITEM_QUICK_BALL 15 +// #define ITEM_LEVEL_BALL 16 +// #define ITEM_LURE_BALL 17 +// #define ITEM_MOON_BALL 18 +// #define ITEM_FRIEND_BALL 19 +// #define ITEM_LOVE_BALL 20 +// #define ITEM_FAST_BALL 21 +// #define ITEM_HEAVY_BALL 22 +// #define ITEM_DREAM_BALL 23 +// #define ITEM_SPORT_BALL 24 +// #define ITEM_PARK_BALL 25 +// #define ITEM_BEAST_BALL 26 +// #define ITEM_CHERISH_BALL 27 + +// // Note: If moving ball IDs around, updating FIRST_BALL/LAST_BALL is not sufficient +// // Several places expect the ball IDs to be first and contiguous (e.g. MON_DATA_POKEBALL) +// // If adding new balls, it's easiest to insert them after the last ball and increment the below IDs (and removing ITEM_034 for example) +// #define FIRST_BALL ITEM_MASTER_BALL +// #define LAST_BALL ITEM_CHERISH_BALL // Medicine #define ITEM_POTION 28 diff --git a/src/battle_anim.c b/src/battle_anim.c index 00f702193..8aafce929 100644 --- a/src/battle_anim.c +++ b/src/battle_anim.c @@ -1741,3 +1741,12 @@ u8 GetAnimBattlerId(u8 wantedBattler) return wantedBattler - MAX_BATTLERS_COUNT; } } + +// battle_anim_throw.c + +bool32 IsCriticalCapture(void) +{ + return gBattleSpritesDataPtr->animationData->isCriticalCapture; +} + +// diff --git a/src/battle_main.c b/src/battle_main.c index 47464d26f..644e8fe0b 100644 --- a/src/battle_main.c +++ b/src/battle_main.c @@ -229,6 +229,8 @@ EWRAM_DATA u8 gIsCriticalHit = FALSE; EWRAM_DATA struct FieldTimer gFieldTimers = {0}; EWRAM_DATA bool8 gHasFetchedBall = FALSE; EWRAM_DATA u8 gLastUsedBall = 0; +EWRAM_DATA u16 gLastThrownBall = 0; +EWRAM_DATA u16 gBallToDisplay = 0; EWRAM_DATA struct QueuedStatBoost gQueuedStatBoosts[MAX_BATTLERS_COUNT] = {0}; void (*gPreBattleCallback1)(void); diff --git a/src/battle_script_commands.c b/src/battle_script_commands.c index 7434d6a1b..3efe9354d 100644 --- a/src/battle_script_commands.c +++ b/src/battle_script_commands.c @@ -340,6 +340,7 @@ static bool8 CanBurnHitThaw(u16 move); static bool32 ChangeOrderTargetAfterAttacker(void); static void TryUpdateEvolutionTracker(u32 evolutionMethod, u32 upAmount); static bool8 CanAbilityPreventStatLoss(u16 abilityDef, bool8 isIntimidate); +static bool32 CriticalCapture(u32 odds); static void Cmd_attackcanceler(void); static void Cmd_accuracycheck(void); @@ -832,7 +833,7 @@ void (* const gBattleScriptingCommandsTable[])(void) = Cmd_pursuitdoubles, //0xEC // done Cmd_snatchsetbattlers, //0xED // done Cmd_removelightscreenreflect, //0xEE // done - Cmd_handleballthrow, //0xEF + Cmd_handleballthrow, //0xEF // done Cmd_givecaughtmon, //0xF0 Cmd_trysetcaughtmondexflags, //0xF1 Cmd_displaydexinfo, //0xF2 @@ -1182,15 +1183,6 @@ static const u16 sWeightToDamageTable[] = 0xFFFF, 0xFFFF }; -// - ITEM_ULTRA_BALL skips Master Ball and ITEM_NONE -static const u8 sBallCatchBonuses[] = -{ - [ITEM_ULTRA_BALL - ITEM_ULTRA_BALL] = 20, - [ITEM_GREAT_BALL - ITEM_ULTRA_BALL] = 15, - [ITEM_POKE_BALL - ITEM_ULTRA_BALL] = 10, - [ITEM_SAFARI_BALL - ITEM_ULTRA_BALL] = 15 -}; - static bool32 NoTargetPresent(u8 battler, u32 move) { if (!IsBattlerAlive(gBattlerTarget)) @@ -15328,12 +15320,16 @@ u8 GetCatchingBattler(void) static void Cmd_handleballthrow(void) { - u8 ballMultiplier = 0; + CMD_ARGS(); + + u16 ballMultiplier = 100; + s8 ballAddition = 0; + if (gBattleControllerExecFlags) return; gActiveBattler = gBattlerAttacker; - gBattlerTarget = gBattlerAttacker ^ BIT_SIDE; + gBattlerTarget = GetCatchingBattler(); if (gBattleTypeFlags & BATTLE_TYPE_GHOST) { @@ -15344,7 +15340,7 @@ static void Cmd_handleballthrow(void) else if (gBattleTypeFlags & BATTLE_TYPE_TRAINER) { BtlController_EmitBallThrowAnim(BUFFER_A, BALL_TRAINER_BLOCK); - MarkBattlerForControllerExec(gActiveBattler); + MarkBattlerForControllerExec(gBattlerAttacker); gBattlescriptCurrInstr = BattleScript_TrainerBallBlock; } else if (gBattleTypeFlags & (BATTLE_TYPE_POKEDUDE | BATTLE_TYPE_OLD_MAN_TUTORIAL)) @@ -15355,88 +15351,209 @@ static void Cmd_handleballthrow(void) } else { - u32 odds; + u32 odds, i; u8 catchRate; - if (gLastUsedItem == ITEM_SAFARI_BALL) + gLastThrownBall = gLastUsedItem; + gBallToDisplay = gLastThrownBall; + if (gBattleTypeFlags & BATTLE_TYPE_SAFARI) catchRate = gBattleStruct->safariCatchFactor * 1275 / 100; else catchRate = gSpeciesInfo[gBattleMons[gBattlerTarget].species].catchRate; - if (gLastUsedItem > ITEM_SAFARI_BALL) + if (gSpeciesInfo[gBattleMons[gBattlerTarget].species].isUltraBeast) + { + if (gLastUsedItem == ITEM_BEAST_BALL) + ballMultiplier = 500; + else + ballMultiplier = 10; + } + else { switch (gLastUsedItem) { + case ITEM_ULTRA_BALL: + ballMultiplier = 200; + break; + case ITEM_SPORT_BALL: + if (B_SPORT_BALL_MODIFIER <= GEN_7) + ballMultiplier = 150; + break; + case ITEM_GREAT_BALL: + ballMultiplier = 150; + break; + case ITEM_SAFARI_BALL: + if (B_SAFARI_BALL_MODIFIER <= GEN_7) + ballMultiplier = 150; + break; case ITEM_NET_BALL: if (IS_BATTLER_OF_TYPE(gBattlerTarget, TYPE_WATER) || IS_BATTLER_OF_TYPE(gBattlerTarget, TYPE_BUG)) - ballMultiplier = 30; - else - ballMultiplier = 10; + ballMultiplier = B_NET_BALL_MODIFIER >= GEN_7 ? 350 : 300; break; case ITEM_DIVE_BALL: - if (GetCurrentMapType() == MAP_TYPE_UNDERWATER) - ballMultiplier = 35; - else - ballMultiplier = 10; + // TODO: gIsFishingEncounter and gIsSurfingEncounter + // if (GetCurrentMapType() == MAP_TYPE_UNDERWATER + // || (B_DIVE_BALL_MODIFIER >= GEN_4 && (gIsFishingEncounter || gIsSurfingEncounter))) + // ballMultiplier = 350; break; case ITEM_NEST_BALL: - if (gBattleMons[gBattlerTarget].level < 40) + if (B_NEST_BALL_MODIFIER >= GEN_6) { - ballMultiplier = 40 - gBattleMons[gBattlerTarget].level; - if (ballMultiplier <= 9) - ballMultiplier = 10; + //((41 - Pokémon's level) ÷ 10)× if Pokémon's level is between 1 and 29, 1× otherwise. + if (gBattleMons[gBattlerTarget].level < 30) + ballMultiplier = 410 - (gBattleMons[gBattlerTarget].level * 10); } - else + else if (B_NEST_BALL_MODIFIER >= GEN_5) { - ballMultiplier = 10; + //((41 - Pokémon's level) ÷ 10)×, minimum 1× + if (gBattleMons[gBattlerTarget].level < 31) + ballMultiplier = 410 - (gBattleMons[gBattlerTarget].level * 10); + } + else if (gBattleMons[gBattlerTarget].level < 40) + { + //((40 - Pokémon's level) ÷ 10)×, minimum 1× + ballMultiplier = 400 - (gBattleMons[gBattlerTarget].level * 10); + if (ballMultiplier <= 90) + ballMultiplier = 100; } break; case ITEM_REPEAT_BALL: if (GetSetPokedexFlag(SpeciesToNationalPokedexNum(gBattleMons[gBattlerTarget].species), FLAG_GET_CAUGHT)) - ballMultiplier = 30; - else - ballMultiplier = 10; + ballMultiplier = (B_REPEAT_BALL_MODIFIER >= GEN_7 ? 350 : 300); break; case ITEM_TIMER_BALL: - ballMultiplier = gBattleResults.battleTurnCounter + 10; - if (ballMultiplier > 40) - ballMultiplier = 40; + ballMultiplier = 100 + (gBattleResults.battleTurnCounter * (B_TIMER_BALL_MODIFIER >= GEN_5 ? 30 : 10)); + if (ballMultiplier > 400) + ballMultiplier = 400; break; - case ITEM_LUXURY_BALL: - case ITEM_PREMIER_BALL: + case ITEM_DUSK_BALL: + // TODO: RTC + // i = GetTimeOfDay(); + // if (i == TIME_EVENING || i == TIME_NIGHT || gMapHeader.cave || gMapHeader.mapType == MAP_TYPE_UNDERGROUND) + // ballMultiplier = (B_DUSK_BALL_MODIFIER >= GEN_7 ? 300 : 350); + break; + case ITEM_QUICK_BALL: + if (gBattleResults.battleTurnCounter == 0) + ballMultiplier = (B_QUICK_BALL_MODIFIER >= GEN_5 ? 500 : 400); + break; + case ITEM_LEVEL_BALL: + if (gBattleMons[gBattlerAttacker].level >= 4 * gBattleMons[gBattlerTarget].level) + ballMultiplier = 800; + else if (gBattleMons[gBattlerAttacker].level > 2 * gBattleMons[gBattlerTarget].level) + ballMultiplier = 400; + else if (gBattleMons[gBattlerAttacker].level > gBattleMons[gBattlerTarget].level) + ballMultiplier = 200; + break; + case ITEM_LURE_BALL: + // TODO: gIsFishingEncounter + // if (gIsFishingEncounter) + // { + // if (B_LURE_BALL_MODIFIER >= GEN_8) + // ballMultiplier = 400; + // else if (B_LURE_BALL_MODIFIER >= GEN_7) + // ballMultiplier = 500; + // else + // ballMultiplier = 300; + // } + break; + case ITEM_MOON_BALL: + { + const struct Evolution *evolutions = GetSpeciesEvolutions(gBattleMons[gBattlerTarget].species); + if (evolutions == NULL) + break; + for (i = 0; evolutions[i].method != EVOLUTIONS_END; i++) + { + if (evolutions[i].method == EVO_ITEM + && evolutions[i].param == ITEM_MOON_STONE) + ballMultiplier = 400; + } + } + break; + case ITEM_LOVE_BALL: + if (gBattleMons[gBattlerTarget].species == gBattleMons[gBattlerAttacker].species) + { + u8 gender1 = GetMonGender(&gEnemyParty[gBattlerPartyIndexes[gBattlerTarget]]); + u8 gender2 = GetMonGender(&gPlayerParty[gBattlerPartyIndexes[gBattlerAttacker]]); + + if (gender1 != gender2 && gender1 != MON_GENDERLESS && gender2 != MON_GENDERLESS) + ballMultiplier = 800; + } + break; + case ITEM_FAST_BALL: + if (gSpeciesInfo[gBattleMons[gBattlerTarget].species].baseSpeed >= 100) + ballMultiplier = 400; + break; + case ITEM_HEAVY_BALL: + i = GetSpeciesWeight(gBattleMons[gBattlerTarget].species); + if (B_HEAVY_BALL_MODIFIER >= GEN_7) + { + if (i < 1000) + ballAddition = -20; + else if (i < 2000) + ballAddition = 0; + else if (i < 3000) + ballAddition = 20; + else + ballAddition = 30; + } + else if (B_HEAVY_BALL_MODIFIER >= GEN_4) + { + if (i < 2048) + ballAddition = -20; + else if (i < 3072) + ballAddition = 20; + else if (i < 4096) + ballAddition = 30; + else + ballAddition = 40; + } + else + { + if (i < 1024) + ballAddition = -20; + else if (i < 2048) + ballAddition = 0; + else if (i < 3072) + ballAddition = 20; + else if (i < 4096) + ballAddition = 30; + else + ballAddition = 40; + } + break; + case ITEM_DREAM_BALL: + if (B_DREAM_BALL_MODIFIER >= GEN_8 && (gBattleMons[gBattlerTarget].status1 & STATUS1_SLEEP || GetBattlerAbility(gBattlerTarget) == ABILITY_COMATOSE)) + ballMultiplier = 400; + break; + case ITEM_BEAST_BALL: ballMultiplier = 10; break; } } - else - ballMultiplier = sBallCatchBonuses[gLastUsedItem - ITEM_ULTRA_BALL]; - odds = (catchRate * ballMultiplier / 10) + // catchRate is unsigned, which means that it may potentially overflow if sum is applied directly. + if (catchRate < 21 && ballAddition == -20) + catchRate = 1; + else + catchRate = catchRate + ballAddition; + + odds = (catchRate * ballMultiplier / 100) * (gBattleMons[gBattlerTarget].maxHP * 3 - gBattleMons[gBattlerTarget].hp * 2) / (3 * gBattleMons[gBattlerTarget].maxHP); if (gBattleMons[gBattlerTarget].status1 & (STATUS1_SLEEP | STATUS1_FREEZE)) odds *= 2; - if (gBattleMons[gBattlerTarget].status1 & (STATUS1_POISON | STATUS1_BURN | STATUS1_PARALYSIS | STATUS1_TOXIC_POISON)) + if (gBattleMons[gBattlerTarget].status1 & (STATUS1_POISON | STATUS1_BURN | STATUS1_PARALYSIS | STATUS1_TOXIC_POISON | STATUS1_FROSTBITE)) odds = (odds * 15) / 10; - if (gLastUsedItem != ITEM_SAFARI_BALL) - { - if (gLastUsedItem == ITEM_MASTER_BALL) - { - gBattleResults.usedMasterBall = TRUE; - } - else - { - if (gBattleResults.catchAttempts[gLastUsedItem - ITEM_ULTRA_BALL] < 255) - gBattleResults.catchAttempts[gLastUsedItem - ITEM_ULTRA_BALL]++; - } - } + if (gBattleResults.catchAttempts[gLastUsedItem - FIRST_BALL] < 255) + gBattleResults.catchAttempts[gLastUsedItem - FIRST_BALL]++; if (odds > 254) // mon caught { BtlController_EmitBallThrowAnim(BUFFER_A, BALL_3_SHAKES_SUCCESS); - MarkBattlerForControllerExec(gActiveBattler); + MarkBattlerForControllerExec(gBattlerAttacker); + TryBattleFormChange(gBattlerTarget, FORM_CHANGE_END_BATTLE); gBattlescriptCurrInstr = BattleScript_SuccessBallThrow; SetMonData(&gEnemyParty[gBattlerPartyIndexes[gBattlerTarget]], MON_DATA_POKEBALL, &gLastUsedItem); @@ -15444,24 +15561,54 @@ static void Cmd_handleballthrow(void) gBattleCommunication[MULTISTRING_CHOOSER] = 0; else gBattleCommunication[MULTISTRING_CHOOSER] = 1; + + if (gLastUsedItem == ITEM_HEAL_BALL) + { + MonRestorePP(&gEnemyParty[gBattlerPartyIndexes[gBattlerTarget]]); + HealStatusConditions(&gEnemyParty[gBattlerPartyIndexes[gBattlerTarget]], STATUS1_ANY, gBattlerTarget); + gBattleMons[gBattlerTarget].hp = gBattleMons[gBattlerTarget].maxHP; + SetMonData(&gEnemyParty[gBattlerPartyIndexes[gBattlerTarget]], MON_DATA_HP, &gBattleMons[gBattlerTarget].hp); + } } else // mon may be caught, calculate shakes { u8 shakes; + u8 maxShakes; - odds = Sqrt(Sqrt(16711680 / odds)); - odds = 1048560 / odds; + // TODO: critical capture + gBattleSpritesDataPtr->animationData->isCriticalCapture = FALSE; + gBattleSpritesDataPtr->animationData->criticalCaptureSuccess = FALSE; - for (shakes = 0; shakes < BALL_3_SHAKES_SUCCESS && Random() < odds; shakes++); + if (CriticalCapture(odds)) + { + maxShakes = BALL_1_SHAKE; // critical capture doesn't guarantee capture + gBattleSpritesDataPtr->animationData->isCriticalCapture = TRUE; + } + else + { + maxShakes = BALL_3_SHAKES_SUCCESS; + } 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(BUFFER_A, shakes); - MarkBattlerForControllerExec(gActiveBattler); + MarkBattlerForControllerExec(gBattlerAttacker); - 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 = TRUE; + + TryBattleFormChange(gBattlerTarget, FORM_CHANGE_END_BATTLE); gBattlescriptCurrInstr = BattleScript_SuccessBallThrow; SetMonData(&gEnemyParty[gBattlerPartyIndexes[gBattlerTarget]], MON_DATA_POKEBALL, &gLastUsedItem); @@ -15469,10 +15616,25 @@ static void Cmd_handleballthrow(void) gBattleCommunication[MULTISTRING_CHOOSER] = 0; else gBattleCommunication[MULTISTRING_CHOOSER] = 1; + + if (gLastUsedItem == ITEM_HEAL_BALL) + { + MonRestorePP(&gEnemyParty[gBattlerPartyIndexes[gBattlerTarget]]); + HealStatusConditions(&gEnemyParty[gBattlerPartyIndexes[gBattlerTarget]], STATUS1_ANY, gBattlerTarget); + gBattleMons[gBattlerTarget].hp = gBattleMons[gBattlerTarget].maxHP; + SetMonData(&gEnemyParty[gBattlerPartyIndexes[gBattlerTarget]], MON_DATA_HP, &gBattleMons[gBattlerTarget].hp); + } } else // not caught { - gBattleCommunication[MULTISTRING_CHOOSER] = shakes; + if (!gHasFetchedBall) + gLastUsedBall = gLastUsedItem; + + if (IsCriticalCapture()) + gBattleCommunication[MULTISTRING_CHOOSER] = BALL_3_SHAKES_FAIL; + else + gBattleCommunication[MULTISTRING_CHOOSER] = shakes; + gBattlescriptCurrInstr = BattleScript_ShakeBallThrow; } } @@ -15958,6 +16120,38 @@ void BS_RunStatChangeItems(void) ItemBattleEffects(ITEMEFFECT_STATS_CHANGED, GetBattlerForBattleScript(cmd->battler), FALSE); // TODO: update } +static bool32 CriticalCapture(u32 odds) +{ + u32 numCaught; + + if (B_CRITICAL_CAPTURE == FALSE) + return FALSE; + + numCaught = GetNationalPokedexCount(FLAG_GET_CAUGHT); + + if (numCaught <= (NATIONAL_DEX_COUNT * 30) / 650) + odds = 0; + else if (numCaught <= (NATIONAL_DEX_COUNT * 150) / 650) + odds /= 2; + else if (numCaught <= (NATIONAL_DEX_COUNT * 300) / 650) + ; // odds = (odds * 100) / 100; + else if (numCaught <= (NATIONAL_DEX_COUNT * 450) / 650) + odds = (odds * 150) / 100; + else if (numCaught <= (NATIONAL_DEX_COUNT * 600) / 650) + odds *= 2; + else + odds = (odds * 250) / 100; + + if (CheckBagHasItem(ITEM_CATCHING_CHARM, 1)) + odds = (odds * (100 + B_CATCHING_CHARM_BOOST)) / 100; + + odds /= 6; + if ((Random() % 255) < odds) + return TRUE; + + return FALSE; +} + bool32 IsMoveAffectedByParentalBond(u32 move, u32 battler) { if (move != MOVE_NONE && move != MOVE_UNAVAILABLE && move != MOVE_STRUGGLE