Table of Contents
- How To Add To Your Hack
- data/battle_scripts_2.s
- include/constants/items.h
- include/graphics.h
- include/pokeball.h
- include/pokemon.h
- src/data/graphics/items.h
- src/data/graphics/pokeballs.c
- src/data/text/item_descriptions.h
- src/data/items.h
- src/data/item_icon_table.h
- src/battle_anim_throw.c
- src/battle_script_commands.c
- src/pokeball.c
- src/pokemon.c
Shining Ball
Inspired by the Shiny Ball from Pokemon Prism, this new pokeball will 100% capture a wild/scripted pokemon battle and convert it to a shiny, with a shiny sparkle and tinkle SE on capture, and a new sprite using the strange ball from RHH's emerald-expansion.
This code was created by a friend of mine, MewInfinity, full credit to them
ball image png and palettes sourced from the Strange Ball from RHH's pokeemerald-expansion
Before starting this guide:
if you have already added pokeballs or new items into your hack, adjust any numbers or line placings as appropriate
and always keep a backup
How To Add To Your Hack
pull from my git branch or add manually using the below code
git remote add tenaya15 https://github.com/tenaya15/tenaya15emerald.git
git pull tenaya15 shiny_ball
first, download this zip file and extract the graphics folder to your root directory.
This will place the needed image and palette files into the correct folders.
data/battle_scripts_2.s
in the first section
add after .4byte BattleScript_BallThrow @ ITEM_PREMIER_BALL
.4byte BattleScript_ShinyBallThrow @ ITEM_SHINING_BALL
then add to the end of the file:
BattleScript_ShinyBallThrow::
printstring STRINGID_PLAYERUSEDITEM
handleballthrow
include/constants/items.h
IF YOU USE AN UP-TO-DATE POKEEMERALD THAT USES enum FOR ITEMS:
delete ITEM_034,
and add after ITEM_PREMIER_BALL,
ITEM_SHINING_BALL,
IF YOU USE AN OLDER POKEEMERALD THAT STILL USES NUMBERS FOR DEFINING ITEMS:
delete #define ITEM_034 52 and move everything from after the balls down 1 number
#define ITEM_SHINING_BALL 52
FOR BOTH OLD AND UP-TO-DATE DECOMPS:
change #define LAST_BALL ITEM_PREMIER_BALL to
#define LAST_BALL ITEM_SHINING_BALL
include/graphics.h
add to the end of file, before #endif //GUARD_GRAPHICS_H
// ITEM_SHINING_BALL
extern const u32 gBallGfx_Shining[];
extern const u32 gBallPal_Shining[];
extern const u32 gItemIcon_ShiningBall[];
extern const u32 gItemIconPalette_ShiningBall[];
include/pokeball.h
in the first enum list
add after BALL_PREMIER,
BALL_SHINING,
include/pokemon.h
add to end of file, before #endif // GUARD_POKEMON_H
bool8 SetMonPersonalitySafely(struct Pokemon *mon, u32 personality);
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:
// ITEM_SHINING_BALL
const u32 gItemIcon_ShiningBall[] = INCGFX_U32("graphics/items/icons/shining_ball.png", ".4bpp.lz");
const u32 gItemIconPalette_ShiningBall[] = INCGFX_U32("graphics/items/icon_palettes/shining_ball.pal", ".gbapal.lz");
OR
IF YOU HAVE AN OLDER VERSION THAT STILL USES INCBIN
add to the end of the file:
// ITEM_SHINING_BALL
const u32 gItemIcon_ShiningBall[] = INCBIN_U32("graphics/items/icons/shining_ball.4bpp.lz");
const u32 gItemIconPalette_ShiningBall[] = INCBIN_U32("graphics/items/icon_palettes/shining_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:
// ITEM_SHINING_BALL
const u32 gBallGfx_Shining[] = INCGFX_U32("graphics/balls/shining.png", ".4bpp.lz");
const u32 gBallPal_Shining[] = INCGFX_U32("graphics/balls/shining.pal", ".gbapal.lz");
OR
IF YOU HAVE AN OLDER VERSION THAT STILL USES INCBIN
add to the end of the file:
// ITEM_SHINING_BALL
const u32 gBallGfx_Shining[] = INCBIN_U32("graphics/balls/shining.4bpp.lz");
const u32 gBallPal_Shining[] = INCBIN_U32("graphics/balls/shining.gbapal.lz");
src/data/text/item_descriptions.h
add to the end of the file
static const u8 sShiningBallDesc[] = _(
"A super-rare BALL\n"
"that somehow makes\n"
"a Pokémon shiny!");
note: if you store custom item descriptions in a different file, add it there instead.
src/data/items.h
delete the entire section for [ITEM_034] = including both { and },
add after the section for [ITEM_PREMIER_BALL] =
[ITEM_SHINING_BALL] =
{
.name = _("SHINING BALL"),
.itemId = ITEM_SHINING_BALL,
.price = 0,
.description = sShiningBallDesc,
.pocket = POCKET_POKE_BALLS,
.type = ITEM_SHINING_BALL - FIRST_BALL,
.fieldUseFunc = ItemUseOutOfBattle_CannotUse,
.battleUsage = ITEM_B_USE_OTHER,
.battleUseFunc = ItemUseInBattle_PokeBall,
.secondaryId = ITEM_SHINING_BALL - FIRST_BALL,
},
src/data/item_icon_table.h
add after [ITEM_PREMIER_BALL] = {gItemIcon_PremierBall, gItemIconPalette_LuxuryBall},
[ITEM_SHINING_BALL] = {gItemIcon_ShiningBall, gItemIconPalette_ShiningBall},
then delete line [ITEM_034] = {gItemIcon_QuestionMark, gItemIconPalette_QuestionMark},
src/battle_anim_throw.c
add after #define TAG_PARTICLES_PREMIERBALL 55031
#define TAG_PARTICLES_SHININGBALL 55032
__
in function static const struct CompressedSpriteSheet sBallParticleSpriteSheets[POKEBALL_COUNT] =
add after [BALL_PREMIER] = {gBattleAnimSpriteGfx_Particles, 0x100, TAG_PARTICLES_PREMIERBALL},
[BALL_SHINING] = {gBattleAnimSpriteGfx_Particles, 0x100, TAG_PARTICLES_SHININGBALL},
__
in function static const struct CompressedSpritePalette sBallParticlePalettes[POKEBALL_COUNT] =
add after [BALL_PREMIER] = {gBattleAnimSpritePal_CircleImpact, TAG_PARTICLES_PREMIERBALL},
[BALL_SHINING] = {gBattleAnimSpritePal_CircleImpact, TAG_PARTICLES_SHININGBALL},
__
in function static const u8 sBallParticleAnimNums[POKEBALL_COUNT] =
add after [BALL_PREMIER] = 4,
[BALL_SHINING] = 1,
__
in function static const TaskFunc sBallParticleAnimationFuncs[POKEBALL_COUNT] =
add after [BALL_PREMIER] = PremierBallOpenParticleAnimation,
[BALL_SHINING] = MasterBallOpenParticleAnimation,
__
in function static const struct SpriteTemplate sBallParticleSpriteTemplates[POKEBALL_COUNT] =
add after the section for [BALL_PREMIER] = { ... }
[BALL_SHINING] = {
.tileTag = TAG_PARTICLES_SHININGBALL,
.paletteTag = TAG_PARTICLES_SHININGBALL,
.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_SHINING] = RGB(31, 27, 8),
__
in function u8 ItemIdToBallId(u16 ballItem)
add after return BALL_PREMIER; (line 754 in an un-modified file)
case ITEM_SHINING_BALL:
return BALL_SHINING;
__
in function static void SpriteCB_Ball_Capture_Step(struct Sprite *sprite)
add after the final else if section and before the final } of the function (else if (sprite->sTimer == 315) { ... })
if (gLastUsedItem == ITEM_SHINING_BALL)
{
struct Pokemon *mon =
&gEnemyParty[gBattlerPartyIndexes[gBattleAnimTarget]];
TryShinyAnimation(gBattleAnimTarget, mon);
}
src/battle_script_commands.c
in function static void Cmd_handleballthrow(void) (at line 9905 in an unmodified file)
in the first else section
add inbetween u8 catchRate; and if (gLastUsedItem == ITEM_SAFARI_BALL)
if (gLastUsedItem == ITEM_SHINING_BALL)
{
struct Pokemon *mon;
u32 otId;
u32 personality;
mon = &gEnemyParty[gBattlerPartyIndexes[gBattlerTarget]];
otId = GetMonData(mon, MON_DATA_OT_ID);
do
{
personality = Random32();
} while (!IsShinyOtIdPersonality(otId, personality));
if (!SetMonPersonalitySafely(mon, personality))
{
gBattlescriptCurrInstr = BattleScript_BallThrow;
return;
}
CalculateMonStats(mon);
if (!IsMonShiny(mon))
{
gBattlescriptCurrInstr = BattleScript_BallThrow;
return;
}
SetMonData(mon, MON_DATA_POKEBALL, &gLastUsedItem);
BtlController_EmitBallThrowAnim(B_COMM_TO_CONTROLLER, BALL_3_SHAKES_SUCCESS);
MarkBattlerForControllerExec(gActiveBattler);
gBattlescriptCurrInstr = BattleScript_SuccessBallThrow;
if (CalculatePlayerPartyCount() == PARTY_SIZE)
gBattleCommunication[MULTISTRING_CHOOSER] = 0;
else
gBattleCommunication[MULTISTRING_CHOOSER] = 1;
gBattleSpritesDataPtr->healthBoxesData[gBattlerTarget].triedShinyMonAnim = FALSE;
gBattleSpritesDataPtr->healthBoxesData[gBattlerTarget].finishedShinyMonAnim = FALSE;
return;
}
src/pokeball.c
add after #define GFX_TAG_PREMIER_BALL 55011
#define GFX_TAG_SHINING_BALL 55012
in function const struct CompressedSpriteSheet gBallSpriteSheets[POKEBALL_COUNT] =
add after [BALL_PREMIER] = {gBallGfx_Premier, 384, GFX_TAG_PREMIER_BALL},
[BALL_SHINING] = {gBallGfx_Shining, 384, GFX_TAG_SHINING_BALL},
in function const struct CompressedSpritePalette gBallSpritePalettes[POKEBALL_COUNT] =
add after [BALL_PREMIER] = {gBallPal_Premier, GFX_TAG_PREMIER_BALL},
[BALL_SHINING] = {gBallPal_Shining, GFX_TAG_SHINING_BALL},
in function const struct SpriteTemplate gBallSpriteTemplates[POKEBALL_COUNT] =
add after section for [BALL_PREMIER] = { ... },
[BALL_SHINING] =
{
.tileTag = GFX_TAG_SHINING_BALL,
.paletteTag = GFX_TAG_SHINING_BALL,
.oam = &sBallOamData,
.anims = sBallAnimSequences,
.images = NULL,
.affineAnims = sAffineAnim_BallRotate,
.callback = SpriteCB_BallThrow,
},
src/pokemon.c
add after the section for function static union PokemonSubstruct *GetSubstruct(struct BoxPokemon *boxMon, u32 personality, u8 substructType)
bool8 SetMonPersonalitySafely(struct Pokemon *mon, u32 personality)
{
struct BoxPokemon *boxMon = &mon->box;
union PokemonSubstruct substructs[4];
u32 oldPersonality = boxMon->personality;
u8 i;
if (oldPersonality == personality)
return TRUE;
DecryptBoxMon(boxMon);
if (CalculateBoxMonChecksum(boxMon) != boxMon->checksum)
{
EncryptBoxMon(boxMon);
return FALSE;
}
for (i = 0; i < ARRAY_COUNT(substructs); i++)
{
memcpy(&substructs[i],
GetSubstruct(boxMon, oldPersonality, i),
sizeof(union PokemonSubstruct));
}
boxMon->personality = personality;
for (i = 0; i < ARRAY_COUNT(substructs); i++)
{
memcpy(GetSubstruct(boxMon, personality, i),
&substructs[i],
sizeof(union PokemonSubstruct));
}
boxMon->checksum = CalculateBoxMonChecksum(boxMon);
EncryptBoxMon(boxMon);
return TRUE;
}
For best results, run make clean before running make
This will clear out any potential artifact errors