5 Add Difficulty Mode
voloved edited this page 2023-05-08 22:29:01 -04:00

By devolov. Huge help for FRLG-Plus for inspiring this

Goal: Add difficulty mode that causes trainers' Pokemon's levels to scale with the badge count.

For a given badge count and difficulty setting, the level addition to the trainer's Pokemon is given in the table:

Badges Easy Normal Hard
<2 -1 0 1
<4 -2 0 2
<6 -3 0 3
<Champion -4 0 4
Champion -5 0 5

Ex: At 5 badges and a HARD setting, a normal level 16 trainer Pokemon will be 19.

If you already have a menu in your game, then adding the logic is all you need along with setting VAR_DIFFICULTY in your menu (0 = Normal, 1 = Hard, 2 = Easy).
If you don't then you can follow my method, which is allowing the setting to be changed at One of Prof Birch's PCs.

Add Difficulty Mode Logic

Declare a var that will hold the challenge

--------------------------- include/constants/vars.h ---------------------------
index 214b80d1b..82378d7fb 100644
@@ -267,9 +267,9 @@
 #define VAR_WILD_PKMN_ROUTE_SEEN_1                       0x40F8
 #define VAR_WILD_PKMN_ROUTE_SEEN_2                       0x40F9
 #define VAR_WILD_PKMN_ROUTE_SEEN_3                       0x40FA
 #define VAR_WILD_PKMN_ROUTE_SEEN_4                       0x40FB
-#define VAR_UNUSED_0x40FC                                0x40FC // Unused Var
+#define VAR_DIFFICULTY                                   0x40FC
 #define VAR_UNUSED_0x40FD                                0x40FD // Unused Var
 #define VAR_UNUSED_0x40FE                                0x40FE // Unused Var
 #define VAR_UNUSED_0x40FF                                0x40FF // Unused Var

Add #defines for the settings that VAR_DIFFICULTY holds. Normal setting is 0 so that it will be set as such by default at a new game.

------------------------------- include/global.h -------------------------------
index 6e2cfa9b8..deea675e2 100644
@@ -138,8 +138,12 @@
 // This produces an error at compile-time if expr is zero.
 // It looks like file.c:line: size of array `id' is negative
 #define STATIC_ASSERT(expr, id) typedef char id[(expr) ? 1 : -1];
 
+#define DIFFICULTY_NORMAL    0
+#define DIFFICULTY_HARD      1
+#define DIFFICULTY_EASY      2
+
 struct Coords8
 {
     s8 x;
     s8 y;

Add the code that modifies the Pokemon's level and add it to the GetSumOfEnemyPartyLevel function (which looks like it's only used for the transition scene).

------------------------------ src/battle_setup.c ------------------------------
index 62158de8d..01b029c06 100644
@@ -810,33 +811,33 @@ static u8 GetSumOfEnemyPartyLevel(u16 opponentId, u8 numMons)
         {
             const struct TrainerMonNoItemDefaultMoves *party;
             party = gTrainers[opponentId].party.NoItemDefaultMoves;
             for (i = 0; i < count; i++)
-                sum += party[i].lvl;
+                sum += GetScaledLevel(party[i].lvl);
         }
         break;
     case F_TRAINER_PARTY_CUSTOM_MOVESET:
         {
             const struct TrainerMonNoItemCustomMoves *party;
             party = gTrainers[opponentId].party.NoItemCustomMoves;
             for (i = 0; i < count; i++)
-                sum += party[i].lvl;
+                sum += GetScaledLevel(party[i].lvl);
         }
         break;
     case F_TRAINER_PARTY_HELD_ITEM:
         {
             const struct TrainerMonItemDefaultMoves *party;
             party = gTrainers[opponentId].party.ItemDefaultMoves;
             for (i = 0; i < count; i++)
-                sum += party[i].lvl;
+                sum += GetScaledLevel(party[i].lvl);
         }
         break;
     case F_TRAINER_PARTY_CUSTOM_MOVESET | F_TRAINER_PARTY_HELD_ITEM:
         {
             const struct TrainerMonItemCustomMoves *party;
             party = gTrainers[opponentId].party.ItemCustomMoves;
             for (i = 0; i < count; i++)
-                sum += party[i].lvl;
+                sum += GetScaledLevel(party[i].lvl);
         }
         break;
     }
 
@@ -2347,4 +2348,38 @@ u8 currLocConvertForNuzlocke(u8 currLocation){
     default:
         return currLocation;
     }
 }
