Skip to content

Commit

Permalink
Document the capture formula
Browse files Browse the repository at this point in the history
  • Loading branch information
lhearachel committed Oct 23, 2023
1 parent d2c301c commit 7584276
Show file tree
Hide file tree
Showing 5 changed files with 110 additions and 91 deletions.
2 changes: 1 addition & 1 deletion include/battle/battle_context.h
Original file line number Diff line number Diff line change
Expand Up @@ -286,7 +286,7 @@ struct BattleContext {
int flingTemp;
int flingScript;

u8 safariCatchCount;
u8 safariCatchStage;
u8 safariEscapeCount;
u8 runAttempts;
u8 battleEndFlag;
Expand Down
1 change: 1 addition & 0 deletions include/constants/battle.h
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@
#define BATTLE_TYPE_NO_EXPERIENCE (BATTLE_TYPE_LINK | BATTLE_TYPE_SAFARI | BATTLE_TYPE_FRONTIER | BATTLE_TYPE_PAL_PARK)
#define BATTLE_TYPE_NO_ABILITIES (BATTLE_TYPE_SAFARI | BATTLE_TYPE_PAL_PARK)
#define BATTLE_TYPE_2vs2_TAG (BATTLE_TYPE_2vs2 | BATTLE_TYPE_TAG)
#define BATTLE_TYPE_ALWAYS_CATCH (BATTLE_TYPE_PAL_PARK | BATTLE_TYPE_CATCH_TUTORIAL)

#define MAX_LINK_BATTLERS 4
#define MAX_BATTLERS 4
Expand Down
8 changes: 4 additions & 4 deletions src/overlay016/battle_controller.c
Original file line number Diff line number Diff line change
Expand Up @@ -2085,8 +2085,8 @@ static void BattleController_SafariBaitCommand(BattleSystem *battleSys, BattleCo
battleCtx->scriptTemp = BattleSystem_RandNext(battleSys) % 10;

// TODO: This 12 could use with a constant
if (battleCtx->safariCatchCount < 12) {
battleCtx->safariCatchCount++;
if (battleCtx->safariCatchStage < 12) {
battleCtx->safariCatchStage++;
}

if (battleCtx->scriptTemp != 0) {
Expand All @@ -2113,8 +2113,8 @@ static void BattleController_SafariRockCommand(BattleSystem *battleSys, BattleCo
if (battleCtx->scriptTemp != 0) {
battleCtx->msgTemp = 1;

if (battleCtx->safariCatchCount) {
battleCtx->safariCatchCount--;
if (battleCtx->safariCatchStage) {
battleCtx->safariCatchStage--;
}
}
}
Expand Down
188 changes: 103 additions & 85 deletions src/overlay016/battle_script.c
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
#include "constants/battle/message_tags.h"
#include "constants/battle/side_effects.h"
#include "constants/battle/system_control.h"
#include "constants/battle/terrain.h"
#include "constants/narc_files/battle_skill_subseq.h"

#include "struct_decls/struct_02002F38_decl.h"
Expand Down Expand Up @@ -43,6 +44,7 @@
#include "struct_defs/trainer_data.h"
#include "struct_defs/battle_system.h"
#include "struct_defs/struct_0208737C.h"
#include "struct_defs/fraction.h"
#include "overlay012/struct_ov12_02237728.h"
#include "overlay016/struct_ov16_022431BC.h"
#include "overlay016/struct_ov16_022431BC_1.h"
Expand Down Expand Up @@ -353,7 +355,7 @@ static int BattleMessage_TrainerNameTag(BattleSystem *battleSys, BattleContext *

static u32 ov16_022431BC(BattleSystem * param0, BattleContext * param1, int param2);
static void BattleScript_CalcEffortValues(Party *party, int slot, int species, int form);
static int ov16_0224A724(BattleSystem * param0, BattleContext * param1);
static int BattleScript_CalcCatchShakes(BattleSystem *battleSys, BattleContext *battleCtx);
static void BattleScript_LoadPartyLevelUpIcon(BattleSystem * param0, BattleScriptTaskData * param1, Pokemon * param2);
static void BattleScript_FreePartyLevelUpIcon(BattleSystem * param0, BattleScriptTaskData * param1);
static void BattleScript_UpdateFriendship(BattleSystem *battleSys, BattleContext *battleCtx, int faintingBattler);
Expand Down Expand Up @@ -9128,7 +9130,7 @@ static void ov16_02249B80 (SysTask * param0, void * param1)
case 2:
if (--v2->tmpData[1] == 0) {
ov16_02265050(v2->battleSys, v1, v2->ball);
v2->tmpData[2] = ov16_0224A724(v2->battleSys, v2->battleCtx);
v2->tmpData[2] = BattleScript_CalcCatchShakes(v2->battleSys, v2->battleCtx);

if (v2->tmpData[2] < 4) {
v2->tmpData[3] = v2->tmpData[2];
Expand Down Expand Up @@ -9604,140 +9606,156 @@ static void ov16_02249B80 (SysTask * param0, void * param1)
}
}

static const u8 Unk_ov16_0226E570[] = {
0x14,
0xF,
0xA,
0xF
static const u8 sBasicBallMod[] = {
20, // ITEM_ULTRA_BALL
15, // ITEM_GREAT_BALL
10, // ITEM_POKE_BALL
15, // ITEM_SAFARI_BALL
};

static const u8 Unk_ov16_0226E638[][2] = {
{0xA, 0x28},
{0xA, 0x23},
{0xA, 0x1E},
{0xA, 0x19},
{0xA, 0x14},
{0xA, 0xF},
{0xA, 0xA},
{0xF, 0xA},
{0x14, 0xA},
{0x19, 0xA},
{0x1E, 0xA},
{0x23, 0xA},
{0x28, 0xA}
static const struct Fraction sSafariCatchRate[] = {
{ 10, 40 },
{ 10, 35 },
{ 10, 30 },
{ 10, 25 },
{ 10, 20 },
{ 10, 15 },
{ 10, 10 },
{ 15, 10 },
{ 20, 10 },
{ 25, 10 },
{ 30, 10 },
{ 35, 10 },
{ 40, 10 }
};

static int ov16_0224A724 (BattleSystem * param0, BattleContext * param1)
/**
* @brief Computes the number of times that a Poke Ball will "shake" when
* attempting to capture a wild Pokemon.
*
* This is, effectively, the capture formula. It performs all computations which
* factor into whether a given Pokemon will be caught or not, including the wild
* Pokemon's species, the type of ball being used, and Safari Zone mechanics.
*
* @param battleSys
* @param battleCtx
* @return The number of times that a Poke Ball will shake during a capture
* attempt. 4 shakes is defined as a successful capture.
*/
static int BattleScript_CalcCatchShakes(BattleSystem *battleSys, BattleContext *battleCtx)
{
int v0;
u32 v1;
u32 v2;
u32 v3;
u32 v4;
int v5;
int v6;

if (BattleSystem_BattleType(param0) & (0x200 | 0x400)) {
if (BattleSystem_BattleType(battleSys) & BATTLE_TYPE_ALWAYS_CATCH) {
return 4;
}

if (param1->msgItemTemp == 5) {
v3 = PokemonPersonalData_GetSpeciesValue(param1->battleMons[param1->defender].species, 8);
v3 = v3 * Unk_ov16_0226E638[param1->safariCatchCount][0] / Unk_ov16_0226E638[param1->safariCatchCount][1];
u32 speciesMod;
if (battleCtx->msgItemTemp == ITEM_SAFARI_BALL) {
speciesMod = PokemonPersonalData_GetSpeciesValue(battleCtx->battleMons[battleCtx->defender].species, MON_DATA_PERSONAL_CATCH_RATE);
speciesMod = speciesMod * sSafariCatchRate[battleCtx->safariCatchStage].numerator / sSafariCatchRate[battleCtx->safariCatchStage].denominator;
} else {
v3 = PokemonPersonalData_GetSpeciesValue(param1->battleMons[param1->defender].species, 8);
speciesMod = PokemonPersonalData_GetSpeciesValue(battleCtx->battleMons[battleCtx->defender].species, MON_DATA_PERSONAL_CATCH_RATE);
}

v4 = 10;
v5 = BattleMon_Get(param1, param1->defender, 27, NULL);
v6 = BattleMon_Get(param1, param1->defender, 28, NULL);
u32 ballMod = 10;
int type1 = BattleMon_Get(battleCtx, battleCtx->defender, BATTLEMON_TYPE_1, NULL);
int type2 = BattleMon_Get(battleCtx, battleCtx->defender, BATTLEMON_TYPE_2, NULL);

if (param1->msgItemTemp > 5) {
switch (param1->msgItemTemp) {
case 6:
if ((v5 == 11) || (v6 == 11) || (v5 == 6) || (v6 == 6)) {
v4 = 30;
if (battleCtx->msgItemTemp > ITEM_SAFARI_BALL) {
switch (battleCtx->msgItemTemp) {
case ITEM_NET_BALL:
if (type1 == TYPE_WATER || type2 == TYPE_WATER
|| type1 == TYPE_BUG || type2 == TYPE_BUG) {
ballMod = 30;
}
break;
case 7:
if (BattleSystem_Terrain(param0) == 7) {
v4 = 35;

case ITEM_DIVE_BALL:
if (BattleSystem_Terrain(battleSys) == TERRAIN_WATER) {
ballMod = 35;
}
break;
case 8:
if (param1->battleMons[param1->defender].level < 40) {
v4 = 40 - param1->battleMons[param1->defender].level;

if (v4 < 10) {
v4 = 10;
case ITEM_NEST_BALL:
if (battleCtx->battleMons[battleCtx->defender].level < 40) {
ballMod = 40 - battleCtx->battleMons[battleCtx->defender].level;

if (ballMod < 10) {
ballMod = 10;
}
}
break;
case 9:
if (BattleSystem_CaughtSpecies(param0, param1->battleMons[param1->defender].species) == 1) {
v4 = 30;

case ITEM_REPEAT_BALL:
if (BattleSystem_CaughtSpecies(battleSys, battleCtx->battleMons[battleCtx->defender].species) == TRUE) {
ballMod = 30;
}
break;
case 10:
v4 = 10 + param1->totalTurns;

if (v4 > 40) {
v4 = 40;
case ITEM_TIMER_BALL:
ballMod = 10 + battleCtx->totalTurns;
if (ballMod > 40) {
ballMod = 40;
}
break;
case 13:
if ((BattleSystem_Time(param0) == 3) || (BattleSystem_Time(param0) == 4) || (BattleSystem_Terrain(param0) == 5)) {
v4 = 35;

case ITEM_DUSK_BALL:
if (BattleSystem_Time(battleSys) == TIME_NIGHT
|| BattleSystem_Time(battleSys) == TIME_MIDNIGHT
|| BattleSystem_Terrain(battleSys) == TERRAIN_CAVE) {
ballMod = 35;
}
break;
case 15:
if (param1->totalTurns < 1) {
v4 = 40;

case ITEM_QUICK_BALL:
if (battleCtx->totalTurns < 1) {
ballMod = 40;
}
break;
}
} else {
v4 = Unk_ov16_0226E570[param1->msgItemTemp - 2];
ballMod = sBasicBallMod[battleCtx->msgItemTemp - ITEM_ULTRA_BALL];
}

v1 = ((v3 * v4) / 10) * (param1->battleMons[param1->defender].maxHP * 3 - param1->battleMons[param1->defender].curHP * 2) / (param1->battleMons[param1->defender].maxHP * 3);
u32 catchRate = (speciesMod * ballMod / 10)
* (battleCtx->battleMons[battleCtx->defender].maxHP * 3 - battleCtx->battleMons[battleCtx->defender].curHP * 2)
/ (battleCtx->battleMons[battleCtx->defender].maxHP * 3);

if (param1->battleMons[param1->defender].status & (0x7 | 0x20)) {
v1 *= 2;
if (battleCtx->battleMons[battleCtx->defender].status & (MON_CONDITION_SLEEP | MON_CONDITION_FREEZE)) {
catchRate *= 2;
}

if (param1->battleMons[param1->defender].status & (0x8 | 0x40 | 0x10 | 0x80)) {
v1 = v1 * 15 / 10;
if (battleCtx->battleMons[battleCtx->defender].status & (MON_CONDITION_POISON | MON_CONDITION_BURN | MON_CONDITION_PARALYSIS | MON_CONDITION_TOXIC)) {
catchRate = catchRate * 15 / 10;
}

if (v1 >= 255) {
v0 = 4;
int shakes;
if (catchRate >= 0xFF) {
shakes = 4;
} else {
v2 = (255 << 16) / v1;

CP_SetSqrt32(v2);
u32 sqrtRate = (0xFF << 16) / catchRate;
CP_SetSqrt32(sqrtRate);
CP_WaitSqrt();

v2 = CP_GetSqrtResult32();

CP_SetSqrt32(v2);
sqrtRate = CP_GetSqrtResult32();
CP_SetSqrt32(sqrtRate);
CP_WaitSqrt();

v1 = CP_GetSqrtResult32();
v1 = (65535 << 4) / v1;
catchRate = CP_GetSqrtResult32();
catchRate = (0xFFFF << 4) / catchRate;

for (v0 = 0; v0 < 4; v0++) {
if (BattleSystem_RandNext(param0) >= v1) {
for (shakes = 0; shakes < 4; shakes++) {
if (BattleSystem_RandNext(battleSys) >= catchRate) {
break;
}
}

if (param1->msgItemTemp == 1) {
v0 = 4;
if (battleCtx->msgItemTemp == ITEM_MASTER_BALL) {
shakes = 4;
}
}

return v0;

return shakes;
}

/**
Expand Down
2 changes: 1 addition & 1 deletion src/overlay016/ov16_0225177C.c
Original file line number Diff line number Diff line change
Expand Up @@ -2071,7 +2071,7 @@ void BattleContext_InitCounters (BattleSystem * param0, BattleContext * param1)
param1->battlersSwitchingMask |= FlagIndex(3);
}

param1->safariCatchCount = 6;
param1->safariCatchStage = 6;
param1->safariEscapeCount = 6;
}

Expand Down

0 comments on commit 7584276

Please sign in to comment.