Skip to content

Learn moves upon evolution

ExpoSeed edited this page Jul 28, 2020 · 6 revisions

Credit to Zeturic and UltimaSoul for this feature, and to ExpoSeed and Lunos for modifying it.

This tutorial will allow Pokemon to learn moves immediately after evolving, like in Gen 7. This is useful because it avoids a situation where a Pokemon (like Feebas) has no useful or damaging moves because it evolved slightly too late.

We will implement this by writing a new function to handle learning a new move on evolution. Declare a new function in include/pokemon.h:

u16 MonTryLearningNewMoveEvolution(struct Pokemon *mon, bool8 firstMove);

Next, we will write the function itself. Add a new function in src/pokemon.c:

u16 MonTryLearningNewMoveEvolution(struct Pokemon *mon, bool8 firstMove)
{
    u16 species = GetMonData(mon, MON_DATA_SPECIES, NULL);
    u8 level = GetMonData(mon, MON_DATA_LEVEL, NULL);

    // since you can learn more than one move per level
    // the game needs to know whether you decided to
    // learn it or keep the old set to avoid asking
    // you to learn the same move over and over again
    if (firstMove)
    {
        sLearningMoveTableID = 0;
    }
    while(gLevelUpLearnsets[species][sLearningMoveTableID] != LEVEL_UP_END)
    {
        u16 moveLevel;
        moveLevel = (gLevelUpLearnsets[species][sLearningMoveTableID] & LEVEL_UP_MOVE_LV);
        while (moveLevel == 0 || moveLevel == (level << 9))
        {
            gMoveToLearn = (gLevelUpLearnsets[species][sLearningMoveTableID] & LEVEL_UP_MOVE_ID);
            sLearningMoveTableID++;
            return GiveMoveToMon(mon, gMoveToLearn);
        }
        sLearningMoveTableID++;
    }
    return 0;
}

This iterates through the mon's level up learnset and attempts to teach all the moves that are either level 0 or the same level as the mon.

Next, we need to call our new function. The two times the normal MonTryLearningNewMove function is called are in src/evolution_scene.c.

Edit Task_EvolutionScene of src/evolution_scene.c:

     case 15: // check if it wants to learn a new move
         if (!IsTextPrinterActive(0))
         {
-            var = MonTryLearningNewMove(mon, gTasks[taskID].tLearnsFirstMove);
+            var = MonTryLearningNewMoveEvolution(mon, gTasks[taskID].tLearnsFirstMove);
             if (var != 0 && !gTasks[taskID].tEvoWasStopped)
             {
                 u8 text[20];
                 if (!(gTasks[taskID].tBits & TASK_BIT_LEARN_MOVE))
                 {
                     StopMapMusic();
                     Overworld_PlaySpecialMapMusic();
                 }
                 gTasks[taskID].tBits |= TASK_BIT_LEARN_MOVE;
                 gTasks[taskID].tLearnsFirstMove = FALSE;
                 gTasks[taskID].tLearnMoveState = 0;
                 GetMonData(mon, MON_DATA_NICKNAME, text);
                 StringCopy10(gBattleTextBuff1, text);
                 if (var == MON_HAS_MAX_MOVES)
                     gTasks[taskID].tState = 22;
                 else if (var == MON_ALREADY_KNOWS_MOVE)
                     break;
                 else
                     gTasks[taskID].tState = 20; // move has been learned
             }
             else // no move to learn
             {
                 BeginNormalPaletteFade(0xFFFFFFFF, 0, 0, 0x10, RGB_BLACK);
                 gTasks[taskID].tState++;
             }
         }
         break;

And edit Task_TradeEvolutionScene of src/evolution_scene.c:

     case 13:
         if (!IsTextPrinterActive(0) && IsFanfareTaskInactive() == TRUE)
         {
-            var = MonTryLearningNewMove(mon, gTasks[taskID].tLearnsFirstMove);
+            var = MonTryLearningNewMoveEvolution(mon, gTasks[taskID].tLearnsFirstMove);
             if (var != 0 && !gTasks[taskID].tEvoWasStopped)
             {
                 u8 text[20];
                 gTasks[taskID].tBits |= TASK_BIT_LEARN_MOVE;
                 gTasks[taskID].tLearnsFirstMove = FALSE;
                 gTasks[taskID].tLearnMoveState = 0;
                 GetMonData(mon, MON_DATA_NICKNAME, text);
                 StringCopy10(gBattleTextBuff1, text);
                 if (var == MON_HAS_MAX_MOVES)
                     gTasks[taskID].tState = 20;
                 else if (var == MON_ALREADY_KNOWS_MOVE)
                     break;
                 else
                     gTasks[taskID].tState = 18;
             }
             else
             {
                 PlayBGM(MUS_SHINKA);
                 DrawTextOnTradeWindow(0, gText_CommunicationStandby5, 1);
                 gTasks[taskID].tState++;
             }
         }
         break;

We also need to fix the generation of moves when a mon is initially created. Otherwise, new mons will start with their level 0 moves learned. To fix this, edit src/pokemon.c:

 void GiveBoxMonInitialMoveset(struct BoxPokemon *boxMon)
 {
     u16 species = GetBoxMonData(boxMon, MON_DATA_SPECIES, NULL);
     s32 level = GetLevelFromBoxMonExp(boxMon);
     s32 i;
 
     for (i = 0; gLevelUpLearnsets[species][i] != LEVEL_UP_END; i++)
     {
         u16 moveLevel;
         u16 move;
 
         moveLevel = (gLevelUpLearnsets[species][i] & LEVEL_UP_MOVE_LV);
 
+        if (moveLevel == 0)
+            continue;
 
         if (moveLevel > (level << 9))
             break;
 
         move = (gLevelUpLearnsets[species][i] & LEVEL_UP_MOVE_ID);
 
         if (GiveMoveToBoxMon(boxMon, move) == MON_HAS_MAX_MOVES)
             DeleteFirstMoveAndGiveMoveToBoxMon(boxMon, move);
     }
 }

Finally, we need to actually give our mons evolution moves. This info can be found in src/data/pokemon/level_up_learnsets.h. The moves need to be at the beginning of the list and at level 0. Here is an example of what this looks like:

static const u16 sMarshtompLevelUpLearnset[] = {
    LEVEL_UP_MOVE( 0, MOVE_MUD_SHOT),
    LEVEL_UP_MOVE( 1, MOVE_MUD_SHOT),
    LEVEL_UP_MOVE( 1, MOVE_TACKLE),
    LEVEL_UP_MOVE( 1, MOVE_GROWL),
    LEVEL_UP_MOVE( 1, MOVE_WATER_GUN),
    LEVEL_UP_MOVE( 1, MOVE_MUD_SLAP),
    LEVEL_UP_MOVE( 4, MOVE_WATER_GUN),
    LEVEL_UP_MOVE( 9, MOVE_MUD_SLAP),
    LEVEL_UP_MOVE(12, MOVE_FORESIGHT),
    LEVEL_UP_MOVE(18, MOVE_BIDE),
    LEVEL_UP_MOVE(22, MOVE_MUD_BOMB),
    LEVEL_UP_MOVE(28, MOVE_ROCK_SLIDE),
    LEVEL_UP_MOVE(32, MOVE_PROTECT),
    LEVEL_UP_MOVE(38, MOVE_MUDDY_WATER),
    LEVEL_UP_MOVE(42, MOVE_TAKE_DOWN),
    LEVEL_UP_MOVE(48, MOVE_EARTHQUAKE),
    LEVEL_UP_MOVE(52, MOVE_ENDEAVOR),
    LEVEL_UP_END
};

And that's it!

Clone this wiki locally