• 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
    • 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
    • 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!

    Simple Modifications Directory


    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
    • 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
    • Seen Jul 12, 2021
    [PokeEmerald] Grindrunning For Easier Diagonal Movement​
    Simple Modifications Directory

    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
    5
    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
    • 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
    • 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
    • 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:
    Simple Modifications Directory

    V2:
    Simple Modifications Directory


    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
    • Seen May 10, 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)

    Simple Modifications Directory


    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
    7
    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).

    Simple Modifications Directory


    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