• Our software update is now concluded. You will need to reset your password to log in. In order to do this, you will have to click "Log in" in the top right corner and then "Forgot your password?".
  • Welcome to PokéCommunity! Register now and join one of the best fan communities on the 'net to talk Pokémon and more! We are not affiliated with The Pokémon Company or Nintendo.

Simple Modifications Directory

11
Posts
3
Years
  • Age 33
  • Seen Jul 12, 2021
Flying Taxis

Make NPCs that are able to fly you around, similar to the SwSh Corviknight cabbies.

Help wanted for the animations; look in src/field_effect.c and please let me know if you work out something nicer.

How to use: In the script for a cabbie NPC, call Common_EventScript_FlyingTaxi.


Code:
git remote add surskitty https://github.com/surskitty/pokeemerald
git pull surskitty flying_taxi
 
118
Posts
4
Years
[PokeEmerald]
Improving Switching AI
So, this is a very simple hack that will be helpful if your hack aims for more complicated AI. Essentially, in real life, if you send in a Pokémon, but all of your moves aren't very effective against your opponent, you'd probably want to switch. Gen III AI would disagree. This is where this hack comes in.
in:
Code:
data/battle_scripts_ai.s
highlight this:
Code:
if_type_effectiveness AI_EFFECTIVENESS_x0, Score_Minus10
and replace it with this:
Code:
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
If you're curious, I'll explain how this works.
Essentially, each move is given a point value depending on how good they would be against the target. The default is 100, and points are added or subtracted from this total depending on the context (type, ability, move strength, etc...). However, the programmers never thought to add a consideration for whether the move is not very effective (it only adds points for super effective and removes points for 0x effective). This script makes it so that .5x effective subtracts 5 points and .25x/0x effective subtracts 30. Now, let me explain how switching depending on moves works. From what I can tell, if a Pokémon is sufficiently strong (all stats add up to over 350) and they have more than 1/2 of their HP left, then the games checks to see if the AI flag "Check viability" is set. If it is, the score must be greater than 95 for the game to stop checking for bad moves. If it's not, then the score must be greater than 93. If no score is greater than 95/93, then the Pokémon is switched out.

This is a simple script, but it will prevent the AI from staying in when they have no chance of winning.
 
118
Posts
4
Years
[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.
below function:
Code:
src/battle_ai_switch_items.c GetAIPartyIndexes
write:
Code:
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;
}
Then, find:
Code:
if (availableToSwitch == 0)
        return FALSE;
    if (ShouldSwitchIfAllBadMoves())
        return TRUE;
    if (ShouldSwitchIfPerishSong())
        return TRUE;
    if (ShouldSwitchIfWonderGuard())
        return TRUE;
    if (FindMonThatAbsorbsOpponentsMove())
        return TRUE;
and replace it with:
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;
Now, go to GetMostSuitableMonToSwitchInto, delete this:
Code:
    bestMonId = GetBestMonDmg(party, firstId, lastId, invalidMons, opposingBattler);
    if (bestMonId != PARTY_SIZE)
        return bestMonId;
Deleting this will prevent the opponent from switching repeatedly from Pokemon to Pokemon if none have a type advantage. I may not be the best player, but if none of my Pokemon have a good type advantage, then I probably wouldn't want to instead switch to the one who does the most damage instead.
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.
NOTE: Sometimes, the AI will switch to a pokemon that is not ideal, then switch right away. Currently looking into this, but it's not super pressing.
 
Last edited:
20
Posts
8
Years
  • Age 41
  • Seen Nov 9, 2022