+
+u8 GetScaledLevel(u8 lvl)
+{
+    u8 badgeCount = 0;
+    u8 levelScaling = 0;
+    u32 i;
+    for (i = FLAG_BADGE01_GET; i < FLAG_BADGE01_GET + NUM_BADGES; i++)
+    {
+        if (FlagGet(i))
+            badgeCount++;
+    }
+
+    if (FlagGet(FLAG_IS_CHAMPION))
+        levelScaling = 5;
+    else if (badgeCount >= 6)
+        levelScaling = 4;
+    else if (badgeCount >= 4)
+        levelScaling = 3;
+    else if (badgeCount >= 2)
+        levelScaling = 2;
+    else
+        levelScaling = 1;
+
+    if (VarGet(VAR_DIFFICULTY) == DIFFICULTY_HARD)
+        lvl += levelScaling;
+    else if (VarGet(VAR_DIFFICULTY) == DIFFICULTY_EASY)
+        lvl -= levelScaling;
+
+    if (lvl > 100)
+        lvl = 100;
+    if (lvl < 1)
+        lvl = 1;
+    return lvl;
+}
---------------------------- include/battle_setup.h ----------------------------
index feaa30c00..5541cbac6 100644
@@ -46,8 +46,9 @@ void SetTrainerFlag(u16 trainerId);
 void ClearTrainerFlag(u16 trainerId);
 void BattleSetup_StartTrainerBattle(void);
 void BattleSetup_StartRematchBattle(void);
 void ShowTrainerIntroSpeech(void);
+u8 GetScaledLevel(u8 lvl);
 const u8 *BattleSetup_GetScriptAddrAfterBattle(void);
 const u8 *BattleSetup_GetTrainerPostBattleScript(void);
 void ShowTrainerCantBattleSpeech(void);
 void PlayTrainerEncounterMusic(void);

Modify the CreateNPCTrainerParty so each Pokemon they use have their levels scaled.

------------------------------ src/battle_main.c ------------------------------
index 0f16660f1..cfd4265a5 100644
@@ -2085,9 +2085,9 @@ static u8 CreateNPCTrainerParty(struct Pokemon *party, u16 trainerNum, bool8 fir
                     nameHash += gSpeciesNames[partyData[i].species][j];
 
                 personalityValue += nameHash << 8;
                 fixedIV = partyData[i].iv * MAX_PER_STAT_IVS / 255;
-                CreateMon(&party[i], partyData[i].species, partyData[i].lvl, fixedIV, TRUE, personalityValue, OT_ID_PRESET, fixedOTID);
+                CreateMon(&party[i], partyData[i].species, GetScaledLevel(partyData[i].lvl), fixedIV, TRUE, personalityValue, OT_ID_PRESET, fixedOTID);
                 SetMonData(&party[i], MON_DATA_OT_GENDER, &otGender);
                 SetMonData(&party[i], MON_DATA_OT_NAME, gTrainers[trainerNum].trainerName);
                 break;
             }
