-
Notifications
You must be signed in to change notification settings - Fork 2.6k
Raise Odds of Catching a Pokemon by Pressing B When the Ball Shakes
By devolov
Goal: I had a fake rumor in grade school that said that if you press B when the ball is shaking then your odds of catching it go up. That's not true, but I want it to be true.
Below is the changes I made to allow it to occur. I had to break apart cmd_handleballthrow
and add a new global struct called BattleOddsModifierButtonPress gBallShakesBData
which holds the catch odds, the amount of wanted shakes, and a variable that's contains data on whether the B button was pressed when the ball shook, wasn't held when the current shake begins, and the current shake count.
Having a variable that checks for all of the the B presses and holds is not necessary with how I have the code now, since it only recalculates if the only last shake had both of those occur, but having all of that data may be useful in current work.
The current code does all of the ball shake calculations before playing the animation, this code changes the logic so that each ball shake is calculated after the previous one's animation.
The logic here for adding to the odds is that it compounds them continuously. As an example:
Say The ball shakes 3 times and I press B on the first and last shake and not the middle one.
If my initial odds are 50 and BALL_SHAKE_BUTTON_MULT
= 11,
Then:
1st Shake: 55
2nd Shake: 55
3rd Shake: 61
From 6da1c0115c7aa969fe7c7dcc02568bd4fb66b5a0
Added raising catch percent if left and B are held when transitioning back to battlefield
To baf5c81b5e12f8ccbdd7ae2c48baae0c76aa1b16
Merge branch 'B_shake_odds' of https://gitlab.com/devolov/pokeemerald_fork into B_shake_odds
------------------------- asm/macros/battle_script.inc -------------------------
index ccb887327..cb1dcdc96 100644
@@ -1252,8 +1252,12 @@
.byte 0xf8
.byte \position
.endm
+ .macro ballthrowend
+ .byte 0xf9
+ .endm
+
@ various command changed to more readable macros
.macro cancelmultiturnmoves battler:req
various \battler, VARIOUS_CANCEL_MULTI_TURN_MOVES
.endm
--------------------------- data/battle_scripts_2.s ---------------------------
index d1881fcf0..3b167e846 100644
@@ -51,8 +51,11 @@ BattleScript_BallThrow::
jumpifword CMP_COMMON_BITS, gBattleTypeFlags, BATTLE_TYPE_WALLY_TUTORIAL, BattleScript_BallThrowByWally
printstring STRINGID_PLAYERUSEDITEM
handleballthrow
+BattleScript_BallThrowEnd::
+ ballthrowend
+
BattleScript_BallThrowByWally::
printstring STRINGID_WALLYUSEDITEM
handleballthrow
------------------------------- include/battle.h -------------------------------
index 1cc5f829e..314d3e721 100644
@@ -645,8 +645,9 @@ extern s32 gBattleMoveDamage;
extern s32 gHpDealt;
extern s32 gTakenDmg[MAX_BATTLERS_COUNT];
extern u16 gLastUsedItem;
+extern struct BattleOddsModifierButtonPress gBallShakesBData;
extern u8 gLastUsedAbility;
extern u8 gBattlerAttacker;
extern u8 gBattlerTarget;
extern u8 gBattlerFainted;
@@ -717,6 +718,8 @@ extern void (*gBattlerControllerFuncs[MAX_BATTLERS_COUNT])(void);
extern u8 gHealthboxSpriteIds[MAX_BATTLERS_COUNT];
extern u8 gMultiUsePlayerCursor;
extern u8 gNumberOfMovesToChoose;
extern u8 gBattleControllerData[MAX_BATTLERS_COUNT];
+bool8 CalcNextShakeFromOdds(u32 odds);
#endif // GUARD_BATTLE_H
---------------------------- include/battle_main.h ----------------------------
index 18e397a20..edc818004 100644
@@ -21,8 +21,27 @@ struct MultiPartnerMenuPokemon
/*0x1C*/ u8 gender;
/*0x1D*/ u8 language;
};
+struct BattleOddsModifierButtonPress
+{
+ u8 ballShakesArray;
+ u32 odds;
+ u8 shakes;
+};
+
+/*
+ballShakesArray: 76543210
+76: The amount of shakes we've done
+ 5: If the B button was pressed on the 3rd shake
+ 4: If the B button was held just before the 3rd shake
+ 3: If the B button was pressed on the 2nd shake
+ 2: If the B button was held just before the 2nd shake
+ 1: If the B button was pressed on the 1st shake
+ 0: If the B button was held just before the 1st shake
+*/
+
+
// defines for the u8 array gTypeEffectiveness
#define TYPE_EFFECT_ATK_TYPE(i)((gTypeEffectiveness[i + 0]))
#define TYPE_EFFECT_DEF_TYPE(i)((gTypeEffectiveness[i + 1]))
#define TYPE_EFFECT_MULTIPLIER(i)((gTypeEffectiveness[i + 2]))
--------------------------- include/battle_scripts.h ---------------------------
index 7f3610f33..c5f3def92 100644
@@ -221,7 +221,8 @@ extern const u8 BattleScript_TrainerBallBlock[];
extern const u8 BattleScript_RunByUsingItem[];
extern const u8 BattleScript_ActionWatchesCarefully[];
extern const u8 BattleScript_ActionGetNear[];
extern const u8 BattleScript_ActionThrowPokeblock[];
+extern const u8 BattleScript_BallThrowEnd[];
#endif // GUARD_BATTLE_SCRIPTS_H
--------------------------- src/battle_anim_throw.c ---------------------------
index f0fcee89c..13c170140 100755
@@ -139,8 +139,11 @@ static const struct CaptureStar sCaptureStars[] =
#define TAG_PARTICLES_TIMERBALL 55029
#define TAG_PARTICLES_LUXURYBALL 55030
#define TAG_PARTICLES_PREMIERBALL 55031
+#define BALL_SHAKE_BUTTON_MULT 11 // 10 * (BALL_SHAKE_BUTTON_MULT - 10) is the amount we multiply
+// our catch odds with each time the B button is successfully pressed
+
static const struct CompressedSpriteSheet sBallParticleSpriteSheets[POKEBALL_COUNT] =
{
[BALL_POKE] = {gBattleAnimSpriteGfx_Particles, 0x100, TAG_PARTICLES_POKEBALL},
[BALL_GREAT] = {gBattleAnimSpriteGfx_Particles, 0x100, TAG_PARTICLES_GREATBALL},
@@ -1133,14 +1152,20 @@ static void SpriteCB_Ball_Wobble(struct Sprite *sprite)
#define RESET_STATE(state) (state &= -0x100)
static void SpriteCB_Ball_Wobble_Step(struct Sprite *sprite)
{
- s8 shakes;
+ s8 shakes = SHAKES(sprite->sState) + 1;
u16 frame;
+ if(JOY_NEW(B_BUTTON) && (STATE(sprite->sState) != BALL_WAIT_NEXT_SHAKE)){ // IF new B button pressed when the ball is shaking
+ gBallShakesBData.ballShakesArray |= (1 << ((2 * shakes) - 1));
+ }
switch (STATE(sprite->sState))
{
case BALL_ROLL_1:
+ if(gBallShakesBData.ballShakesArray >> 6 != shakes && !JOY_HELD(B_BUTTON)){ // At the beginning of the shake, check to make sure B isn't being held to discourage spamming B.
+ gBallShakesBData.ballShakesArray |= (1 << ((shakes - 1) * 2));
+ }
// Rolling effect: every frame in the rotation, the sprite shifts 176/256 of a pixel.
if (gBattleSpritesDataPtr->animationData->ballSubpx > 255)
{
sprite->x2 += sprite->sDirection;
@@ -1231,8 +1256,16 @@ static void SpriteCB_Ball_Wobble_Step(struct Sprite *sprite)
break;
case BALL_NEXT_MOVE:
SHAKE_INC(sprite->sState);
shakes = SHAKES(sprite->sState);
+ gBallShakesBData.ballShakesArray &= 0x3F; //b0011.1111 to clear the ball count bits
+ gBallShakesBData.ballShakesArray |= (shakes << 6);
+ // If the B button wasn't held at the beginning and a new B button was pressed when the ball was shaking, increase the odds
+ if (((gBallShakesBData.ballShakesArray >> ((shakes - 1) * 2)) & 0x03) == 0x03){
+ gBallShakesBData.odds = (BALL_SHAKE_BUTTON_MULT * gBallShakesBData.odds) / 10;
+ }
+ gBallShakesBData.shakes += CalcNextShakeFromOdds(gBallShakesBData.odds);
+ gBattleSpritesDataPtr->animationData->ballThrowCaseId = gBallShakesBData.shakes;
if (shakes == gBattleSpritesDataPtr->animationData->ballThrowCaseId)
{
sprite->affineAnimPaused = TRUE;
sprite->callback = SpriteCB_Ball_Release;
------------------------------ src/battle_main.c ------------------------------
index 39e694937..54ff4d614 100644
@@ -174,8 +174,9 @@ EWRAM_DATA s32 gBattleMoveDamage = 0;
EWRAM_DATA s32 gHpDealt = 0;
EWRAM_DATA s32 gTakenDmg[MAX_BATTLERS_COUNT] = {0};
EWRAM_DATA u16 gLastUsedItem = 0;
+EWRAM_DATA struct BattleOddsModifierButtonPress gBallShakesBData = {0};
EWRAM_DATA u8 gLastUsedAbility = 0;
EWRAM_DATA u8 gBattlerAttacker = 0;
EWRAM_DATA u8 gBattlerTarget = 0;
EWRAM_DATA u8 gBattlerFainted = 0;
------------------------- src/battle_script_commands.c -------------------------
index 38ed000e4..cbf287213 100644
@@ -326,8 +326,9 @@ static void Cmd_subattackerhpbydmg(void);
static void Cmd_removeattackerstatus1(void);
static void Cmd_finishaction(void);
static void Cmd_finishturn(void);
static void Cmd_trainerslideout(void);
+static void Cmd_ballthrowend(void);
void (* const gBattleScriptingCommandsTable[])(void) =
{
Cmd_attackcanceler, //0x0
@@ -577,9 +578,10 @@ void (* const gBattleScriptingCommandsTable[])(void) =
Cmd_subattackerhpbydmg, //0xF4
Cmd_removeattackerstatus1, //0xF5
Cmd_finishaction, //0xF6
Cmd_finishturn, //0xF7
- Cmd_trainerslideout //0xF8
+ Cmd_trainerslideout, //0xF8
+ Cmd_ballthrowend //0xF9
};
struct StatFractions
{
@@ -9811,8 +9813,9 @@ static void Cmd_removelightscreenreflect(void)
static void Cmd_handleballthrow(void)
{
u8 ballMultiplier = 0;
+ gBallShakesBData.ballShakesArray = 0;
if (gBattleControllerExecFlags)
return;
@@ -9872,9 +9872,10 @@ static void Cmd_handleballthrow(void)
gBattlescriptCurrInstr = BattleScript_TrainerBallBlock;
}
else if (gBattleTypeFlags & BATTLE_TYPE_WALLY_TUTORIAL)
{
- BtlController_EmitBallThrowAnim(BUFFER_A, BALL_3_SHAKES_SUCCESS);
+ gBallShakesBData.shakes = CalcNextShakeFromOdds(gBallShakesBData.odds);
+ BtlController_EmitBallThrowAnim(BUFFER_A, gBallShakesBData.shakes);
MarkBattlerForControllerExec(gActiveBattler);
gBattlescriptCurrInstr = BattleScript_WallyBallThrow;
}
else
@@ -9929,62 +9932,13 @@ static void Cmd_handleballthrow(void)
gBattleResults.catchAttempts[gLastUsedItem - ITEM_ULTRA_BALL]++;
}
}
- if (odds > 254) // mon caught
- {
- BtlController_EmitBallThrowAnim(BUFFER_A, BALL_3_SHAKES_SUCCESS);
- MarkBattlerForControllerExec(gActiveBattler);
- }
- gBattlescriptCurrInstr = BattleScript_SuccessBallThrow;
- SetMonData(&gEnemyParty[gBattlerPartyIndexes[gBattlerTarget]], MON_DATA_POKEBALL, &gLastUsedItem);
-
- if (CalculatePlayerPartyCount() == PARTY_SIZE)
- gBattleCommunication[MULTISTRING_CHOOSER] = 0;
- else
- gBattleCommunication[MULTISTRING_CHOOSER] = 1;
- }
- else // mon may be caught, calculate shakes
- {
- u8 shakes;
-
- odds = Sqrt(Sqrt(16711680 / odds));
- odds = 1048560 / odds;
-
- for (shakes = 0; shakes < BALL_3_SHAKES_SUCCESS && Random() < odds; shakes++);
-
- if (gLastUsedItem == ITEM_MASTER_BALL)
- shakes = BALL_3_SHAKES_SUCCESS; // why calculate the shakes before that check?
-
- BtlController_EmitBallThrowAnim(BUFFER_A, shakes);
- MarkBattlerForControllerExec(gActiveBattler);
- if (shakes == BALL_3_SHAKES_SUCCESS) // mon caught, copy of the code above
- {
- gBattlescriptCurrInstr = BattleScript_SuccessBallThrow;
- SetMonData(&gEnemyParty[gBattlerPartyIndexes[gBattlerTarget]], MON_DATA_POKEBALL, &gLastUsedItem);
-
- if (CalculatePlayerPartyCount() == PARTY_SIZE)
- gBattleCommunication[MULTISTRING_CHOOSER] = 0;
- else
- gBattleCommunication[MULTISTRING_CHOOSER] = 1;
- }
- else // not caught
- {
- gBattleCommunication[MULTISTRING_CHOOSER] = shakes;
- gBattlescriptCurrInstr = BattleScript_ShakeBallThrow;
- }
- }
+ gBallShakesBData.odds = odds;
+ gBallShakesBData.shakes = CalcNextShakeFromOdds(gBallShakesBData.odds);
+ BtlController_EmitBallThrowAnim(BUFFER_A, gBallShakesBData.shakes);
+ MarkBattlerForControllerExec(gActiveBattler);
+ gBattlescriptCurrInstr = BattleScript_BallThrowEnd;
}
}
static void Cmd_givecaughtmon(void)
@@ -10272,4 +10226,44 @@ static void Cmd_trainerslideout(void)
MarkBattlerForControllerExec(gActiveBattler);
gBattlescriptCurrInstr += 2;
}
+
+static void Cmd_ballthrowend(void)
+{
+ u8 shakes = gBallShakesBData.shakes;
+ if (shakes == BALL_3_SHAKES_SUCCESS) // mon caught, copy of the code above
+ {
+ gBattlescriptCurrInstr = BattleScript_SuccessBallThrow;
+ SetMonData(&gEnemyParty[gBattlerPartyIndexes[gBattlerTarget]], MON_DATA_POKEBALL, &gLastUsedItem);
+
+ if (CalculatePlayerPartyCount() == PARTY_SIZE)
+ gBattleCommunication[MULTISTRING_CHOOSER] = 0;
+ else
+ gBattleCommunication[MULTISTRING_CHOOSER] = 1;
+ }
+ else // not caught
+ {
+ gBattleCommunication[MULTISTRING_CHOOSER] = shakes;
+ gBattlescriptCurrInstr = BattleScript_ShakeBallThrow;
+ }
+ gBallShakesBData.odds = 0;
+ gBallShakesBData.ballShakesArray = 0;
+ gBallShakesBData.shakes = 0;
+}
+
+bool8 CalcNextShakeFromOdds(u32 odds)
+{
+ if (odds > 254 || gLastUsedItem == ITEM_MASTER_BALL || gBattleTypeFlags & BATTLE_TYPE_WALLY_TUTORIAL) // mon caught
+ return TRUE;
+ odds = Sqrt(Sqrt(16711680 / odds));
+ odds = 1048560 / odds;
+ return Random() < odds;
+}
Given the example above, if you'd prefer your odds to be:
1st Shake: 55
2nd Shake: 50
3rd Shake: 55
Then you just need to make these small changes to the above code:
---------------------------- include/battle_main.h ----------------------------
index edc818004..9f62a5958 100644
@@ -25,8 +25,9 @@ struct MultiPartnerMenuPokemon
struct BattleOddsModifierButtonPress
{
u8 ballShakesArray;
u32 odds;
+ u32 oddsOriginal;
u8 shakes;
};
/*
--------------------------- src/battle_anim_throw.c ---------------------------
index 9b3a8acf3..f0f624d69 100755
@@ -1260,9 +1260,12 @@ static void SpriteCB_Ball_Wobble_Step(struct Sprite *sprite)
gBallShakesBData.ballShakesArray &= 0x3F; //b0011.1111 to clear the ball count bits
gBallShakesBData.ballShakesArray |= shakes << 6;
// If the B button wasn't held at the beginning and a new B button was pressed when the ball was shaking, increase the odds
if (((gBallShakesBData.ballShakesArray >> ((shakes - 1) * 2)) & 0x03) == 0x03){
- gBallShakesBData.odds = (BALL_SHAKE_BUTTON_MULT * gBallShakesBData.odds) / 10;
+ gBallShakesBData.odds = (BALL_SHAKE_BUTTON_MULT * gBallShakesBData.oddsOriginal) / 10;
+ }
+ else{
+ gBallShakesBData.odds = gBallShakesBData.oddsOriginal;
}
gBallShakesBData.shakes += CalcNextShakeFromOdds(gBallShakesBData.odds);
gBattleSpritesDataPtr->animationData->ballThrowCaseId = gBallShakesBData.shakes;
DebugPrintf("gBattleSpritesDataPtr->animationData: %d", gBattleSpritesDataPtr->animationData->ballThrowCaseId);
------------------------- src/battle_script_commands.c -------------------------
index 7e1360e36..9f44a9fb8 100644
@@ -9932,9 +9932,9 @@ static void Cmd_handleballthrow(void)
gBattleResults.catchAttempts[gLastUsedItem - ITEM_ULTRA_BALL]++;
}
}
- gBallShakesBData.odds = odds;
+ gBallShakesBData.odds = gBallShakesBData.oddsOriginal = odds;
gBallShakesBData.shakes = CalcShakesFromOdds(gBallShakesBData.odds);
BtlController_EmitBallThrowAnim(BUFFER_A, gBallShakesBData.shakes);
MarkBattlerForControllerExec(gActiveBattler);
gBattlescriptCurrInstr = BattleScript_BallThrowEnd;