[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!

b0ofYYc.gif


I have no repo for this. The wiki page explains the simple code changes.

In the wiki it says:
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:
But this `if` statement is missing in the `ItemUseCB_Medicine` function.
 
239
Posts
8
Years
  • Age 31
  • Seen Apr 15, 2024
In the wiki it says:

But this `if` statement is missing in the `ItemUseCB_Medicine` function.

The function must have been altered. I'll look into updating the wiki.

[Pokeemerald] Delete save file

If you want to punsh your players for whiting out, there's a convenient function to do so:
Code:
ClearSaveData();

All you need to do it add this to `CB2_WhiteOut`, probably after "StopMapMusic".
Code:
ClearSaveData();
DoSoftReset();
I haven't tested exact implementation but it should work.
 
11
Posts
3
Years
  • Age 33
  • Seen Jul 12, 2021
[PokeEmerald] Grindrunning For Easier Diagonal Movement​
pPusQdp.gif

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:

This has trouble with NPC collisions and tends to leave you on the wrong side of a Cut tree.
 
10
Posts
4
Years
[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:
Code:
src/battle_ai_switch_items.c
below function:
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;
    }
}
write:
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;
}
Then, find:
Code:
if (availableToSwitch == 0)
        return FALSE;
    if (ShouldSwitchIfAllBadMoves())
        return TRUE;
    if (ShouldSwitchIfPerishSong())
        return TRUE;
    if (ShouldSwitchIfWonderGuard())
        return TRUE;
    if (FindMonThatAbsorbsOpponentsMove())
        return TRUE;
and replace it with:
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;
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.

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.
 
118
Posts
4
Years
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.

Ok, so Switching AI #2 has two problems that I'm aware of. #1: Sometimes, the mon the Pokemon switches to isn't the best one, and then they will immediately switch to a better one. #2: If no Pokemon is good, then the AI will switch between them over and over until they lose.

I have come up with a solution to problem #2, and I'll update the initial post right away. For now, I'm not sure about why problem #1 even occurs. If I ever find a solution, I'll update the post accordingly.
 
6
Posts
5
Years
  • Age 25
  • Seen Mar 2, 2021
Make text speed-up like holding the A/B buttons automatically

gflib/text.c
go to
Code:
RenderText
and remove on lines 846 and 852.
Code:
JOY_HELD(A_BUTTON | B_BUTTON) &&
I.e. This
Code:
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;
becomes this
Code:
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;
 
6
Posts
5
Years
  • Age 25
  • Seen Mar 2, 2021
Make text autoscroll
Code:
gflib/text.c
Code:
subStruct->autoScrollDelay = 0;
Add this to this if statement in TextPrinterWaitAutoMode. Just so it doesn't go too fast.
Code:
    if (subStruct->autoScrollDelay == 49)
    {
        subStruct->autoScrollDelay = 0;
        return TRUE;
    }
    else
I added it as an option to the options menu, but just make it so it goes to TextPrinterWaitAutoMode in both TextPrinterWaitWithDownArrow and TextPrinterWait when you want it to.

Original:
Code:
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);
    }
Changed to
Code:
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);
    }

Code:
bool8 optionsTextMode;
In include/global.h, add this to Struct SaveBlock2. Just so the above code compiles. This is meant to be an option in the options menu, but doesn't necessarily have to be.


To add it as an option to the options menu, make the changes on this commit

Currently got an issue with the options menu not showing all the options if you do it like this. So you may want to replace an option like ButtonMode.
 
Last edited:
118
Posts
4
Years
[PokeEmerald]
Improving the Pace of Battles
Part 1 - Speeding up Text
In Pokemon Emerald, almost all battle text moves automatically after a set time. However, I find this delay to be too slow. I would rather it be so that you can either wait for the delay to end on its own, or you can press a button and skip the delay. This hack shows you how to do that:
Code:
src/battle_script_commands.c 
static void Cmd_waitmessage(void)
replace "if (++gPauseCounterBattle >= toWait)" with "if (++gPauseCounterBattle >= toWait || (JOY_NEW(A_BUTTON | B_BUTTON)))".
Next, in:
Code:
static void Cmd_pause
replace "if (++gPauseCounterBattle >= value)" with "if (++gPauseCounterBattle >= value || (JOY_NEW(A_BUTTON | B_BUTTON)))".
Now, you can speed up the delay by pressing the A or B button.