@@ -2102,9 +2102,9 @@ static u8 CreateNPCTrainerParty(struct Pokemon *party, u16 trainerNum, bool8 fir
                     nameHash += gSpeciesNames[partyData[i].species][j];
 
                 personalityValue += nameHash << 8;
                 fixedIV = partyData[i].iv * MAX_PER_STAT_IVS / 255;
-                CreateMon(&party[i], partyData[i].species, partyData[i].lvl, fixedIV, TRUE, personalityValue, OT_ID_PRESET, fixedOTID);
+                CreateMon(&party[i], partyData[i].species, GetScaledLevel(partyData[i].lvl), fixedIV, TRUE, personalityValue, OT_ID_PRESET, fixedOTID);
                 SetMonData(&party[i], MON_DATA_OT_GENDER, &otGender);
                 SetMonData(&party[i], MON_DATA_OT_NAME, gTrainers[trainerNum].trainerName);
 
                 for (j = 0; j < MAX_MON_MOVES; j++)
@@ -2125,9 +2125,9 @@ static u8 CreateNPCTrainerParty(struct Pokemon *party, u16 trainerNum, bool8 fir
                     nameHash += gSpeciesNames[partyData[i].species][j];
 
                 personalityValue += nameHash << 8;
                 fixedIV = partyData[i].iv * MAX_PER_STAT_IVS / 255;
-                CreateMon(&party[i], partyData[i].species, partyData[i].lvl, fixedIV, TRUE, personalityValue, OT_ID_PRESET, fixedOTID);
+                CreateMon(&party[i], partyData[i].species, GetScaledLevel(partyData[i].lvl), fixedIV, TRUE, personalityValue, OT_ID_PRESET, fixedOTID);
                 SetMonData(&party[i], MON_DATA_OT_GENDER, &otGender);
                 SetMonData(&party[i], MON_DATA_OT_NAME, gTrainers[trainerNum].trainerName);
 
                 SetMonData(&party[i], MON_DATA_HELD_ITEM, &partyData[i].heldItem);
@@ -2144,9 +2144,9 @@ static u8 CreateNPCTrainerParty(struct Pokemon *party, u16 trainerNum, bool8 fir
                     nameHash += gSpeciesNames[partyData[i].species][j];
 
                 personalityValue += nameHash << 8;
                 fixedIV = partyData[i].iv * MAX_PER_STAT_IVS / 255;
-                CreateMon(&party[i], partyData[i].species, partyData[i].lvl, fixedIV, TRUE, personalityValue, OT_ID_PRESET, fixedOTID);
+                CreateMon(&party[i], partyData[i].species, GetScaledLevel(partyData[i].lvl), fixedIV, TRUE, personalityValue, OT_ID_PRESET, fixedOTID);
                 SetMonData(&party[i], MON_DATA_OT_GENDER, &otGender);
                 SetMonData(&party[i], MON_DATA_OT_NAME, gTrainers[trainerNum].trainerName);
                 if (partyData[i].species == SPECIES_SLAKING && gTrainers[trainerNum].trainerPic == TRAINER_PIC_LEADER_NORMAN 
                 && gTrainers[trainerNum].trainerClass == TRAINER_CLASS_LEADER)  //Set Norman's Slaking to have intimidate

Remove Badge Boosts on Hard Difficulty If You'd Like

------------------------------ src/battle_main.c ------------------------------
index cfd4265a5..06cfb2a0e 100644
@@ -4755,8 +4755,9 @@ u8 GetWhoStrikesFirst(u8 battler1, u8 battler2, bool8 ignoreChosenMoves)
 
     // badge boost
     if (!(gBattleTypeFlags & (BATTLE_TYPE_LINK | BATTLE_TYPE_RECORDED_LINK | BATTLE_TYPE_FRONTIER))
         && FlagGet(FLAG_BADGE03_GET)
+        && (VarGet(VAR_DIFFICULTY) != DIFFICULTY_HARD)
         && GetBattlerSide(battler1) == B_SIDE_PLAYER)
     {
         speedBattler1 = (speedBattler1 * 110) / 100;
     }
@@ -4789,8 +4790,9 @@ u8 GetWhoStrikesFirst(u8 battler1, u8 battler2, bool8 ignoreChosenMoves)
 
     // badge boost
     if (!(gBattleTypeFlags & (BATTLE_TYPE_LINK | BATTLE_TYPE_RECORDED_LINK | BATTLE_TYPE_FRONTIER))
         && FlagGet(FLAG_BADGE03_GET)
+        && (VarGet(VAR_DIFFICULTY) != DIFFICULTY_HARD)
         && GetBattlerSide(battler2) == B_SIDE_PLAYER)
     {
         speedBattler2 = (speedBattler2 * 110) / 100;
     }
-------------------------------- src/pokemon.c --------------------------------
index db0e8099d..21bf84c15 100644
@@ -3421,8 +3421,10 @@ u8 CountAliveMonsInBattle(u8 caseId)
 static bool8 ShouldGetStatBadgeBoost(u16 badgeFlag, u8 battlerId)
 {
     if (gBattleTypeFlags & (BATTLE_TYPE_LINK | BATTLE_TYPE_EREADER_TRAINER | BATTLE_TYPE_RECORDED_LINK | BATTLE_TYPE_FRONTIER))
         return FALSE;
+    if (VarGet(VAR_DIFFICULTY) == DIFFICULTY_HARD)
+        return FALSE;
     else if (GetBattlerSide(battlerId) != B_SIDE_PLAYER)
         return FALSE;
     else if (gBattleTypeFlags & BATTLE_TYPE_TRAINER && gTrainerBattleOpponent_A == TRAINER_SECRET_BASE)
         return FALSE;

Adding a Place to Set the Difficulty

pokeemerald_modern-0

----------- data/maps/LittlerootTown_ProfessorBirchsLab/scripts.inc -----------
index 603242d71..d4bc105a4 100644
@@ -686,8 +686,79 @@ LittlerootTown_ProfessorBirchsLab_EventScript_GrindRunNo::
 	clearflag FLAG_GRINDRUN
 	msgbox LittlerootTown_ProfessorBirchsLab_Text_GrindRunNo, MSGBOX_SIGN
 	end
 
+LittlerootTown_ProfessorBirchsLab_EventScript_Challenge::
+	message LittlerootTown_ProfessorBirchsLab_Text_Challenge
+	waitmessage
+	switch VAR_DIFFICULTY
+	case 0, LittlerootTown_ProfessorBirchsLab_EventScript_DefaultNormal
+	case 1, LittlerootTown_ProfessorBirchsLab_EventScript_DefaultHard
+	case 2, LittlerootTown_ProfessorBirchsLab_EventScript_DefaultEasy
+
+LittlerootTown_ProfessorBirchsLab_EventScript_DefaultNormal:
+	multichoicedefault 21, 6, MULTI_TAG_DIFFICULTY, 1, FALSE
+	goto LittlerootTown_ProfessorBirchsLab_EventScript_ChallengeSelect
+
+LittlerootTown_ProfessorBirchsLab_EventScript_DefaultHard:
+	multichoicedefault 21, 6, MULTI_TAG_DIFFICULTY, 2, FALSE
+	goto LittlerootTown_ProfessorBirchsLab_EventScript_ChallengeSelect
+
+LittlerootTown_ProfessorBirchsLab_EventScript_DefaultEasy:
+	multichoicedefault 21, 6, MULTI_TAG_DIFFICULTY, 0, FALSE
+	goto LittlerootTown_ProfessorBirchsLab_EventScript_ChallengeSelect
+
+LittlerootTown_ProfessorBirchsLab_EventScript_ChallengeSelect:
+	switch VAR_RESULT
+	case 0, LittlerootTown_ProfessorBirchsLab_EventScript_SelectedEasy
+	case 1, LittlerootTown_ProfessorBirchsLab_EventScript_SelectedNormal
+	case 2, LittlerootTown_ProfessorBirchsLab_EventScript_SelectedHard
+	case MULTI_B_PRESSED, LittlerootTown_ProfessorBirchsLab_EventScript_SelectedCancel
+	end
+
+LittlerootTown_ProfessorBirchsLab_EventScript_SelectedNormal:
+	msgbox LittlerootTown_ProfessorBirchsLab_Text_SelectedNormal, MSGBOX_DEFAULT
+	setvar VAR_DIFFICULTY, 0
+	closemessage
+	end
+
+LittlerootTown_ProfessorBirchsLab_EventScript_SelectedHard:
+	msgbox LittlerootTown_ProfessorBirchsLab_Text_SelectedHard, MSGBOX_DEFAULT
+	setvar VAR_DIFFICULTY, 1
+	closemessage
+	end
+
+LittlerootTown_ProfessorBirchsLab_EventScript_SelectedEasy:
+	msgbox LittlerootTown_ProfessorBirchsLab_Text_SelectedEasy, MSGBOX_DEFAULT
+	setvar VAR_DIFFICULTY, 2
+	closemessage
+	end
+
+LittlerootTown_ProfessorBirchsLab_EventScript_SelectedCancel:
+	msgbox LittlerootTown_ProfessorBirchsLab_Text_SelectedCancel, MSGBOX_DEFAULT
+	closemessage
+	end
+
+LittlerootTown_ProfessorBirchsLab_Text_Challenge:
+	.string "What difficulty would\n"
+	.string "you like to play at?$"
+
+LittlerootTown_ProfessorBirchsLab_Text_SelectedEasy:
+	.string "Easy difficulty\n"
+	.string "was chosen.$"
+
+LittlerootTown_ProfessorBirchsLab_Text_SelectedNormal:
+	.string "Normal difficulty\n"
+	.string "was chosen.$"
+
+LittlerootTown_ProfessorBirchsLab_Text_SelectedHard:
+	.string "Hard difficulty\n"
+	.string "was chosen.$"
+
+LittlerootTown_ProfessorBirchsLab_Text_SelectedCancel:
+	.string "Difficulty is\n"
+	.string "unchanged.$"
+
 LittlerootTown_ProfessorBirchsLab_Text_SkipIntro:
 	.string "Would you like to skip\n"
 	.string "the intro in the future?$"
------------- data/maps/LittlerootTown_ProfessorBirchsLab/map.json -------------
index 00a3912ec..3695c5256 100644
     {
       "type": "sign",
       "x": 1,
       "y": 10,
       "elevation": 0,
       "player_facing_dir": "BG_EVENT_PLAYER_FACING_ANY",
-      "script": "LittlerootTown_ProfessorBirchsLab_EventScript_PC"
+      "script": "LittlerootTown_ProfessorBirchsLab_EventScript_Challenge"
     },
     {
       "type": "sign",
       "x": 1,
       "y": 9,
       "elevation": 0,
       "player_facing_dir": "BG_EVENT_PLAYER_FACING_ANY",
-      "script": "LittlerootTown_ProfessorBirchsLab_EventScript_PC"
+      "script": "LittlerootTown_ProfessorBirchsLab_EventScript_Challenge"
     },
----------------------- include/constants/script_menu.h -----------------------
index c58df7335..819e89266 100644
@@ -121,8 +121,9 @@
 #define MULTI_WHERES_RAYQUAZA              110
 #define MULTI_SLATEPORT_TENT_RULES         111
 #define MULTI_FALLARBOR_TENT_RULES         112
 #define MULTI_TAG_MATCH_TYPE               113
+#define MULTI_TAG_DIFFICULTY               114
 
 // Lilycove SS Tidal Multichoice Selections
 #define SSTIDAL_SELECTION_SLATEPORT        0
 #define SSTIDAL_SELECTION_BATTLE_FRONTIER  1
-------------------------------- src/strings.c --------------------------------
index 2107e2d82..6ff3c35bb 100644
@@ -1225,8 +1225,10 @@ const u8 gText_BattlePalace[] = _("BATTLE PALACE");
 const u8 gText_BattleFactory[] = _("BATTLE FACTORY");
 const u8 gText_BattleArena[] = _("BATTLE ARENA");
 const u8 gText_BattlePike[] = _("BATTLE PIKE");
 const u8 gText_BattlePyramid[] = _("BATTLE PYRAMID");
+const u8 gText_Easy[] = _("EASY");
+const u8 gText_Hard[] = _("HARD");
 
 ALIGNED(4) const u8 gText_FacilitySingle[] = _("{STR_VAR_1} SINGLE");
 ALIGNED(4) const u8 gText_FacilityDouble[] = _("{STR_VAR_1} DOUBLE");
 ALIGNED(4) const u8 gText_FacilityMulti[] = _("{STR_VAR_1} MULTI");
------------------------------ include/strings.h ------------------------------
index e944de306..ee63e8f5b 100644
@@ -267,8 +267,11 @@ extern const u8 gText_Floor7[];
 extern const u8 gText_Peak[];
 extern const u8 gText_SafariBallStock[];
 extern const u8 gText_BattlePyramidFloor[];
 
+extern const u8 gText_Easy[];
+extern const u8 gText_Hard[];
+
 extern const u8 gText_MenuOptionPokedex[];
 extern const u8 gText_MenuOptionPokemon[];
 extern const u8 gText_MenuOptionBag[];
 extern const u8 gText_MenuOptionPokenav[];
---------------------------- src/data/script_menu.h ----------------------------
index 79355748c..ee4d60478 100644
@@ -770,8 +770,15 @@ static const struct MenuAction MultichoiceList_TagMatchType[] =
     {gText_ExpertTagMatch},
     {gText_Exit},
 };
 
+static const struct MenuAction MultichoiceList_TagChallenge[] =
+{
+    {gText_Easy},
+    {gText_Normal},
+    {gText_Hard},
+};
+
 static const struct MenuAction MultichoiceList_Exit[] =
 {
     {gText_Exit},
 };
@@ -897,8 +904,9 @@ static const struct MultichoiceListStruct sMultichoiceLists[] =
     [MULTI_WHERES_RAYQUAZA]            = MULTICHOICE(MultichoiceList_WheresRayquaza),
     [MULTI_SLATEPORT_TENT_RULES]       = MULTICHOICE(MultichoiceList_SlateportTentRules),
     [MULTI_FALLARBOR_TENT_RULES]       = MULTICHOICE(MultichoiceList_FallarborTentRules),
     [MULTI_TAG_MATCH_TYPE]             = MULTICHOICE(MultichoiceList_TagMatchType),
+    [MULTI_TAG_DIFFICULTY]             = MULTICHOICE(MultichoiceList_TagChallenge),
 };
 
 const u8 *const gStdStrings[] =
 {