Skip to content

Add New Pokeballs From Sinnoh

judicornadamsfoster edited this page Nov 3, 2026 · 2 revisions

This code adds the balls from Sinnoh, Unova and Alola:
ITEM_HEAL_BALL, ITEM_DUSK_BALL, ITEM_QUICK_BALL, ITEM_PARK_BALL, ITEM_CHERISH_BALL, ITEM_DREAM_BALL and ITEM_BEAST_BALL

newballs heal

Credits:

All of this code comes from emerald-expansion, HnS / heart n soul, pokeemerald-worped, crystal dust, and emerald++


Ball effects:

Heal Ball - heals the pokemon on being added to party or sent to box; x1 ball multiplier at all times.
Dusk Ball - between 8pm and 3am or in a map classed as a cave/underground, ball has x3.5 multiplier; otherwise x1 ball multiplier.
Quick Ball - on 1st turn only has x4 ball multiplier; otherwise x1 ball multiplier.
Park Ball - will only catch a pokemon when the Map Type = MAP_TYPE_UNKNOWN, otherwise will NOT catch; x1 ball multiplier at all times. (intended to be used in a "Pal Park"-type area or another Safari Zone and not be available anywhere else).
Cherish Ball - will NOT catch any pokemon no matter what, ever. (intended to be used as a ball for gift pokemon in a "mystery gift"-type pokemon and not be available anywhere else. change ballMultiplier = 0; to any number greater than 0 to make it able to catch pokemon).
Dream Ball - if the wild pokemon is asleep, ball has x5 ball catch multiplier; otherwise x1 ball multiplier.
Beast Ball - if the ultra beasts are added to your decomp, then ball has x5 ball multiplier with a x0.1 multiplier for non-Ultra Beast pokemon. if the ultra beasts are NOT in your decomp, the ball will instead work on Johto Beasts and multiply their catch rate by 30 with a x1.5 ball multiplier, or a x0.1 multiplier for all non-Johto Beast pokemon.


How to change ball multipliers:

change ballMultiplier to any 2 or 3 digit number (up to a maximum 255) eg: ballMultiplier = 75; will give a 7.5x ball catch multiplier


How To Use:

pull from this branch, or add manually using the guide below.

git remote add tenaya15 https://github.com/tenaya15/tenaya15emerald.git
git pull tenaya15 sinnoh_balls

first download this zip file sinnoh_balls.zip

Extract the graphics folder to your decomp/hack root folder


data/battle_scripts_2.s

near the top of filein function gBattlescriptsForBallThrow::
add after .4byte BattleScript_BallThrow @ ITEM_PREMIER_BALL

	.4byte BattleScript_BallThrow        @ ITEM_HEAL_BALL
	.4byte BattleScript_BallThrow        @ ITEM_DUSK_BALL
	.4byte BattleScript_BallThrow        @ ITEM_QUICK_BALL
	.4byte BattleScript_BallThrow        @ ITEM_PARK_BALL
	.4byte BattleScript_BallThrow        @ ITEM_CHERISH_BALL
	.4byte BattleScript_BallThrow        @ ITEM_DREAM_BALL
	.4byte BattleScript_BallThrow        @ ITEM_BEAST_BALL


include/constants/items.h

there are two versions of changes for this file depending if you have an up-to-date pret decomp or not. Only use one of the options for this file.

Option 1: IF YOU HAVE LATEST VERSION THAT USES enum

add to the enum section at the top of the file after ITEM_PREMIER_BALL:

    ITEM_HEAL_BALL,
    ITEM_DUSK_BALL,
    ITEM_QUICK_BALL,
    ITEM_PARK_BALL,
    ITEM_CHERISH_BALL,
    ITEM_DREAM_BALL,
    ITEM_BEAST_BALL,

then delete the following lines from inside the same section:

    ITEM_034,
    ITEM_035,
    ITEM_036,
    ITEM_037,
    ITEM_038,
    ITEM_039,
    ITEM_03A,

__

then change #define LAST_BALL ITEM_PREMIER_BALL near the end of the file to:

#define LAST_BALL  ITEM_BEAST_BALL

__

Option 2: IF YOUR VERSION STILL USES #define FOR ITEMS

we are replacing 7 unused items so this will require moving some items down

NOTE: If you have already added new items to your hack, you will need to manually adjust the below numbers to take into consideration these new items

delete lines

#define ITEM_034 52
#define ITEM_035 53
#define ITEM_036 54
#define ITEM_037 55
#define ITEM_038 56
#define ITEM_039 57
#define ITEM_03A 58

add the following after #define ITEM_PREMIER_BALL 12

#define ITEM_HEAL_BALL 13
#define ITEM_DUSK_BALL 14
#define ITEM_QUICK_BALL 15
#define ITEM_PARK_BALL 16
#define ITEM_CHERISH_BALL 17
#define ITEM_DREAM_BALL 18
#define ITEM_BEAST_BALL 19

// 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. gBattlescriptsForBallThrow and 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_BEAST_BALL

// Pokémon Items
#define ITEM_POTION 20
#define ITEM_ANTIDOTE 21
#define ITEM_BURN_HEAL 22
#define ITEM_ICE_HEAL 23
#define ITEM_AWAKENING 24
#define ITEM_PARALYZE_HEAL 25
#define ITEM_FULL_RESTORE 26
#define ITEM_MAX_POTION 27
#define ITEM_HYPER_POTION 28
#define ITEM_SUPER_POTION 29
#define ITEM_FULL_HEAL 30
#define ITEM_REVIVE 31
#define ITEM_MAX_REVIVE 32
#define ITEM_FRESH_WATER 33
#define ITEM_SODA_POP 34
#define ITEM_LEMONADE 35
#define ITEM_MOOMOO_MILK 36
#define ITEM_ENERGY_POWDER 37
#define ITEM_ENERGY_ROOT 38
#define ITEM_HEAL_POWDER 39
#define ITEM_REVIVAL_HERB 40
#define ITEM_ETHER 41
#define ITEM_MAX_ETHER 42
#define ITEM_ELIXIR 43
#define ITEM_MAX_ELIXIR 44
#define ITEM_LAVA_COOKIE 45
#define ITEM_BLUE_FLUTE 46
#define ITEM_YELLOW_FLUTE 47
#define ITEM_RED_FLUTE 48
#define ITEM_BLACK_FLUTE 49
#define ITEM_WHITE_FLUTE 50
#define ITEM_BERRY_JUICE 51
#define ITEM_SACRED_ASH 52
#define ITEM_SHOAL_SALT 53
#define ITEM_SHOAL_SHELL 54
#define ITEM_RED_SHARD 55
#define ITEM_BLUE_SHARD 56
#define ITEM_YELLOW_SHARD 57
#define ITEM_GREEN_SHARD 58

include/graphics.h

add to the end of file, before #endif //GUARD_GRAPHICS_H


// new balls in battle
extern const u32 gBallGfx_Heal[];
extern const u32 gBallPal_Heal[];
extern const u32 gBallGfx_Dusk[];
extern const u32 gBallPal_Dusk[];
extern const u32 gBallGfx_Quick[];
extern const u32 gBallPal_Quick[];
extern const u32 gBallGfx_Park[];
extern const u32 gBallPal_Park[];
extern const u32 gBallGfx_Cherish[];
extern const u32 gBallPal_Cherish[];
extern const u32 gBallGfx_Dream[];
extern const u32 gBallPal_Dream[];
extern const u32 gBallGfx_Beast[];
extern const u32 gBallPal_Beast[];

//new ball items
extern const u32 gItemIcon_HealBall[];
extern const u32 gItemIconPalette_HealBall[];
extern const u32 gItemIcon_DuskBall[];
extern const u32 gItemIconPalette_DuskBall[];
extern const u32 gItemIcon_QuickBall[];
extern const u32 gItemIconPalette_QuickBall[];
extern const u32 gItemIcon_ParkBall[];
extern const u32 gItemIconPalette_ParkBall[];
extern const u32 gItemIcon_CherishBall[];
extern const u32 gItemIconPalette_CherishBall[];
extern const u32 gItemIcon_DreamBall[];
extern const u32 gItemIconPalette_DreamBall[];
extern const u32 gItemIcon_BeastBall[];
extern const u32 gItemIconPalette_BeastBall[];


include/pokeball.h

add to the first enum section, after line BALL_PREMIER, and before line POKEBALL_COUNT

    BALL_HEAL,
    BALL_DUSK,
    BALL_QUICK,
    BALL_PARK,
    BALL_CHERISH,
    BALL_DREAM,
    BALL_BEAST,

src/data/graphics/items.h

IMPORTANT NOTE: there are two versions to this step depending if you have an up-to-date pokeemerald decomp that uses INCGFX or an older one that still uses INCBIN.

IF YOU HAVE AN UP-TO-DATE VERSION THAT USES INCGFX

add to the end of the file:


// new pokeballs
const u32 gItemIcon_HealBall[] = INCGFX_U32("graphics/items/icons/heal_ball.png", ".4bpp.lz");
const u32 gItemIconPalette_HealBall[] = INCGFX_U32("graphics/items/icon_palettes/heal_ball.pal", ".gbapal.lz");
const u32 gItemIcon_DuskBall[] = INCGFX_U32("graphics/items/icons/dusk_ball.png", ".4bpp.lz");
const u32 gItemIconPalette_DuskBall[] = INCGFX_U32("graphics/items/icon_palettes/dusk_ball.pal", ".gbapal.lz");
const u32 gItemIcon_QuickBall[] = INCGFX_U32("graphics/items/icons/quick_ball.png", ".4bpp.lz");
const u32 gItemIconPalette_QuickBall[] = INCGFX_U32("graphics/items/icon_palettes/quick_ball.pal", ".gbapal.lz");
const u32 gItemIcon_ParkBall[] = INCGFX_U32("graphics/items/icons/park_ball.png", ".4bpp.lz");
const u32 gItemIconPalette_ParkBall[] = INCGFX_U32("graphics/items/icon_palettes/park_ball.pal", ".gbapal.lz");
const u32 gItemIcon_CherishBall[] = INCGFX_U32("graphics/items/icons/cherish_ball.png", ".4bpp.lz");
const u32 gItemIconPalette_CherishBall[] = INCGFX_U32("graphics/items/icon_palettes/cherish_ball.pal", ".gbapal.lz");
const u32 gItemIcon_DreamBall[] = INCGFX_U32("graphics/items/icons/dream_ball.png", ".4bpp.lz");
const u32 gItemIconPalette_DreamBall[] = INCGFX_U32("graphics/items/icon_palettes/dream_ball.pal", ".gbapal.lz");
const u32 gItemIcon_BeastBall[] = INCGFX_U32("graphics/items/icons/beast_ball.png", ".4bpp.lz");
const u32 gItemIconPalette_BeastBall[] = INCGFX_U32("graphics/items/icon_palettes/beast_ball.pal", ".gbapal.lz");

OR

IF YOU HAVE AN OLDER VERSION THAT STILL USES INCBIN

add to the end of the file:

const u32 gItemIcon_HealBall[] = INCBIN_U32("graphics/items/icons/heal_ball.4bpp.lz");
const u32 gItemIconPalette_HealBall[] = INCBIN_U32("graphics/items/icon_palettes/heal_ball.gbapal.lz");
const u32 gItemIcon_DuskBall[] = INCBIN_U32("graphics/items/icons/dusk_ball.4bpp.lz");
const u32 gItemIconPalette_DuskBall[] = INCBIN_U32("graphics/items/icon_palettes/dusk_ball.gbapal.lz");
const u32 gItemIcon_QuickBall[] = INCBIN_U32("graphics/items/icons/quick_ball.4bpp.lz");
const u32 gItemIconPalette_QuickBall[] = INCBIN_U32("graphics/items/icon_palettes/quick_ball.gbapal.lz");
const u32 gItemIcon_ParkBall[] = INCBIN_U32("graphics/items/icons/park_ball.4bpp.lz");
const u32 gItemIconPalette_ParkBall[] = INCBIN_U32("graphics/items/icon_palettes/park_ball.gbapal.lz");
const u32 gItemIcon_CherishBall[] = INCBIN_U32("graphics/items/icons/cherish_ball.4bpp.lz");
const u32 gItemIconPalette_CherishBall[] = INCBIN_U32("graphics/items/icon_palettes/cherish_ball.gbapal.lz");
const u32 gItemIcon_DreamBall[] = INCBIN_U32("graphics/items/icons/dream_ball.4bpp.lz");
const u32 gItemIconPalette_DreamBall[] = INCBIN_U32("graphics/items/icon_palettes/dream_ball.gbapal.lz");
const u32 gItemIcon_BeastBall[] = INCBIN_U32("graphics/items/icons/beast_ball.4bpp.lz");
const u32 gItemIconPalette_BeastBall[] = INCBIN_U32("graphics/items/icon_palettes/beast_ball.gbapal.lz");


src/data/graphics/pokeballs.c

IMPORTANT NOTE: there are two versions to this step depending if you have an up-to-date pokeemerald decomp that uses INCGFX or an older one that still uses INCBIN.

IF YOU HAVE AN UP-TO-DATE VERSION THAT USES INCGFX

add to the end of file:


//new pokeballs
const u32 gBallGfx_Heal[] = INCGFX_U32("graphics/balls/heal.png", ".4bpp.lz");
const u32 gBallPal_Heal[] = INCGFX_U32("graphics/balls/heal.png", ".gbapal.lz");

const u32 gBallGfx_Dusk[] = INCGFX_U32("graphics/balls/dusk.png", ".4bpp.lz");
const u32 gBallPal_Dusk[] = INCGFX_U32("graphics/balls/dusk.png", ".gbapal.lz");

const u32 gBallGfx_Quick[] = INCGFX_U32("graphics/balls/quick.png", ".4bpp.lz");
const u32 gBallPal_Quick[] = INCGFX_U32("graphics/balls/quick.png", ".gbapal.lz");

const u32 gBallGfx_Park[] = INCGFX_U32("graphics/balls/park.png", ".4bpp.lz");
const u32 gBallPal_Park[] = INCGFX_U32("graphics/balls/park.png", ".gbapal.lz");

const u32 gBallGfx_Cherish[] = INCGFX_U32("graphics/balls/cherish.png", ".4bpp.lz");
const u32 gBallPal_Cherish[] = INCGFX_U32("graphics/balls/cherish.png", ".gbapal.lz");

const u32 gBallGfx_Dream[] = INCGFX_U32("graphics/balls/dream.png", ".4bpp.lz");
const u32 gBallPal_Dream[] = INCGFX_U32("graphics/balls/dream.png", ".gbapal.lz");

const u32 gBallGfx_Beast[] = INCGFX_U32("graphics/balls/beast.png", ".4bpp.lz");
const u32 gBallPal_Beast[] = INCGFX_U32("graphics/balls/beast.png", ".gbapal.lz");

OR

IF YOU HAVE AN OLDER VERSION THAT STILL USES INCBIN

add to the end of the file:

const u32 gBallGfx_Heal[] = INCBIN_U32("graphics/balls/heal.4bpp.lz");
const u32 gBallPal_Heal[] = INCBIN_U32("graphics/balls/heal.gbapal.lz");
const u32 gBallGfx_Dusk[] = INCBIN_U32("graphics/balls/dusk.4bpp.lz");
const u32 gBallPal_Dusk[] = INCBIN_U32("graphics/balls/dusk.gbapal.lz");
const u32 gBallGfx_Quick[] = INCBIN_U32("graphics/balls/quick.4bpp.lz");
const u32 gBallPal_Quick[] = INCBIN_U32("graphics/balls/quick.gbapal.lz");
const u32 gBallGfx_Park[] = INCBIN_U32("graphics/balls/park.4bpp.lz");
const u32 gBallPal_Park[] = INCBIN_U32("graphics/balls/park.gbapal.lz");
const u32 gBallGfx_Cherish[] = INCBIN_U32("graphics/balls/cherish.4bpp.lz");
const u32 gBallPal_Cherish[] = INCBIN_U32("graphics/balls/cherish.gbapal.lz");
const u32 gBallGfx_Dream[] = INCBIN_U32("graphics/balls/dream.4bpp.lz");
const u32 gBallPal_Dream[] = INCBIN_U32("graphics/balls/dream.gbapal.lz");
const u32 gBallGfx_Beast[] = INCBIN_U32("graphics/balls/beast.4bpp.lz");
const u32 gBallPal_Beast[] = INCBIN_U32("graphics/balls/beast.gbapal.lz");


src/data/text/item_descriptions.h

add to the end of the file:


// new pokeballs
static const u8 sHealBallDesc[] = _(
    "A remedial ball\n"
    "that restores\n"
    "caught Pokémon.");

static const u8 sDuskBallDesc[] = _(
    "A ball that works\n"
    "better at night\n"
    "or in dark places.");

static const u8 sQuickBallDesc[] = _(
    "A ball that works\n"
    "better at the\n"
    "start of a battle.");