Part 2 - Speeding up Fainting
This is a minor hack that lessens the wait between a Pokemon fainting and the game continuing. In the original game, there is a long delay between a fainting cry and the Pokemon actually fainting. This script shortens this delay. In:
Code:
data/battle_scripts_1.s
BattleScript_FaintAttacker
replace "pause 0x40" with "pause 0x20" (or any smaller number).
Do the same in this function:
Code:
BattleScript_FaintTarget
and done! (NOTE: Since Part 1 allows you to skip delays, this hack might seem a little pointless, but you can do this hack without doing part 1 and if you don't mash the A button during battle like me, it'll be more useful.)

Part 3 - Speeding up Mega Evolution
Use this hack only if you're using the Battle Engine Upgrade. The vanilla Mega Evolution animation is very pretty, but it also takes a long time. This hack sets it so that if you have animations off, the animation is simplified.
In
Code:
src/battle_main.c
replace "CheckMegaEvolutionBeforeTurn" with this:
Code:
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;
}
Next, go to:
Code:
data/battle_scripts_1.s
and replace from "BattleScript_MegaEvolution" to "BattleScript_WishMegaEvolution" with this:
Code:
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
go to:
Code:
include/battle_scripts.h
find "extern const u8 BattleScript_MegaEvolution[];" and replace it with this:
Code:
extern const u8 BattleScript_MegaEvolution[];
extern const u8 BattleScript_MegaEvolutionQ[];
extern const u8 BattleScript_WishMegaEvolution[];
extern const u8 BattleScript_WishMegaEvolutionQ[];
and that's it! (NOTE: Sometimes, the mosaic animation doesn't play and the Pokemon just turns into the other after a brief delay. It's fully functional, but it still has some kinks to be worked out. If anyone knows how to fix it, please respond to this or DM me.)

Thank for reading, and let me know if you have any problems with this.
 
Last edited:
11
Posts
3
Years
  • Age 33
  • Seen Jul 12, 2021
[Pokeemerald] DS-Style party screen (2 versions)
Changes the party screen to one similar to the DS versions. 2 different layouts available!
V1:
5dlIj9X.gif

V2:
MUeq5yM.gif


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:
Code:
git remote add xaman https://github.com/TheXaman/pokeemerald/
For version 1:
Code:
git pull xaman party_screen_ds_style
For version 2:
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

I'm using version 2, and I love it, but I think it has some tile errors in multi battles like the one alongside Steven in Mossdeep. I'm sorry I didn't grab screencaps.
 
17
Posts
3
Years
  • Age 24
  • Seen Feb 19, 2024
[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)

7RjFKud.gif


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
 
98
Posts
6
Years
[Pokeemerald] Hidden Power type in summary screen

Everybody knows Hidden Power's type is based on IVs. A calculation is done in the code to determine which type Hidden Power is, but it is not physically visible so knowing which Hidden Power your Pokémon has is a hard task. So I decided to write some code to show the true Hidden Power type in the summary screen. The type is shown both when it's taught and when you are teaching it (when you have 4 moves).

RWYY8uS.png


To apply this, open src/pokemon_summary_screen.c (everything is done in this file), and first find this function:

Code:
static void SetMoveTypeIcons(void)

And replace the whole function with:

Code:
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);
    }
}

Then, find the next function:

Code:
static void SetNewMoveTypeIcon(void)

And replace the whole function with:

Code:
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);
    }
}

(if you already have changes here somehow, or the code is different, you can just take the first if-statement where it checks for MOVE_HIDDEN_POWER and copy that IF to your function).

What's left to do:
- Add support to show the right type inside a battle
- For people who use the old power calculation, show the right power as well (but I changed Hidden Power to gen VI+ where it's always 60 base power)
- Maybe improve the code, I know the code isn't great but it does work
 
Back
Top