- 1,309
- Posts
- 13
- Years
- She/Her
- Seen Apr 16, 2025
The main post has been updated! Thanks for contributing :)
git remote add surskitty https://github.com/surskitty/pokeemerald
git pull surskitty flying_taxi
data/battle_scripts_ai.s
if_type_effectiveness AI_EFFECTIVENESS_x0, Score_Minus10
if_type_effectiveness AI_EFFECTIVENESS_x0, Score_Minus30
if_type_effectiveness AI_EFFECTIVENESS_x0_5, Score_Minus5
if_type_effectiveness AI_EFFECTIVENESS_x0_25, Score_Minus30
src/battle_ai_switch_items.c GetAIPartyIndexes
static bool8 HasBadOdds(void)
{
u8 opposingPosition; //Variable initialization
u8 opposingBattler;
u8 atkType1;
u8 atkType2;
u8 defType1;
u8 defType2;
u16 move;
s32 i, j;
struct Pokemon *party = NULL;
u32 typeDmg=UQ_4_12(1.0); //baseline typing damage
u16 species = GetMonData(&party[i], MON_DATA_SPECIES);
opposingPosition = BATTLE_OPPOSITE(GetBattlerPosition(gActiveBattler));
opposingBattler = GetBattlerAtPosition(opposingPosition);
atkType1 = gBattleMons[opposingBattler].type1;//Gets types of player(opposingBattler) and computer (gActiveBattler)
atkType2 = gBattleMons[opposingBattler].type2;
defType1 = gBattleMons[gActiveBattler].type1;
defType2 = gBattleMons[gActiveBattler].type2;
if (gBattleTypeFlags & BATTLE_TYPE_DOUBLE) //Won't bother configuring this for double battles. Those are complex enough.
return FALSE;
typeDmg *= UQ_4_12_TO_INT(GetTypeModifier(atkType1, defType1));//Calculates the type advantage
if (atkType2!=atkType1)
typeDmg *=UQ_4_12_TO_INT(GetTypeModifier(atkType2, defType1));
if (defType2!=defType1)
{
typeDmg *=UQ_4_12_TO_INT(GetTypeModifier(atkType1, defType2));
if (atkType2!=atkType1)
typeDmg *=UQ_4_12_TO_INT(GetTypeModifier(atkType2, defType2));
}
if (typeDmg>=UQ_4_12(2.0)) //If the player has a 2x type advantage or greater...
{
if (GetMostSuitableMonToSwitchInto()==PARTY_SIZE) //If there is no better option...
return FALSE;
if ((!HasSuperEffectiveMoveAgainstOpponents(FALSE))
&& (gBattleMons[gActiveBattler].hp >= gBattleMons[gActiveBattler].maxHP/2)) //If the computer doesn't have a super effective move AND they have >1/2 their HP...
{
for (i = 0; i < MAX_MON_MOVES; i++) //Then check their moves to see if they have a status move. If you have a status move, you probably want to use it even if you don't have the advantage.
{
move = gBattleMons[gActiveBattler].moves[i]; //List of status moves under consideration
if ((move == MOVE_REFLECT || move == MOVE_LIGHT_SCREEN
|| move == MOVE_SPIKES || move == MOVE_TOXIC_SPIKES || move == MOVE_STEALTH_ROCK || move == MOVE_STICKY_WEB || move == MOVE_LEECH_SEED
|| move == MOVE_EXPLOSION || move == MOVE_SELF_DESTRUCT
|| move == MOVE_SLEEP_POWDER || move == MOVE_YAWN || move == MOVE_LOVELY_KISS || move == MOVE_GRASS_WHISTLE || move == MOVE_HYPNOSIS
|| move == MOVE_TOXIC || move == MOVE_BANEFUL_BUNKER
|| move == MOVE_WILL_O_WISP
|| move == MOVE_TRICK || move == MOVE_TRICK_ROOM || move== MOVE_WONDER_ROOM || move == MOVE_PSYCHO_SHIFT || move == MOVE_FAKE_OUT
|| move == MOVE_STUN_SPORE || move == MOVE_THUNDER_WAVE || move == MOVE_NUZZLE || move == MOVE_GLARE
) && Random()%5<4) // (check has a 1/5 chance of failing regardless)
{
return FALSE;
}
}
*(gBattleStruct->AI_monToSwitchIntoId + gActiveBattler) = PARTY_SIZE; //Status move check failed. Let's get the Pokémon out of there.
BtlController_EmitTwoReturnValues(1, B_ACTION_SWITCH, 0);
return TRUE;
}
}
return FALSE;
}
if (availableToSwitch == 0)
return FALSE;
if (ShouldSwitchIfAllBadMoves())
return TRUE;
if (ShouldSwitchIfPerishSong())
return TRUE;
if (ShouldSwitchIfWonderGuard())
return TRUE;
if (FindMonThatAbsorbsOpponentsMove())
return TRUE;
if (availableToSwitch == 0)
return FALSE;
if (ShouldSwitchIfAllBadMoves())
return TRUE;
if (ShouldSwitchIfPerishSong())
return TRUE;
if (ShouldSwitchIfWonderGuard())
return TRUE;
if (HasBadOdds())
return TRUE;
if (FindMonThatAbsorbsOpponentsMove())
return TRUE;
bestMonId = GetBestMonDmg(party, firstId, lastId, invalidMons, opposingBattler);
if (bestMonId != PARTY_SIZE)
return bestMonId;
[Pokeemerald] Repeated Field Medicine Use
It's annoying when using potions in the field keeps returning you to the bag. So let's keep the party menu open while we have potions to spare!
![]()
I have no repo for this. The wiki page explains the simple code changes.
But this `if` statement is missing in the `ItemUseCB_Medicine` function.First, find the function `ItemUseCB_Medicine`. Inside the code block that starts with `if (ExecuteTableBasedItemEffect_(gPartyMenu.slotId, item, 0))`, replace `gTasks[taskId].func = task;` with:
In the wiki it says:
But this `if` statement is missing in the `ItemUseCB_Medicine` function.
ClearSaveData();
ClearSaveData();
DoSoftReset();
The function must have been altered. I'll look into updating the wiki.
...
Thanks! May I request a reply to this post if you update it, please?
[PokeEmerald] Grindrunning For Easier Diagonal Movement![]()
Causes the player to automatically turn left/right when running
into a wall. Thus "grinding" against any walls they press
against. This makes diagonal paths in level design much more
viable by removing the need for extra player inputs.
How to use:
- Add the code to your pokeemerald decomp as described below.
- Have the player run into walls that have diagonal free space
- Never stop running!
- Only sheer walls and small crevices will stop you!
How to add:
- git remote add pyredrid https://github.com/Pyredrid/pokeemerald.git
- git pull pyredrid grindrun
[PokeEmerald]
Improving Switching AI #2
Ok, so this is another script I cooked up that should help make battles more interesting. Essentially, how it works is that it follows a simple thought process. If the computer sends out a Pokémon with a type disadvantage, they have no super effective moves against the opponent, and they have most of their HP left, they decide to switch.
in file:below function:Code:src/battle_ai_switch_items.c
write:Code:void GetAIPartyIndexes(u32 battlerId, s32 *firstId, s32 *lastId) { if (BATTLE_TWO_VS_ONE_OPPONENT && (battlerId & BIT_SIDE) == B_SIDE_OPPONENT) { *firstId = 0, *lastId = 6; } else if (gBattleTypeFlags & (BATTLE_TYPE_TWO_OPPONENTS | BATTLE_TYPE_INGAME_PARTNER | BATTLE_TYPE_x800000)) { if ((battlerId & BIT_FLANK) == B_FLANK_LEFT) *firstId = 0, *lastId = 3; else *firstId = 3, *lastId = 6; } else { *firstId = 0, *lastId = 6; } }
Then, find:Code:static bool8 HasBadOdds(void) { u8 opposingPosition; //Variable initialization u8 opposingBattler; u8 atkType1; u8 atkType2; u8 defType1; u8 defType2; u16 move; s32 i; u32 typeDmg=UQ_4_12(1.0); //baseline typing damage opposingPosition = BATTLE_OPPOSITE(GetBattlerPosition(gActiveBattler)); opposingBattler = GetBattlerAtPosition(opposingPosition); atkType1 = gBattleMons[opposingBattler].type1;//Gets types of player(opposingBattler) and computer (gActiveBattler) atkType2 = gBattleMons[opposingBattler].type2; defType1 = gBattleMons[gActiveBattler].type1; defType2 = gBattleMons[gActiveBattler].type2; if (gBattleTypeFlags & BATTLE_TYPE_DOUBLE) //Won't bother configuring this for double battles. Those are complex enough. return FALSE; typeDmg *= UQ_4_12_TO_INT(GetTypeModifier(atkType1, defType1));//Calculates the type advantage if (atkType2!=atkType1) typeDmg *=UQ_4_12_TO_INT(GetTypeModifier(atkType2, defType1)); if (defType2!=defType1) { typeDmg *=UQ_4_12_TO_INT(GetTypeModifier(atkType1, defType2)); if (atkType2!=atkType1) typeDmg *=UQ_4_12_TO_INT(GetTypeModifier(atkType2, defType2)); } if (typeDmg>=UQ_4_12(2.0)) //If the player has a 2x type advantage or greater... { if ((!HasSuperEffectiveMoveAgainstOpponents(FALSE)) && gBattleMons[gActiveBattler].hp >= gBattleMons[gActiveBattler].maxHP / 2) //If the computer doesn't have a super effective move AND they have >1/2 their HP... { for (i = 0; i < MAX_MON_MOVES; i++) //Then check their moves to see if they have a status move. If you have a status move, you probably want to use it even if you don't have the advantage. { move = gBattleMons[gActiveBattler].moves[i]; //List of status moves under consideration if ((move == MOVE_REFLECT || move == MOVE_LIGHT_SCREEN || move == MOVE_SPIKES || move == MOVE_TOXIC_SPIKES || move == MOVE_STEALTH_ROCK || move == MOVE_STICKY_WEB || move == MOVE_LEECH_SEED || move == MOVE_EXPLOSION || move == MOVE_SELF_DESTRUCT || move == MOVE_SLEEP_POWDER || move == MOVE_YAWN || move == MOVE_LOVELY_KISS || move == MOVE_GRASS_WHISTLE || move == MOVE_HYPNOSIS || move == MOVE_TOXIC || move == MOVE_BANEFUL_BUNKER || move == MOVE_WILL_O_WISP || move == MOVE_TRICK || move == MOVE_TRICK_ROOM || move== MOVE_WONDER_ROOM || move == MOVE_PSYCHO_SHIFT || move== MOVE_FAKE_OUT || move == MOVE_STUN_SPORE || move == MOVE_THUNDER_WAVE || move == MOVE_NUZZLE || move == MOVE_GLARE )&& (Random() % 5 < 4)) // (check has a 1/5 chance of failing regardless) { return FALSE; } } *(gBattleStruct->AI_monToSwitchIntoId + gActiveBattler) = PARTY_SIZE; //Status move check failed. Let's get the Pokémon out of there. BtlController_EmitTwoReturnValues(1, B_ACTION_SWITCH, 0); return TRUE; } } return FALSE; }
and replace it with:Code:if (availableToSwitch == 0) return FALSE; if (ShouldSwitchIfAllBadMoves()) return TRUE; if (ShouldSwitchIfPerishSong()) return TRUE; if (ShouldSwitchIfWonderGuard()) return TRUE; if (FindMonThatAbsorbsOpponentsMove()) return TRUE;
And that's it! Now, only Pokémon with a decent shot at winning will stay in. Keep in mind this might produce errors if you're not using the battle engine upgrade, so make sure you're using it. This script works in tandem with the first AI switching script, but they don't rely on each other.Code:if (availableToSwitch == 0) return FALSE; if (ShouldSwitchIfAllBadMoves()) return TRUE; if (ShouldSwitchIfPerishSong()) return TRUE; if (ShouldSwitchIfWonderGuard()) return TRUE; if (HasBadOdds()) return TRUE; if (FindMonThatAbsorbsOpponentsMove()) return TRUE;
This will create an infinite loop in a situation where you have, let's say, a mudkip, and the enemy has 2 geodudes. He'll switch them each turn. There's no check for the mon to send in to be actually advantaged towards yours.
The wiki has been updated. I've also created a branch for convenience: https://github.com/ghoulslash/pokeemerald/tree/field-medicine
go togflib/text.c
RenderText
JOY_HELD(A_BUTTON | B_BUTTON) &&
switch (textPrinter->state)
{
case 0:
if ((JOY_HELD(A_BUTTON | B_BUTTON)) && subStruct->hasPrintBeenSpedUp)
textPrinter->delayCounter = 0;
if (textPrinter->delayCounter && textPrinter->textSpeed)
{
textPrinter->delayCounter--;
if (gTextFlags.canABSpeedUpPrint && (JOY_NEW(A_BUTTON | B_BUTTON)))
{
subStruct->hasPrintBeenSpedUp = TRUE;
textPrinter->delayCounter = 0;
switch (textPrinter->state)
{
case 0:
if (subStruct->hasPrintBeenSpedUp)
textPrinter->delayCounter = 0;
if (textPrinter->delayCounter && textPrinter->textSpeed)
{
textPrinter->delayCounter--;
if (gTextFlags.canABSpeedUpPrint)
{
subStruct->hasPrintBeenSpedUp = TRUE;
textPrinter->delayCounter = 0;
gflib/text.c
subStruct->autoScrollDelay = 0;
if (subStruct->autoScrollDelay == 49)
{
subStruct->autoScrollDelay = 0;
return TRUE;
}
else
bool16 TextPrinterWaitWithDownArrow(struct TextPrinter *textPrinter)
{
bool8 result = FALSE;
if (gTextFlags.autoScroll != 0)
{
result = TextPrinterWaitAutoMode(textPrinter);
}
else
{
TextPrinterDrawDownArrow(textPrinter);
if (JOY_NEW(A_BUTTON | B_BUTTON))
{
result = TRUE;
PlaySE(SE_SELECT);
}
}
return result;
}
bool16 TextPrinterWait(struct TextPrinter *textPrinter)
{
bool16 result = FALSE;
if (gTextFlags.autoScroll != 0)
{
result = TextPrinterWaitAutoMode(textPrinter);
}
bool16 TextPrinterWaitWithDownArrow(struct TextPrinter *textPrinter)
{
bool8 result = FALSE;
if (gTextFlags.autoScroll != 0 || gSaveBlock2Ptr->optionsTextMode)
{
result = TextPrinterWaitAutoMode(textPrinter);
}
else
{
TextPrinterDrawDownArrow(textPrinter);
if (JOY_NEW(A_BUTTON | B_BUTTON))
{
result = TRUE;
PlaySE(SE_SELECT);
}
}
return result;
}
bool16 TextPrinterWait(struct TextPrinter *textPrinter)
{
bool16 result = FALSE;
if (gTextFlags.autoScroll != 0 || gSaveBlock2Ptr->optionsTextMode)
{
result = TextPrinterWaitAutoMode(textPrinter);
}
bool8 optionsTextMode;
src/battle_script_commands.c
static void Cmd_waitmessage(void)
static void Cmd_pause
data/battle_scripts_1.s
BattleScript_FaintAttacker
BattleScript_FaintTarget
src/battle_main.c
static void CheckMegaEvolutionBeforeTurn(void)
{
if (!(gHitMarker & HITMARKER_RUN))
{
while (gBattleStruct->mega.battlerId < gBattlersCount)
{
gActiveBattler = gBattlerAttacker = gBattleStruct->mega.battlerId;
gBattleStruct->mega.battlerId++;
if (gBattleStruct->mega.toEvolve & gBitTable[gActiveBattler]
&& !(gProtectStructs[gActiveBattler].noValidMoves))
{
gBattleStruct->mega.toEvolve &= ~(gBitTable[gActiveBattler]);
gLastUsedItem = gBattleMons[gActiveBattler].item;
if (gBattleStruct->mega.isWishMegaEvo == TRUE){
if (gHitMarker & HITMARKER_NO_ANIMATIONS)
BattleScriptExecute(BattleScript_WishMegaEvolutionQ);
else
BattleScriptExecute(BattleScript_WishMegaEvolution);
}
else{
if (gHitMarker & HITMARKER_NO_ANIMATIONS)
BattleScriptExecute(BattleScript_MegaEvolutionQ);
else
BattleScriptExecute(BattleScript_MegaEvolution);
}
return;
}
}
}
gBattleMainFunc = CheckFocusPunch_ClearVarsBeforeTurnStarts;
gBattleStruct->focusPunchBattlerId = 0;
}
data/battle_scripts_1.s
BattleScript_MegaEvolution::
printstring STRINGID_MEGAEVOREACTING
waitmessage 0x40
setbyte gIsCriticalHit, 0
handlemegaevo BS_ATTACKER, 0
handlemegaevo BS_ATTACKER, 1
playanimation BS_ATTACKER, B_ANIM_MEGA_EVOLUTION, NULL
waitanimation
handlemegaevo BS_ATTACKER, 2
printstring STRINGID_MEGAEVOEVOLVED
waitmessage 0x40
switchinabilities BS_ATTACKER
end2
BattleScript_MegaEvolutionQ::
printstring STRINGID_MEGAEVOREACTING
waitmessage 0x40
setbyte gIsCriticalHit, 0
handlemegaevo BS_ATTACKER, 0
handlemegaevo BS_ATTACKER, 1
playanimation BS_ATTACKER, B_ANIM_FORM_CHANGE, NULL
waitanimation
handlemegaevo BS_ATTACKER, 2
printstring STRINGID_MEGAEVOEVOLVED
waitmessage 0x40
switchinabilities BS_ATTACKER
end2
BattleScript_WishMegaEvolution::
printstring STRINGID_FERVENTWISHREACHED
waitmessage 0x40
setbyte gIsCriticalHit, 0
handlemegaevo BS_ATTACKER, 0
handlemegaevo BS_ATTACKER, 1
playanimation BS_ATTACKER, B_ANIM_MEGA_EVOLUTION, NULL
waitanimation
handlemegaevo BS_ATTACKER, 2
printstring STRINGID_MEGAEVOEVOLVED
waitmessage 0x40
switchinabilities BS_ATTACKER
end2
BattleScript_WishMegaEvolutionQ::
printstring STRINGID_FERVENTWISHREACHED
waitmessage 0x40
setbyte gIsCriticalHit, 0
handlemegaevo BS_ATTACKER, 0
handlemegaevo BS_ATTACKER, 1
playanimation BS_ATTACKER, B_ANIM_FORM_CHANGE, NULL
waitanimation
handlemegaevo BS_ATTACKER, 2
printstring STRINGID_MEGAEVOEVOLVED
waitmessage 0x40
switchinabilities BS_ATTACKER
end2
include/battle_scripts.h
extern const u8 BattleScript_MegaEvolution[];
extern const u8 BattleScript_MegaEvolutionQ[];
extern const u8 BattleScript_WishMegaEvolution[];
extern const u8 BattleScript_WishMegaEvolutionQ[];
[Pokeemerald] DS-Style party screen (2 versions)
Changes the party screen to one similar to the DS versions. 2 different layouts available!
V1:![]()
V2:![]()
How to:
V1: Have a look at the following commit for the main implementation LINK.
V2: Implement the first version and the have a look at the commit LINK
Or clone it into your repo:
For version 1:Code:git remote add xaman https://github.com/TheXaman/pokeemerald/
For version 2:Code:git pull xaman party_screen_ds_style
Code:git pull xaman party_screen_ds_style2
Thats it! If you find any bugs, let me know.
If you want to change the look of the windows you need to alter graphics/interface/party_menu_bg.png
[EM] Register Items with LR
L and R are useless in the overworld, so let's give them something to do! This lets you register key items to L, R, and Select (which may be overkill but ah well)
![]()
Steps:
1. Pull from my repo branch
2. that's it. I may add code changes here if requested.
I have a request for the code changes!
There's a bit too many merge conflicts in my hack otherwise
static void SetMoveTypeIcons(void)
static void SetMoveTypeIcons(void)
{
u8 i;
struct PokeSummary *summary = &sMonSummaryScreen->summary;
struct Pokemon *mon = &sMonSummaryScreen->currentMon;
u16 species = GetMonData(mon, MON_DATA_SPECIES);
for (i = 0; i < MAX_MON_MOVES; i++)
{
if (summary->moves[i] != MOVE_NONE) {
if (summary->moves[i] == MOVE_HIDDEN_POWER) {
u8 typeBits = ((GetMonData(mon, MON_DATA_HP_IV) & 1) << 0)
| ((GetMonData(mon, MON_DATA_ATK_IV) & 1) << 1)
| ((GetMonData(mon, MON_DATA_DEF_IV) & 1) << 2)
| ((GetMonData(mon, MON_DATA_SPEED_IV) & 1) << 3)
| ((GetMonData(mon, MON_DATA_SPATK_IV) & 1) << 4)
| ((GetMonData(mon, MON_DATA_SPDEF_IV) & 1) << 5);
u8 type = (15 * typeBits) / 63 + 1;
if (type >= TYPE_MYSTERY)
type++;
type |= 0xC0;
SetTypeSpritePosAndPal(type & 0x3F, 85, 32 + (i * 16), i + SPRITE_ARR_ID_TYPE);
} else {
SetTypeSpritePosAndPal(gBattleMoves[summary->moves[i]].type, 85, 32 + (i * 16), i + SPRITE_ARR_ID_TYPE);
}
}
else
SetSpriteInvisibility(i + SPRITE_ARR_ID_TYPE, TRUE);
}
}
static void SetNewMoveTypeIcon(void)
static void SetNewMoveTypeIcon(void)
{
struct Pokemon *mon = &sMonSummaryScreen->currentMon;
u16 species = GetMonData(mon, MON_DATA_SPECIES);
if (sMonSummaryScreen->newMove == MOVE_NONE)
{
SetSpriteInvisibility(SPRITE_ARR_ID_TYPE + 4, TRUE);
}
else
{
if (sMonSummaryScreen->currPageIndex == PSS_PAGE_BATTLE_MOVES)
if (sMonSummaryScreen->newMove == MOVE_HIDDEN_POWER) {
u8 typeBits = ((GetMonData(mon, MON_DATA_HP_IV) & 1) << 0)
| ((GetMonData(mon, MON_DATA_ATK_IV) & 1) << 1)
| ((GetMonData(mon, MON_DATA_DEF_IV) & 1) << 2)
| ((GetMonData(mon, MON_DATA_SPEED_IV) & 1) << 3)
| ((GetMonData(mon, MON_DATA_SPATK_IV) & 1) << 4)
| ((GetMonData(mon, MON_DATA_SPDEF_IV) & 1) << 5);
u8 type = (15 * typeBits) / 63 + 1;
if (type >= TYPE_MYSTERY)
type++;
type |= 0xC0;
SetTypeSpritePosAndPal(type & 0x3F, 85, 96, SPRITE_ARR_ID_TYPE + 4);
} else {
SetTypeSpritePosAndPal(gBattleMoves[sMonSummaryScreen->newMove].type, 85, 96, SPRITE_ARR_ID_TYPE + 4);
}
else
SetTypeSpritePosAndPal(NUMBER_OF_MON_TYPES + gContestMoves[sMonSummaryScreen->newMove].contestCategory, 85, 96, SPRITE_ARR_ID_TYPE + 4);
}
}