static const u8 sParkBallDesc[] = _(
    "A special ball\n"
    "used only in\n"
    "the Pal Park.");

static const u8 sCherishBallDesc[] = _(
    "A rare ball crafted\n"
    "to commemorate a\n"
    "special occasion.");

static const u8 sDreamBallDesc[] = _(
    "A ball that works\n"
    "better on Pokémon\n"
    "that are asleep");

static const u8 sBeastBallDesc[] = _(
    "A special ball\n"
    "designed to catch\n"
    "Ultra Beasts.");


src/data/item_icon_table.h

NOTE: if you have already added new items to your hack, you may already have deleted some of the lines below

add after [ITEM_PREMIER_BALL] = {gItemIcon_PremierBall, gItemIconPalette_LuxuryBall},

    // new pokeballs
	[ITEM_HEAL_BALL] = {gItemIcon_HealBall, gItemIconPalette_HealBall},
	[ITEM_DUSK_BALL] = {gItemIcon_DuskBall, gItemIconPalette_DuskBall},
	[ITEM_QUICK_BALL] = {gItemIcon_QuickBall, gItemIconPalette_QuickBall},
	[ITEM_PARK_BALL] = {gItemIcon_ParkBall, gItemIconPalette_ParkBall},
	[ITEM_CHERISH_BALL] = {gItemIcon_CherishBall, gItemIconPalette_CherishBall},
	[ITEM_DREAM_BALL] = {gItemIcon_DreamBall, gItemIconPalette_DreamBall},
	[ITEM_BEAST_BALL] = {gItemIcon_BeastBall, gItemIconPalette_BeastBall},

then delete these lines

    // ????????
    [ITEM_034] = {gItemIcon_QuestionMark, gItemIconPalette_QuestionMark},
    [ITEM_035] = {gItemIcon_QuestionMark, gItemIconPalette_QuestionMark},
    [ITEM_036] = {gItemIcon_QuestionMark, gItemIconPalette_QuestionMark},
    [ITEM_037] = {gItemIcon_QuestionMark, gItemIconPalette_QuestionMark},
    [ITEM_038] = {gItemIcon_QuestionMark, gItemIconPalette_QuestionMark},
    [ITEM_039] = {gItemIcon_QuestionMark, gItemIconPalette_QuestionMark},
    [ITEM_03A] = {gItemIcon_QuestionMark, gItemIconPalette_QuestionMark},


src/data/items.h

add after the section for [ITEM_PREMIER_BALL] = (around line 171)


    [ITEM_HEAL_BALL] =
    {
        .name = _("HEAL BALL"),
        .itemId = ITEM_HEAL_BALL,
        .price = 1000,
        .description = sHealBallDesc,
        .pocket = POCKET_POKE_BALLS,
        .type = ITEM_HEAL_BALL - FIRST_BALL,
        .battleUsage = ITEM_B_USE_OTHER,
        .battleUseFunc = ItemUseInBattle_PokeBall,
        .secondaryId = ITEM_HEAL_BALL - FIRST_BALL,
    },
	
    [ITEM_DUSK_BALL] =
    {
        .name = _("DUSK BALL"),
        .itemId = ITEM_DUSK_BALL,
        .price = 1000,
        .description = sDuskBallDesc,
        .pocket = POCKET_POKE_BALLS,
        .type = ITEM_DUSK_BALL - FIRST_BALL,
        .battleUsage = ITEM_B_USE_OTHER,
        .battleUseFunc = ItemUseInBattle_PokeBall,
        .secondaryId = ITEM_DUSK_BALL - FIRST_BALL,
    },

    [ITEM_QUICK_BALL] =
    {
        .name = _("QUICK BALL"),
        .itemId = ITEM_QUICK_BALL,
        .price = 1000,
        .description = sQuickBallDesc,
        .pocket = POCKET_POKE_BALLS,
        .type = ITEM_QUICK_BALL - FIRST_BALL,
        .battleUsage = ITEM_B_USE_OTHER,
        .battleUseFunc = ItemUseInBattle_PokeBall,
        .secondaryId = ITEM_QUICK_BALL - FIRST_BALL,
    },

    [ITEM_PARK_BALL] =
    {
        .name = _("PARK BALL"),
        .itemId = ITEM_PARK_BALL,
        .price = 1000,
        .description = sParkBallDesc,
        .pocket = POCKET_POKE_BALLS,
        .type = ITEM_PARK_BALL - FIRST_BALL,
        .battleUsage = ITEM_B_USE_OTHER,
        .battleUseFunc = ItemUseInBattle_PokeBall,
        .secondaryId = ITEM_PARK_BALL - FIRST_BALL,
    },

    [ITEM_CHERISH_BALL] =
    {
        .name = _("CHERISH BALL"),
        .itemId = ITEM_CHERISH_BALL,
        .price = 100000,
        .description = sCherishBallDesc,
        .pocket = POCKET_POKE_BALLS,
        .type = ITEM_USE_PARTY_MENU,
        .battleUsage = ITEM_B_USE_OTHER,
        .battleUseFunc = ItemUseInBattle_PokeBall,
        .secondaryId = ITEM_CHERISH_BALL - FIRST_BALL,
    },

    [ITEM_DREAM_BALL] =
    {
        .name = _("DREAM BALL"),
        .itemId = ITEM_DREAM_BALL,
        .price = 1000,
        .description = sDreamBallDesc,
        .pocket = POCKET_POKE_BALLS,
        .type = ITEM_DREAM_BALL - FIRST_BALL,
        .battleUsage = ITEM_B_USE_OTHER,
        .battleUseFunc = ItemUseInBattle_PokeBall,
        .secondaryId = ITEM_DREAM_BALL - FIRST_BALL,
    },

    [ITEM_BEAST_BALL] =
    {
        .name = _("BEAST BALL"),
        .itemId = ITEM_BEAST_BALL,
        .price = 10000,
        .description = sBeastBallDesc,
        .pocket = POCKET_POKE_BALLS,
        .type = ITEM_BEAST_BALL - FIRST_BALL,
        .battleUsage = ITEM_B_USE_OTHER,
        .battleUseFunc = ItemUseInBattle_PokeBall,
        .secondaryId = ITEM_BEAST_BALL - FIRST_BALL,
    },

/* Use if Cherish Ball is to be used as a Key Item instead and remove the above entry
    [ITEM_CHERISH_BALL] =
    {
        .name = _("CHERISH BALL"),
        .itemId = ITEM_CHERISH_BALL,
        .price = 0,
        .description = sCherishBallDesc,
        .importance = 1,
        .pocket = POCKET_KEY_ITEMS,
        .type = ITEM_USE_BAG_MENU,
        .fieldUseFunc = ItemUseOutOfBattle_CannotUse,
    },
*/


src/battle_anim_throw.c

add after #define TAG_PARTICLES_PREMIERBALL 55031 (around line 141)

#define TAG_PARTICLES_HEALBALL    55032
#define TAG_PARTICLES_DUSKBALL    55033
#define TAG_PARTICLES_QUICKBALL   55034
#define TAG_PARTICLES_PARKBALL    55035
#define TAG_PARTICLES_CHERISHBALL 55036
#define TAG_PARTICLES_DREAMBALL   55037
#define TAG_PARTICLES_BEASTBALL   55038

__

in function static const struct CompressedSpriteSheet sBallParticleSpriteSheets[POKEBALL_COUNT] =
add after [BALL_PREMIER] = {gBattleAnimSpriteGfx_Particles, 0x100, TAG_PARTICLES_PREMIERBALL},

    [BALL_HEAL]    = {gBattleAnimSpriteGfx_Particles, 0x100, TAG_PARTICLES_HEALBALL},
    [BALL_DUSK]    = {gBattleAnimSpriteGfx_Particles, 0x100, TAG_PARTICLES_DUSKBALL},
    [BALL_QUICK]   = {gBattleAnimSpriteGfx_Particles, 0x100, TAG_PARTICLES_QUICKBALL},
    [BALL_PARK]    = {gBattleAnimSpriteGfx_Particles, 0x100, TAG_PARTICLES_PARKBALL},
    [BALL_CHERISH] = {gBattleAnimSpriteGfx_Particles, 0x100, TAG_PARTICLES_CHERISHBALL},
    [BALL_DREAM]   = {gBattleAnimSpriteGfx_Particles, 0x100, TAG_PARTICLES_DREAMBALL},
    [BALL_BEAST]   = {gBattleAnimSpriteGfx_Particles, 0x100, TAG_PARTICLES_BEASTBALL},

__

in function static const struct CompressedSpritePalette sBallParticlePalettes[POKEBALL_COUNT] =
add after [BALL_PREMIER] = {gBattleAnimSpritePal_CircleImpact, TAG_PARTICLES_PREMIERBALL},

    [BALL_HEAL]    = {gBattleAnimSpritePal_CircleImpact, TAG_PARTICLES_HEALBALL},
    [BALL_DUSK]    = {gBattleAnimSpritePal_CircleImpact, TAG_PARTICLES_DUSKBALL},
    [BALL_QUICK]   = {gBattleAnimSpritePal_CircleImpact, TAG_PARTICLES_QUICKBALL},
    [BALL_PARK]    = {gBattleAnimSpritePal_CircleImpact, TAG_PARTICLES_PARKBALL},
    [BALL_CHERISH] = {gBattleAnimSpritePal_CircleImpact, TAG_PARTICLES_CHERISHBALL},
    [BALL_DREAM]   = {gBattleAnimSpritePal_CircleImpact, TAG_PARTICLES_DREAMBALL},
    [BALL_BEAST]   = {gBattleAnimSpritePal_CircleImpact, TAG_PARTICLES_BEASTBALL},

__

in function static const u8 sBallParticleAnimNums[POKEBALL_COUNT] =
add after [BALL_PREMIER] = 4,

    [BALL_HEAL]    = 3,
    [BALL_DUSK]    = 5,
    [BALL_QUICK]   = 2,
    [BALL_PARK]    = 4,
    [BALL_CHERISH] = 5,
    [BALL_DREAM]   = 5,
    [BALL_BEAST]   = 5,

note: the numbers determine which animation it will use from the section above it (0 = sAnim_RegularBall, 1 = sAnim_MasterBall, 2 = sAnim_NetDiveBall, 3 = sAnim_NestBall, 4 = sAnim_LuxuryPremierBall, 5= sAnim_UltraRepeatTimerBall,)
__

in function static const TaskFunc sBallParticleAnimationFuncs[POKEBALL_COUNT] =
add after [BALL_PREMIER] = PremierBallOpenParticleAnimation,

    [BALL_HEAL]    = PokeBallOpenParticleAnimation,
    [BALL_DUSK]    = UltraBallOpenParticleAnimation,
    [BALL_QUICK]   = UltraBallOpenParticleAnimation,
    [BALL_PARK]    = UltraBallOpenParticleAnimation,
    [BALL_CHERISH] = MasterBallOpenParticleAnimation,
    [BALL_DREAM]   = UltraBallOpenParticleAnimation,
    [BALL_BEAST]   = UltraBallOpenParticleAnimation,

NOTE: feel free to change the open particle animations around and experiment with different combinations __

in function static const struct SpriteTemplate sBallParticleSpriteTemplates[POKEBALL_COUNT] =
add after the final section [BALL_PREMIER] = { ... },

    [BALL_HEAL] = {
        .tileTag = TAG_PARTICLES_HEALBALL,
        .paletteTag = TAG_PARTICLES_HEALBALL,
        .oam = &gOamData_AffineOff_ObjNormal_8x8,
        .anims = sAnims_BallParticles,
        .images = NULL,
        .affineAnims = gDummySpriteAffineAnimTable,
        .callback = SpriteCallbackDummy,
    },
    [BALL_DUSK] = {
        .tileTag = TAG_PARTICLES_DUSKBALL,
        .paletteTag = TAG_PARTICLES_DUSKBALL,
        .oam = &gOamData_AffineOff_ObjNormal_8x8,
        .anims = sAnims_BallParticles,
        .images = NULL,
        .affineAnims = gDummySpriteAffineAnimTable,
        .callback = SpriteCallbackDummy,
    },
    [BALL_QUICK] = {
        .tileTag = TAG_PARTICLES_QUICKBALL,
        .paletteTag = TAG_PARTICLES_QUICKBALL,
        .oam = &gOamData_AffineOff_ObjNormal_8x8,
        .anims = sAnims_BallParticles,
        .images = NULL,
        .affineAnims = gDummySpriteAffineAnimTable,
        .callback = SpriteCallbackDummy,
    },
    [BALL_PARK] = {
        .tileTag = TAG_PARTICLES_PARKBALL,
        .paletteTag = TAG_PARTICLES_PARKBALL,
        .oam = &gOamData_AffineOff_ObjNormal_8x8,
        .anims = sAnims_BallParticles,
        .images = NULL,
        .affineAnims = gDummySpriteAffineAnimTable,
        .callback = SpriteCallbackDummy,
    },
    [BALL_CHERISH] = {
        .tileTag = TAG_PARTICLES_CHERISHBALL,
        .paletteTag = TAG_PARTICLES_CHERISHBALL,
        .oam = &gOamData_AffineOff_ObjNormal_8x8,
        .anims = sAnims_BallParticles,
        .images = NULL,
        .affineAnims = gDummySpriteAffineAnimTable,
        .callback = SpriteCallbackDummy,
    },
    [BALL_DREAM] = {
        .tileTag = TAG_PARTICLES_DREAMBALL,
        .paletteTag = TAG_PARTICLES_DREAMBALL,
        .oam = &gOamData_AffineOff_ObjNormal_8x8,
        .anims = sAnims_BallParticles,
        .images = NULL,
        .affineAnims = gDummySpriteAffineAnimTable,
        .callback = SpriteCallbackDummy,
    },
    [BALL_BEAST] = {
        .tileTag = TAG_PARTICLES_BEASTBALL,
        .paletteTag = TAG_PARTICLES_BEASTBALL,
        .oam = &gOamData_AffineOff_ObjNormal_8x8,
        .anims = sAnims_BallParticles,
        .images = NULL,
        .affineAnims = gDummySpriteAffineAnimTable,
        .callback = SpriteCallbackDummy,
    },

__

in function const u16 gBallOpenFadeColors[] =
add after [BALL_PREMIER] = RGB(31, 9, 10),

    [BALL_HEAL]    = RGB(31, 23, 27),
    [BALL_DUSK]    = RGB(7, 1, 13),
    [BALL_QUICK]   = RGB(16, 25, 30),
    [BALL_PARK]    = RGB(31, 31, 15),
    [BALL_CHERISH] = RGB(25, 4, 3),
    [BALL_DREAM]   = RGB(31, 31, 15),
    [BALL_BEAST]   = RGB(16, 25, 30),

__

in function u8 ItemIdToBallId(u16 ballItem)
add after return BALL_PREMIER;

    case ITEM_HEAL_BALL:
        return BALL_HEAL;
    case ITEM_DUSK_BALL:
        return BALL_DUSK;
    case ITEM_QUICK_BALL:
        return BALL_QUICK;
    case ITEM_PARK_BALL:
        return BALL_PARK;
    case ITEM_CHERISH_BALL:
        return BALL_CHERISH;
    case ITEM_DREAM_BALL:
        return BALL_DREAM;
    case ITEM_BEAST_BALL:
        return BALL_BEAST;

src/battle_script_commands.c

This is where you can change what rules the balls have for giving bonuses

at top of file add:

#include "rtc.h"
#include "constants/species.h"

in function static void Cmd_handleballthrow(void)
replace all with:

{
    u8 ballMultiplier = 0;

    if (gBattleControllerExecFlags)
        return;

    gActiveBattler = gBattlerAttacker;
    gBattlerTarget = BATTLE_OPPOSITE(gBattlerAttacker);

    if (gBattleTypeFlags & BATTLE_TYPE_TRAINER)
    {
        BtlController_EmitBallThrowAnim(B_COMM_TO_CONTROLLER, BALL_TRAINER_BLOCK);
        MarkBattlerForControllerExec(gActiveBattler);
        gBattlescriptCurrInstr = BattleScript_TrainerBallBlock;
    }
    else if (gBattleTypeFlags & BATTLE_TYPE_WALLY_TUTORIAL)
    {
        BtlController_EmitBallThrowAnim(B_COMM_TO_CONTROLLER, BALL_3_SHAKES_SUCCESS);
        MarkBattlerForControllerExec(gActiveBattler);
        gBattlescriptCurrInstr = BattleScript_WallyBallThrow;
    }
    else
    {
        u32 odds;
        u8 catchRate;

        if (gLastUsedItem == ITEM_SAFARI_BALL)
            catchRate = gBattleStruct->safariCatchFactor * 1275 / 100;
        else
            catchRate = gSpeciesInfo[gBattleMons[gBattlerTarget].species].catchRate;

        if (gLastUsedItem == ITEM_BEAST_BALL)
        {
            switch (gBattleMons[gBattlerTarget].species)
            {
				#ifdef SPECIES_NECROZMA
				    case SPECIES_NIHILEGO:
				    case SPECIES_BUZZWHOLE:
				    case SPECIES_PHEROMOSA:
				    case SPECIES_XURKITREE:
				    case SPECIES_CELESTEELA:
				    case SPECIES_KARTANA:
				    case SPECIES_GUZZLORD:
				    case SPECIES_POIPOLE:
				    case SPECIES_NAGANADEL:
				    case SPECIES_STAKATAKA:
				    case SPECIES_BLACEPHALON:
		                ballMultiplier = 50;
                    break;

                    default:
                        ballMultiplier = 1;
                    break;
				#else
                    case SPECIES_RAIKOU:
                    case SPECIES_ENTEI:
                    case SPECIES_SUICUNE:
                        catchRate = (gSpeciesInfo[gBattleMons[gBattlerTarget].species].catchRate * 30);
		                ballMultiplier = 15;
                    break;

                    default:
                        ballMultiplier = 1;
                    break;
				#endif
            }
        }

        if (gLastUsedItem > ITEM_SAFARI_BALL)
        {
            switch (gLastUsedItem)
            {
            case ITEM_NET_BALL:
                if (IS_BATTLER_OF_TYPE(gBattlerTarget, TYPE_WATER) || IS_BATTLER_OF_TYPE(gBattlerTarget, TYPE_BUG))
                    ballMultiplier = 30;
                else
                    ballMultiplier = 10;
                break;
            case ITEM_DIVE_BALL:
                if (GetCurrentMapType() == MAP_TYPE_UNDERWATER)
                    ballMultiplier = 35;
                else
                    ballMultiplier = 10;
                break;
            case ITEM_NEST_BALL:
                if (gBattleMons[gBattlerTarget].level < 40)
                {
                    ballMultiplier = 40 - gBattleMons[gBattlerTarget].level;
                    if (ballMultiplier <= 9)
                        ballMultiplier = 10;
                }
                else
                {
                    ballMultiplier = 10;
                }
                break;
            case ITEM_REPEAT_BALL:
                if (GetSetPokedexFlag(SpeciesToNationalPokedexNum(gBattleMons[gBattlerTarget].species), FLAG_GET_CAUGHT))
                    ballMultiplier = 30;
                else
                    ballMultiplier = 10;
                break;
            case ITEM_TIMER_BALL:
                ballMultiplier = gBattleResults.battleTurnCounter + 10;
                if (ballMultiplier > 40)
                    ballMultiplier = 40;
                break;
            case ITEM_LUXURY_BALL:
            case ITEM_PREMIER_BALL:
                ballMultiplier = 10;
                break;
// sinnoh balls start
            case ITEM_HEAL_BALL:
                ballMultiplier = 10;
                break;
            case ITEM_DUSK_BALL:
                RtcCalcLocalTime();
                if ((gLocalTime.hours >= 20 && gLocalTime.hours <= 3) || gMapHeader.cave || gMapHeader.mapType == MAP_TYPE_UNDERGROUND)
                    ballMultiplier = 35;
                else
                    ballMultiplier = 10;
                break;
            case ITEM_QUICK_BALL:
                if (gBattleResults.battleTurnCounter == 0)
                    ballMultiplier = 40;
                else
                    ballMultiplier = 10;
                break;
            case ITEM_PARK_BALL:
// set map type as MAP_TYPE_UNKNOWN in porymap
                if (GetCurrentMapType() == MAP_TYPE_UNKNOWN)
                    ballMultiplier = 35;
                break;
            case ITEM_CHERISH_BALL:
                ballMultiplier = 0;
                break;
            case ITEM_DREAM_BALL:
                if (gBattleMons[gBattlerTarget].status1 & STATUS1_SLEEP)
                    ballMultiplier = 40;
                else
                    ballMultiplier = 10;
                break;
            }
        }
        else
        {
            ballMultiplier = sBallCatchBonuses[gLastUsedItem - ITEM_ULTRA_BALL];
        }

        odds = (catchRate * ballMultiplier / 10)
            * (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))
            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 (odds > 254) // mon caught
        {
            BtlController_EmitBallThrowAnim(B_COMM_TO_CONTROLLER, 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;

// sinnoh heal ball start
            if (gLastUsedItem == ITEM_HEAL_BALL)
            {
                MonRestorePP(&gEnemyParty[gBattlerPartyIndexes[gBattlerTarget]]);
                HealStatusConditions(&gEnemyParty[gBattlerPartyIndexes[gBattlerTarget]], gBattlerPartyIndexes[gBattlerTarget], STATUS1_ANY, gBattlerTarget);
                gBattleMons[gBattlerTarget].hp = gBattleMons[gBattlerTarget].maxHP;
                SetMonData(&gEnemyParty[gBattlerPartyIndexes[gBattlerTarget]], MON_DATA_HP, &gBattleMons[gBattlerTarget].hp);
            }
// sinnoh heal ball end

        }

        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(B_COMM_TO_CONTROLLER, 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;

// sinnoh heal ball start
                if (gLastUsedItem == ITEM_HEAL_BALL)
                {
                    MonRestorePP(&gEnemyParty[gBattlerPartyIndexes[gBattlerTarget]]);
                    HealStatusConditions(&gEnemyParty[gBattlerPartyIndexes[gBattlerTarget]], gBattlerPartyIndexes[gBattlerTarget], STATUS1_ANY, gBattlerTarget);
                    gBattleMons[gBattlerTarget].hp = gBattleMons[gBattlerTarget].maxHP;
                    SetMonData(&gEnemyParty[gBattlerPartyIndexes[gBattlerTarget]], MON_DATA_HP, &gBattleMons[gBattlerTarget].hp);
                }
// sinnoh heal ball end

            }
            else // not caught
            {
                gBattleCommunication[MULTISTRING_CHOOSER] = shakes;
                gBattlescriptCurrInstr = BattleScript_ShakeBallThrow;
            }
        }
    }
}

