- 183
- Posts
- 7
- Years
- Seen Jan 22, 2025
Hi! Sometime ago, when I graduated from a Whack a Hack! RGSS scripting school, I made a Catch Combo system for Essentials. And now, I ported it to pokeemerald! (big codes are put in spoilers for easier access and navigation through the page)
This version of the Catch Combo System includes:
Below "static const u8 *GetTrainerCantBattleSpeech(void);"
Once we do that, we replace the whole "static void CB2_EndWildBattle(void)" definition with this one:
Then, we add this variables in "include/constants/vars.h"
VAR_CHAIN
and
VAR_SPECIESCHAINED
We then go to src/pokemon.c and we replace the whole "
this if we don't have the Eggg item expansion and battle engine
But if we do have them, we replace it with this one:
Then, we go to "data/scripts/" and we create there an archive named "chain.inc", and inside we add this;
And we have to define it on "data/event_scripts.s", adding this at the end:
.include "data/scripts/chain.inc"
And with that, we have it all done. If I missed something or if there's a way to improve the code, let me know!
This version of the Catch Combo System includes:
- Chain a Pokémon, with 30 as the maximum chain possible
- Shiny Pokémon appear more often with the level of the chain!
- A message box appears at the end of a Wild Battle when the chain is 3 or bigger, so it could appear and be noticed for all players
- When a different Pokémon appears, you can run away and the chain continues!
- Catching OR defeating the same Pokémon you did before will continue the chain
- Modify the encounter tables when going forward in the chain
- Modify the IV's of the Pokemon (probably gonna add it tomorrow)
- Make the chained Pokémon appear more often with the level of the chain
- Make (a) special Pokémon sometimes appear at higher chain levels
Code:
extern const u8 ChainNumber[];
extern const u8 AddChain[];
Once we do that, we replace the whole "static void CB2_EndWildBattle(void)" definition with this one:
Spoiler:
Code:
static void CB2_EndWildBattle(void)
{
u16 species;
u16 ptr;
u8 nickname[POKEMON_NAME_LENGTH + 1];
u16 lastPokemonFound;
species = GetMonData(&gEnemyParty[0], MON_DATA_SPECIES);
CpuFill16(0, (void*)(BG_PLTT), BG_PLTT_SIZE);
ResetOamRange(0, 128);
if (IsPlayerDefeated(gBattleOutcome) == TRUE && !InBattlePyramid() && !InBattlePike())
{
SetMainCallback2(CB2_WhiteOut);
}
else
{
if (gBattleOutcome != (B_OUTCOME_WON || B_OUTCOME_CAUGHT))
{
if (species == VarGet(VAR_SPECIESCHAINED) && VarGet(VAR_CHAIN) >= 1)
{
VarSet(VAR_CHAIN,0);
VarSet(VAR_SPECIESCHAINED,0);
}
else if ((species != VarGet(VAR_SPECIESCHAINED)) && (VarGet(VAR_CHAIN) >= 1))
species = VarGet(VAR_SPECIESCHAINED);
}
else if (gBattleOutcome == (B_OUTCOME_WON || B_OUTCOME_CAUGHT))
{
if (VarGet(VAR_CHAIN) == 0)
{
VarSet(VAR_SPECIESCHAINED,species);
ScriptContext1_SetupScript(AddChain);
}
else if ((species == VarGet(VAR_SPECIESCHAINED)) && VarGet(VAR_CHAIN) >=3)
{
GetSpeciesName(gStringVar2 ,VarGet(VAR_SPECIESCHAINED));
ScriptContext1_SetupScript(ChainNumber);
}
else if ((species == VarGet(VAR_SPECIESCHAINED)) && (VarGet(VAR_CHAIN) ==2 || VarGet(VAR_CHAIN) ==1))
ScriptContext1_SetupScript(AddChain);
else if ((species != VarGet(VAR_SPECIESCHAINED)) && (VarGet(VAR_CHAIN) != 0))
VarSet(VAR_CHAIN,0);
VarSet(VAR_SPECIESCHAINED,species);
}
SetMainCallback2(CB2_ReturnToField);
gFieldCallback = sub_80AF6F0;
}
}
VAR_CHAIN
and
VAR_SPECIESCHAINED
We then go to src/pokemon.c and we replace the whole "
this if we don't have the Eggg item expansion and battle engine
Spoiler:
Code:
void CreateBoxMon(struct BoxPokemon *boxMon, u16 species, u8 level, u8 fixedIV, u8 hasFixedPersonality, u32 fixedPersonality, u8 otIdType, u32 fixedOtId)
{
u8 speciesName[POKEMON_NAME_LENGTH + 1];
u32 personality;
u32 value;
u16 checksum;
ZeroBoxMonData(boxMon);
if (hasFixedPersonality)
personality = fixedPersonality;
else
personality = Random32();
//Determine original trainer ID
if (otIdType == OT_ID_RANDOM_NO_SHINY) //Pokemon cannot be shiny
{
u32 shinyValue;
do
{
value = Random32();
shinyValue = HIHALF(value) ^ LOHALF(value) ^ HIHALF(personality) ^ LOHALF(personality);
} while (shinyValue < SHINY_ODDS);
}
else if (otIdType == OT_ID_PRESET) //Pokemon has a preset OT ID
{
value = fixedOtId;
}
else //Player is the OT
{
value = gSaveBlock2Ptr->playerTrainerId[0]
| (gSaveBlock2Ptr->playerTrainerId[1] << 8)
| (gSaveBlock2Ptr->playerTrainerId[2] << 16)
| (gSaveBlock2Ptr->playerTrainerId[3] << 24);
if (VarGet(VAR_CHAIN) >=2)
{
u32 shinyValue;
u32 rolls = 0;
do
{
personality = Random32();
shinyValue = HIHALF(value) ^ LOHALF(value) ^ HIHALF(personality) ^ LOHALF(personality);
rolls++;
} while (shinyValue >= SHINY_ODDS && rolls <= (VarGet(VAR_CHAIN)));
}
}
SetBoxMonData(boxMon, MON_DATA_PERSONALITY, &personality);
SetBoxMonData(boxMon, MON_DATA_OT_ID, &value);
checksum = CalculateBoxMonChecksum(boxMon);
SetBoxMonData(boxMon, MON_DATA_CHECKSUM, &checksum);
EncryptBoxMon(boxMon);
GetSpeciesName(speciesName, species);
SetBoxMonData(boxMon, MON_DATA_NICKNAME, speciesName);
SetBoxMonData(boxMon, MON_DATA_LANGUAGE, &gGameLanguage);
SetBoxMonData(boxMon, MON_DATA_OT_NAME, gSaveBlock2Ptr->playerName);
SetBoxMonData(boxMon, MON_DATA_SPECIES, &species);
SetBoxMonData(boxMon, MON_DATA_EXP, &gExperienceTables[gBaseStats[species].growthRate][level]);
SetBoxMonData(boxMon, MON_DATA_FRIENDSHIP, &gBaseStats[species].friendship);
value = GetCurrentRegionMapSectionId();
SetBoxMonData(boxMon, MON_DATA_MET_LOCATION, &value);
SetBoxMonData(boxMon, MON_DATA_MET_LEVEL, &level);
SetBoxMonData(boxMon, MON_DATA_MET_GAME, &gGameVersion);
value = ITEM_POKE_BALL;
SetBoxMonData(boxMon, MON_DATA_POKEBALL, &value);
SetBoxMonData(boxMon, MON_DATA_OT_GENDER, &gSaveBlock2Ptr->playerGender);
if (fixedIV < 32)
{
SetBoxMonData(boxMon, MON_DATA_HP_IV, &fixedIV);
SetBoxMonData(boxMon, MON_DATA_ATK_IV, &fixedIV);
SetBoxMonData(boxMon, MON_DATA_DEF_IV, &fixedIV);
SetBoxMonData(boxMon, MON_DATA_SPEED_IV, &fixedIV);
SetBoxMonData(boxMon, MON_DATA_SPATK_IV, &fixedIV);
SetBoxMonData(boxMon, MON_DATA_SPDEF_IV, &fixedIV);
}
else
{
u32 iv;
value = Random();
iv = value & 0x1F;
SetBoxMonData(boxMon, MON_DATA_HP_IV, &iv);
iv = (value & 0x3E0) >> 5;
SetBoxMonData(boxMon, MON_DATA_ATK_IV, &iv);
iv = (value & 0x7C00) >> 10;
SetBoxMonData(boxMon, MON_DATA_DEF_IV, &iv);
value = Random();
iv = value & 0x1F;
SetBoxMonData(boxMon, MON_DATA_SPEED_IV, &iv);
iv = (value & 0x3E0) >> 5;
SetBoxMonData(boxMon, MON_DATA_SPATK_IV, &iv);
iv = (value & 0x7C00) >> 10;
SetBoxMonData(boxMon, MON_DATA_SPDEF_IV, &iv);
}
if (gBaseStats[species].abilities[1])
{
value = personality & 1;
SetBoxMonData(boxMon, MON_DATA_ABILITY_NUM, &value);
}
GiveBoxMonInitialMoveset(boxMon);
}
Spoiler:
Code:
void CreateBoxMon(struct BoxPokemon *boxMon, u16 species, u8 level, u8 fixedIV, u8 hasFixedPersonality, u32 fixedPersonality, u8 otIdType, u32 fixedOtId)
{
u8 speciesName[POKEMON_NAME_LENGTH + 1];
u32 personality;
u32 value;
u16 checksum;
ZeroBoxMonData(boxMon);
if (hasFixedPersonality)
personality = fixedPersonality;
else
personality = Random32();
//Determine original trainer ID
if (otIdType == OT_ID_RANDOM_NO_SHINY) //Pokemon cannot be shiny
{
u32 shinyValue;
do
{
value = Random32();
shinyValue = HIHALF(value) ^ LOHALF(value) ^ HIHALF(personality) ^ LOHALF(personality);
} while (shinyValue < SHINY_ODDS);
}
else if (otIdType == OT_ID_PRESET) //Pokemon has a preset OT ID
{
value = fixedOtId;
}
else //Player is the OT
{
value = gSaveBlock2Ptr->playerTrainerId[0]
| (gSaveBlock2Ptr->playerTrainerId[1] << 8)
| (gSaveBlock2Ptr->playerTrainerId[2] << 16)
| (gSaveBlock2Ptr->playerTrainerId[3] << 24);
if (CheckBagHasItem(ITEM_SHINY_CHARM, 1) && (VarGet(VAR_CHAIN) >=2))
{
u32 shinyValue;
u32 rolls = 0;
do
{
personality = Random32();
shinyValue = HIHALF(value) ^ LOHALF(value) ^ HIHALF(personality) ^ LOHALF(personality);
rolls++;
} while (shinyValue >= SHINY_ODDS && rolls < (SHINY_CHARM_REROLLS + (VarGet(VAR_CHAIN))));
}
else if (CheckBagHasItem(ITEM_SHINY_CHARM, 1))
{
u32 shinyValue;
u32 rolls = 0;
do
{
personality = Random32();
shinyValue = HIHALF(value) ^ LOHALF(value) ^ HIHALF(personality) ^ LOHALF(personality);
rolls++;
} while (shinyValue >= SHINY_ODDS && rolls < SHINY_CHARM_REROLLS);
}
else if (VarGet(VAR_CHAIN) >=2)
{
u32 shinyValue;
u32 rolls = 0;
do
{
personality = Random32();
shinyValue = HIHALF(value) ^ LOHALF(value) ^ HIHALF(personality) ^ LOHALF(personality);
rolls++;
} while (shinyValue >= SHINY_ODDS && rolls <= (VarGet(VAR_CHAIN)));
}
}
SetBoxMonData(boxMon, MON_DATA_PERSONALITY, &personality);
SetBoxMonData(boxMon, MON_DATA_OT_ID, &value);
checksum = CalculateBoxMonChecksum(boxMon);
SetBoxMonData(boxMon, MON_DATA_CHECKSUM, &checksum);
EncryptBoxMon(boxMon);
GetSpeciesName(speciesName, species);
SetBoxMonData(boxMon, MON_DATA_NICKNAME, speciesName);
SetBoxMonData(boxMon, MON_DATA_LANGUAGE, &gGameLanguage);
SetBoxMonData(boxMon, MON_DATA_OT_NAME, gSaveBlock2Ptr->playerName);
SetBoxMonData(boxMon, MON_DATA_SPECIES, &species);
SetBoxMonData(boxMon, MON_DATA_EXP, &gExperienceTables[gBaseStats[species].growthRate][level]);
SetBoxMonData(boxMon, MON_DATA_FRIENDSHIP, &gBaseStats[species].friendship);
value = GetCurrentRegionMapSectionId();
SetBoxMonData(boxMon, MON_DATA_MET_LOCATION, &value);
SetBoxMonData(boxMon, MON_DATA_MET_LEVEL, &level);
SetBoxMonData(boxMon, MON_DATA_MET_GAME, &gGameVersion);
value = ITEM_POKE_BALL;
SetBoxMonData(boxMon, MON_DATA_POKEBALL, &value);
SetBoxMonData(boxMon, MON_DATA_OT_GENDER, &gSaveBlock2Ptr->playerGender);
if (fixedIV < 32)
{
SetBoxMonData(boxMon, MON_DATA_HP_IV, &fixedIV);
SetBoxMonData(boxMon, MON_DATA_ATK_IV, &fixedIV);
SetBoxMonData(boxMon, MON_DATA_DEF_IV, &fixedIV);
SetBoxMonData(boxMon, MON_DATA_SPEED_IV, &fixedIV);
SetBoxMonData(boxMon, MON_DATA_SPATK_IV, &fixedIV);
SetBoxMonData(boxMon, MON_DATA_SPDEF_IV, &fixedIV);
}
else
{
u32 iv;
value = Random();
iv = value & 0x1F;
SetBoxMonData(boxMon, MON_DATA_HP_IV, &iv);
iv = (value & 0x3E0) >> 5;
SetBoxMonData(boxMon, MON_DATA_ATK_IV, &iv);
iv = (value & 0x7C00) >> 10;
SetBoxMonData(boxMon, MON_DATA_DEF_IV, &iv);
value = Random();
iv = value & 0x1F;
SetBoxMonData(boxMon, MON_DATA_SPEED_IV, &iv);
iv = (value & 0x3E0) >> 5;
SetBoxMonData(boxMon, MON_DATA_SPATK_IV, &iv);
iv = (value & 0x7C00) >> 10;
SetBoxMonData(boxMon, MON_DATA_SPDEF_IV, &iv);
}
if (gBaseStats[species].abilities[1])
{
value = personality & 1;
SetBoxMonData(boxMon, MON_DATA_ABILITY_NUM, &value);
}
GiveBoxMonInitialMoveset(boxMon);
}
Code:
ChainNumber::
addvar VAR_CHAIN, 1
buffernumberstring 0, VAR_CHAIN
msgbox TextoCool, 2
end
TextoCool::
.string "Chain Level: {STR_VAR_1}\n chained :{STR_VAR_2}.$"
AddChain::
addvar VAR_CHAIN, 1
end
.include "data/scripts/chain.inc"
And with that, we have it all done. If I missed something or if there's a way to improve the code, let me know!
Last edited: