6 Learn moves upon evolution
ExpoSeed edited this page 2020-07-27 19:41:32 -05:00

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

Note: this feature as written is not compatible with DizzyEgg's battle_engine_v2.

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!