src/pokeball.c

near the top of the file, add after #define GFX_TAG_PREMIER_BALL 55011

#define GFX_TAG_HEAL_BALL    55012
#define GFX_TAG_DUSK_BALL    55013
#define GFX_TAG_QUICK_BALL   55014
#define GFX_TAG_PARK_BALL    55015
#define GFX_TAG_CHERISH_BALL 55016
#define GFX_TAG_DREAM_BALL   55017
#define GFX_TAG_BEAST_BALL   55018

__

in function const struct CompressedSpriteSheet gBallSpriteSheets[POKEBALL_COUNT] =
add after [BALL_PREMIER] = {gBallGfx_Premier, 384, GFX_TAG_PREMIER_BALL},

    [BALL_HEAL]    = {gBallGfx_Heal,    384, GFX_TAG_HEAL_BALL},
    [BALL_DUSK]    = {gBallGfx_Dusk,    384, GFX_TAG_DUSK_BALL},
    [BALL_QUICK]   = {gBallGfx_Quick,   384, GFX_TAG_QUICK_BALL},
    [BALL_PARK]    = {gBallGfx_Park,    384, GFX_TAG_PARK_BALL},
    [BALL_CHERISH] = {gBallGfx_Cherish, 384, GFX_TAG_CHERISH_BALL},
    [BALL_DREAM]   = {gBallGfx_Dream,   384, GFX_TAG_DREAM_BALL},
    [BALL_BEAST]   = {gBallGfx_Beast,   384, GFX_TAG_BEAST_BALL},

__

in function const struct CompressedSpritePalette gBallSpritePalettes[POKEBALL_COUNT] =
add after [BALL_PREMIER] = {gBallPal_Premier, GFX_TAG_PREMIER_BALL},

    [BALL_HEAL]    = {gBallPal_Heal,    GFX_TAG_HEAL_BALL},
    [BALL_DUSK]    = {gBallPal_Dusk,    GFX_TAG_DUSK_BALL},
    [BALL_QUICK]   = {gBallPal_Quick,   GFX_TAG_QUICK_BALL},
    [BALL_PARK]    = {gBallPal_Park,    GFX_TAG_PARK_BALL},
    [BALL_CHERISH] = {gBallPal_Cherish, GFX_TAG_CHERISH_BALL},
    [BALL_DREAM]   = {gBallPal_Dream,   GFX_TAG_DREAM_BALL},
    [BALL_BEAST]   = {gBallPal_Beast,   GFX_TAG_BEAST_BALL},

__

in function const struct SpriteTemplate gBallSpriteTemplates[POKEBALL_COUNT] =
add after section for [BALL_PREMIER] =

    [BALL_HEAL] =
    {
        .tileTag = GFX_TAG_HEAL_BALL,
        .paletteTag = GFX_TAG_HEAL_BALL,
        .oam = &sBallOamData,
        .anims = sBallAnimSequences,
        .images = NULL,
        .affineAnims = sAffineAnim_BallRotate,
        .callback = SpriteCB_BallThrow,
    },
    [BALL_DUSK] =
    {
        .tileTag = GFX_TAG_DUSK_BALL,
        .paletteTag = GFX_TAG_DUSK_BALL,
        .oam = &sBallOamData,
        .anims = sBallAnimSequences,
        .images = NULL,
        .affineAnims = sAffineAnim_BallRotate,
        .callback = SpriteCB_BallThrow,
    },
    [BALL_QUICK] =
    {
        .tileTag = GFX_TAG_QUICK_BALL,
        .paletteTag = GFX_TAG_QUICK_BALL,
        .oam = &sBallOamData,
        .anims = sBallAnimSequences,
        .images = NULL,
        .affineAnims = sAffineAnim_BallRotate,
        .callback = SpriteCB_BallThrow,
    },
    [BALL_PARK] =
    {
        .tileTag = GFX_TAG_PARK_BALL,
        .paletteTag = GFX_TAG_PARK_BALL,
        .oam = &sBallOamData,
        .anims = sBallAnimSequences,
        .images = NULL,
        .affineAnims = sAffineAnim_BallRotate,
        .callback = SpriteCB_BallThrow,
    },
    [BALL_CHERISH] =
    {
        .tileTag = GFX_TAG_CHERISH_BALL,
        .paletteTag = GFX_TAG_CHERISH_BALL,
        .oam = &sBallOamData,
        .anims = sBallAnimSequences,
        .images = NULL,
        .affineAnims = sAffineAnim_BallRotate,
        .callback = SpriteCB_BallThrow,
    },
    [BALL_DREAM] =
    {
        .tileTag = GFX_TAG_DREAM_BALL,
        .paletteTag = GFX_TAG_DREAM_BALL,
        .oam = &sBallOamData,
        .anims = sBallAnimSequences,
        .images = NULL,
        .affineAnims = sAffineAnim_BallRotate,
        .callback = SpriteCB_BallThrow,
    },
    [BALL_BEAST] =
    {
        .tileTag = GFX_TAG_BEAST_BALL,
        .paletteTag = GFX_TAG_BEAST_BALL,
        .oam = &sBallOamData,
        .anims = sBallAnimSequences,
        .images = NULL,
        .affineAnims = sAffineAnim_BallRotate,
        .callback = SpriteCB_BallThrow,
    },

end of guide

Before you run the make command to compile your rom, run a make clean first to make sure there's no errors with the newly added graphics files.

Clone this wiki locally