Skip to content

Add Nuzlocke Challenge

voloved edited this page Oct 30, 2023 · 17 revisions

By devolov

Goal: Add in the ability to have the Nuzlocke challenge in the game in a way where a vanilla save file would still work.

pokeemerald-0 pokeemerald-1
pokeemerald-2 pokeemerald_modern-0
pokeemerald_modern-3 pokeemerald_modern-4

The rules for the Nuzlocke challenge:

The challenge is chosen at a new game right after chosing your gender. Routes where Pokemon catches are monitored can be seen in the function currLocConvertForNuzlocke and HasWildPokmnOnThisRouteBeenSeen.

Below is all of the code changes (and let me know on Discord if you find any bugs!).

Here's my solution for each requirement:

If a Pokemon faints, they cannot be revived

Instead of releasing the Pokemon, since some people like to hold onto them, I instead made a fainted Pokemon not be able to regain HP. The BoxPokemon struct has five unused bits, so using one of them allows for the Pokemon to retain their dead data even after depositing into the PC. When trading, the dead bit gets cleared.
After that hurdle is cleared, it's a matter of finding the healing items and party healing functions and ignoring dead Pokemon. In my testing, I found that Sacred Ash does not work at all anymore if a Dead Pokemon is in the party, but that's enough of an edge-case for me to not lose sleep on.

Only the first Pokemon in a route can be caught

There are about 100 locations where Pokemon can reasonably be caught in the game. To keep the goal of having it work with a vanilla save, we can use 5 variables in vars.h, where each bit in the variable holds if a Pokemon was seen in a route. This is after lots of Googling and ignoring event locations and consolidating things like underwater routes with their above-ground routes. We then check if the bit was set when we get a wild encounter, set it if it wasn't, and only allow catching if it wasn't originally set.

-------------------------- include/constants/flags.h --------------------------
#define FLAG_ITEM_MAGMA_HIDEOUT_4F_MAX_REVIVE                       0x490
#define FLAG_ITEM_SAFARI_ZONE_NORTH_EAST_NUGGET                     0x491
#define FLAG_ITEM_SAFARI_ZONE_SOUTH_EAST_BIG_PEARL                  0x492

-#define FLAG_UNUSED_0x493                                           0x493 // Unused Flag
+#define FLAG_NUZLOCKE                                               0x493
#define FLAG_UNUSED_0x494                                           0x494 // Unused Flag
#define FLAG_UNUSED_0x495                                           0x495 // Unused Flag

------------------------------- src/main_menu.c -------------------------------
index f99703622..75520e337 100644
@@ -36,8 +36,9 @@
 #include "text_window.h"
 #include "title_screen.h"
 #include "window.h"
 #include "mystery_gift_menu.h"
+#include "constants/flags.h"
 
 /*
  * Main menu state machine
  * -----------------------
@@ -125,13 +126,18 @@
  * Task_NewGameBirchSpeech_WaitToShowGenderMenu
  * Task_NewGameBirchSpeech_ChooseGender
  *  - Animates by advancing to Task_NewGameBirchSpeech_SlideOutOldGenderSprite
  *    whenever the player's selection changes.
- *  - Advances to Task_NewGameBirchSpeech_WhatsYourName when done.
+ *  - Advances to Task_NewGameBirchSpeech_Nuzlocke when done.
  *
  * Task_NewGameBirchSpeech_SlideOutOldGenderSprite
  * Task_NewGameBirchSpeech_SlideInNewGenderSprite
  *  - Returns back to Task_NewGameBirchSpeech_ChooseGender.
+ * 
+ * Task_NewGameBirchSpeech_Nuzlocke
+ * Task_NewGameBirchSpeech_WaitToShowNuzlockeMenu
+ * Task_NewGameBirchSpeech_ChooseNuzlocke
+ *  Advances to Task_NewGameBirchSpeech_WhatsYourName when done.
  *
  * Task_NewGameBirchSpeech_WhatsYourName
  * Task_NewGameBirchSpeech_WaitForWhatsYourNameToPrint
  * Task_NewGameBirchSpeech_WaitPressBeforeNameChoice
@@ -208,16 +214,22 @@ static void NewGameBirchSpeech_StartFadePlatformIn(u8, u8);
 static void Task_NewGameBirchSpeech_SlidePlatformAway(u8);
 static void Task_NewGameBirchSpeech_StartPlayerFadeIn(u8);
 static void Task_NewGameBirchSpeech_WaitForPlayerFadeIn(u8);
 static void Task_NewGameBirchSpeech_BoyOrGirl(u8);
+static void Task_NewGameBirchSpeech_Nuzlocke(u8);
 static void LoadMainMenuWindowFrameTiles(u8, u16);
 static void DrawMainMenuWindowBorder(const struct WindowTemplate *, u16);
 static void Task_HighlightSelectedMainMenuItem(u8);
 static void Task_NewGameBirchSpeech_WaitToShowGenderMenu(u8);
 static void Task_NewGameBirchSpeech_ChooseGender(u8);
+static void Task_NewGameBirchSpeech_WaitToShowNuzlockeMenu(u8);
+static void Task_NewGameBirchSpeech_ChooseNuzlocke(u8);
 static void NewGameBirchSpeech_ShowGenderMenu(void);
 static s8 NewGameBirchSpeech_ProcessGenderMenuInput(void);
 static void NewGameBirchSpeech_ClearGenderWindow(u8, u8);
+static void NewGameBirchSpeech_ShowNuzlockeMenu(void);
+static s8 NewGameBirchSpeech_ProcessNuzlockeMenuInput(void);
+static void NewGameBirchSpeech_ClearNuzlockeWindow(u8, u8);
 static void Task_NewGameBirchSpeech_WhatsYourName(u8);
 static void Task_NewGameBirchSpeech_SlideOutOldGenderSprite(u8);
 static void Task_NewGameBirchSpeech_SlideInNewGenderSprite(u8);
 static void Task_NewGameBirchSpeech_WaitForWhatsYourNameToPrint(u8);
@@ -456,8 +468,13 @@ static const struct MenuAction sMenuActions_Gender[] = {
     {gText_BirchBoy, NULL},
     {gText_BirchGirl, NULL}
 };
 
+static const struct MenuAction sMenuActions_Nuzlocke[] = {
+    {gText_Yes, NULL},
+    {gText_No, NULL}
+};
+
 static const u8 *const sMalePresetNames[] = {
     gText_DefaultNameStu,
     gText_DefaultNameMilton,
     gText_DefaultNameTom,
@@ -1507,15 +1524,15 @@ static void Task_NewGameBirchSpeech_ChooseGender(u8 taskId)
         case MALE:
             PlaySE(SE_SELECT);
             gSaveBlock2Ptr->playerGender = gender;
             NewGameBirchSpeech_ClearGenderWindow(1, 1);
-            gTasks[taskId].func = Task_NewGameBirchSpeech_WhatsYourName;
+            gTasks[taskId].func = Task_NewGameBirchSpeech_Nuzlocke;
             break;
         case FEMALE:
             PlaySE(SE_SELECT);
             gSaveBlock2Ptr->playerGender = gender;
             NewGameBirchSpeech_ClearGenderWindow(1, 1);
-            gTasks[taskId].func = Task_NewGameBirchSpeech_WhatsYourName;
+            gTasks[taskId].func = Task_NewGameBirchSpeech_Nuzlocke;
             break;
     }
     gender2 = Menu_GetCursorPos();
     if (gender2 != gTasks[taskId].tPlayerGender)
@@ -1569,8 +1586,46 @@ static void Task_NewGameBirchSpeech_SlideInNewGenderSprite(u8 taskId)
         }
     }
 }
 
+
+static void Task_NewGameBirchSpeech_Nuzlocke(u8 taskId)
+{
+    NewGameBirchSpeech_ClearWindow(0);
+    StringExpandPlaceholders(gStringVar4, gText_Birch_Nuzlocke);
+    AddTextPrinterForMessage(TRUE);
+    gTasks[taskId].func = Task_NewGameBirchSpeech_WaitToShowNuzlockeMenu;
+}
+
+static void Task_NewGameBirchSpeech_WaitToShowNuzlockeMenu(u8 taskId)
+{
+    if (!RunTextPrintersAndIsPrinter0Active())
+    {
+        NewGameBirchSpeech_ShowNuzlockeMenu();
+        gTasks[taskId].func = Task_NewGameBirchSpeech_ChooseNuzlocke;
+    }
+}
+
+static void Task_NewGameBirchSpeech_ChooseNuzlocke(u8 taskId)
+{
+    int nuzlocke = NewGameBirchSpeech_ProcessNuzlockeMenuInput();
+    switch (nuzlocke)
+    {
+        case 0:
+            PlaySE(SE_SELECT);
+            FlagSet(FLAG_NUZLOCKE);
+            NewGameBirchSpeech_ClearNuzlockeWindow(1, 1);
+            gTasks[taskId].func = Task_NewGameBirchSpeech_WhatsYourName;
+            break;
+        case 1:
+            PlaySE(SE_SELECT);
+            FlagClear(FLAG_NUZLOCKE);
+            NewGameBirchSpeech_ClearNuzlockeWindow(1, 1);
+            gTasks[taskId].func = Task_NewGameBirchSpeech_WhatsYourName;
+            break;
+    }
+}
+
 static void Task_NewGameBirchSpeech_WhatsYourName(u8 taskId)
 {
     NewGameBirchSpeech_ClearWindow(0);
     StringExpandPlaceholders(gStringVar4, gText_Birch_WhatsYourName);
@@ -2101,8 +2156,23 @@ static s8 NewGameBirchSpeech_ProcessGenderMenuInput(void)
 {
     return Menu_ProcessInputNoWrap();
 }
 
+static void NewGameBirchSpeech_ShowNuzlockeMenu(void)
+{
+    DrawMainMenuWindowBorder(&sNewGameBirchSpeechTextWindows[1], 0xF3);
+    FillWindowPixelBuffer(1, PIXEL_FILL(1));
+    PrintMenuTable(1, ARRAY_COUNT(sMenuActions_Nuzlocke), sMenuActions_Nuzlocke);
+    InitMenuInUpperLeftCornerNormal(1, ARRAY_COUNT(sMenuActions_Nuzlocke), 1);
+    PutWindowTilemap(1);
+    CopyWindowToVram(1, COPYWIN_FULL);
+}
+
+static s8 NewGameBirchSpeech_ProcessNuzlockeMenuInput(void)
+{
+    return Menu_ProcessInputNoWrap();
+}
+
 void NewGameBirchSpeech_SetDefaultPlayerName(u8 nameId)
 {
     const u8 *name;
     u8 i;
@@ -2236,8 +2306,22 @@ static void NewGameBirchSpeech_ClearGenderWindow(u8 windowId, bool8 copyToVram)
     if (copyToVram == TRUE)
         CopyWindowToVram(windowId, COPYWIN_FULL);
 }
 
+static void NewGameBirchSpeech_ClearNuzlockeWindowTilemap(u8 bg, u8 x, u8 y, u8 width, u8 height, u8 unused)
+{
+    FillBgTilemapBufferRect(bg, 0, x + 255, y + 255, width + 2, height + 2, 2);
+}
+
+static void NewGameBirchSpeech_ClearNuzlockeWindow(u8 windowId, bool8 copyToVram)
+{
+    CallWindowFunction(windowId, NewGameBirchSpeech_ClearNuzlockeWindowTilemap);
+    FillWindowPixelBuffer(windowId, PIXEL_FILL(1));
+    ClearWindowTilemap(windowId);
+    if (copyToVram == TRUE)
+        CopyWindowToVram(windowId, COPYWIN_FULL);
+}
+
 static void NewGameBirchSpeech_ClearWindow(u8 windowId)
 {
     u8 bgColor = GetFontAttribute(FONT_NORMAL, FONTATTR_COLOR_BACKGROUND);
     u8 maxCharWidth = GetFontAttribute(FONT_NORMAL, FONTATTR_MAX_LETTER_WIDTH);

----------------------------- data/event_scripts.s -----------------------------
index f2db321a5..9f0894b28 100644
@@ -883,8 +883,12 @@ gText_UnusedNicknameReceivedPokemon::
 gText_PlayerWhitedOut::
 	.string "{PLAYER} is out of usable\n"
 	.string "POKéMON!\p{PLAYER} whited out!$"
 
+gText_PlayerNuzlockeFailed::
+	.string "{PLAYER} failed the\n"
+	.string "Nuzlocke challenge.\p"
+	.string "The Nuzlocke setting\n"
+	.string "has been turned off.$"
+
 gText_RegisteredTrainerinPokeNav::
 	.string "Registered {STR_VAR_1} {STR_VAR_2}\n"
 	.string "in the POKéNAV.$"
 

------------ data/maps/LittlerootTown_BrendansHouse_1F/scripts.inc ------------
index cce8cd59b..6351e1097 100644
@@ -415,8 +415,12 @@ PlayersHouse_1F_Text_DadShouldStayLonger:
 	.string "He comes home for the first time in a\n"
 	.string "while, but all he talks about is POKéMON.\p"
 	.string "He should relax and stay a little longer.$"
 
+PlayersHouse_1F_Text_NuzlockeIsOver:
+	.string "Also, you completed the Nuzlocke\n"
+	.string "challenge. It's over. Congrats!$"
+
 PlayersHouse_1F_Text_IsThatABreakingStory:
 	.string "MOM: Is that a breaking news story?$"
 
 PlayersHouse_1F_Text_LatiEmergencyNewsFlash:

------------------------ data/scripts/field_poison.inc ------------------------
index ddda34eba..554a75d69 100644
@@ -10,24 +10,32 @@ EventScript_FieldPoison::
 EventScript_FieldWhiteOut::
 	message gText_PlayerWhitedOut
 	waitmessage
 	waitbuttonpress
+	call_if_set FLAG_NUZLOCKE, EventScript_LostNuzlocke
+EventScript_FieldWhiteOutCont::
 	special Script_FadeOutMapMusic
 	waitstate
 	fadescreen FADE_TO_BLACK
 	call_if_set FLAG_WHITEOUT_TO_LAVARIDGE, EventScript_SetRespawnLavaridgePkmnCenter
 	special SetCB2WhiteOut
 	waitstate
 	end
 
+EventScript_LostNuzlocke::
+	call_if_unset FLAG_SYS_POKEDEX_GET, EventScript_FieldWhiteOutCont
+	clearflag FLAG_NUZLOCKE
+	message gText_PlayerNuzlockeFailed
+	waitmessage
+	waitbuttonpress
+	return
+
 EventScript_SetRespawnLavaridgePkmnCenter::
 	setrespawn HEAL_LOCATION_LAVARIDGE_TOWN
 	return
 
 EventScript_FrontierFieldWhiteOut::
 	message gText_PlayerWhitedOut
 	waitmessage
 	waitbuttonpress
+	call_if_set FLAG_NUZLOCKE, EventScript_LostNuzlocke
 	pike_inchallenge
 	goto_if_eq VAR_RESULT, TRUE, BattleFrontier_BattlePike_EventScript_Retire
 	pyramid_inchallenge
 	goto_if_eq VAR_RESULT, 1, BattleFrontier_BattlePyramid_EventScript_WarpToLobbyLost  @ On Pyramid floor

------------------------ data/scripts/players_house.inc ------------------------
index caa4e6955..b8ce4e2df 100644
@@ -455,8 +455,9 @@ PlayersHouse_1F_EventScript_GetSSTicketAndSeeLatiTV::
 	call_if_eq VAR_0x8008, MALE, PlayersHouse_1F_EventScript_MomApproachPlayerMale
 	call_if_eq VAR_0x8008, FEMALE, PlayersHouse_1F_EventScript_MomApproachPlayerFemale
 	delay 20
 	msgbox PlayersHouse_1F_Text_DadShouldStayLonger, MSGBOX_DEFAULT
+	call_if_set FLAG_NUZLOCKE, PlayersHouse_1F_EventScript_NuzlockeOver
 	closemessage
 	setflag FLAG_SYS_TV_LATIAS_LATIOS
 	special TurnOnTVScreen
 	delay 60
@@ -484,8 +485,15 @@ PlayersHouse_1F_EventScript_GetSSTicketAndSeeLatiTV::
 	setvar VAR_LITTLEROOT_HOUSES_STATE_BRENDAN, 4
 	releaseall
 	end
 
+PlayersHouse_1F_EventScript_NuzlockeOver::
+	playfanfare MUS_OBTAIN_BADGE
+	msgbox PlayersHouse_1F_Text_NuzlockeIsOver, MSGBOX_DEFAULT
+	waitfanfare
+	clearflag FLAG_NUZLOCKE
+	return
+
 @ Never called.
 PlayersHouse_1F_EventScript_AirLatiBroadcast::
 	setflag FLAG_SYS_TV_LATIAS_LATIOS
 	return

-------------------------- data/text/birch_speech.inc --------------------------
index 359d619f1..341f170e9 100644
@@ -34,8 +34,12 @@ gText_Birch_AndYouAre::
 gText_Birch_BoyOrGirl::
 	.string "Are you a boy?\n"
 	.string "Or are you a girl?$"
 
+gText_Birch_Nuzlocke::
+	.string "Will you be performing the\n"
+	.string "Nuzlocke challenge?$"
+
 gText_Birch_WhatsYourName::
 	.string "All right.\n"
 	.string "What's your name?$"

-------------------- include/constants/battle_string_ids.h --------------------
index 18d495d79..26cd431d1 100644
@@ -6,8 +6,9 @@
 #define STRINGID_RETURNMON      2
 #define STRINGID_SWITCHINMON    3
 #define STRINGID_USEDMOVE       4
 #define STRINGID_BATTLEEND      5
+#define STRINGID_NUZLOCKELOST   6
 
 // todo: make some of those names less vague: attacker/target vs pkmn, etc.
 #define STRINGID_TRAINER1LOSETEXT           12
 #define STRINGID_PKMNGAINEDEXP              13
 
------------------------------ include/strings.h ------------------------------
index 4ec286c8b..a209f5ae2 100644
@@ -345,8 +345,9 @@ extern const u8 gText_Birch_Welcome[];
 extern const u8 gText_ThisIsAPokemon[];
 extern const u8 gText_Birch_MainSpeech[];
 extern const u8 gText_Birch_AndYouAre[];
 extern const u8 gText_Birch_BoyOrGirl[];
+extern const u8 gText_Birch_Nuzlocke[];
 extern const u8 gText_Birch_WhatsYourName[];
 extern const u8 gText_Birch_SoItsPlayer[];
 extern const u8 gText_Birch_YourePlayer[];
 extern const u8 gText_Birch_AreYouReady[];
@@ -942,8 +943,10 @@ extern const u8 gText_PlayerUsedVar2[];
 extern const u8 gText_RepelEffectsLingered[];
 extern const u8 gText_UsedVar2WildLured[];
 extern const u8 gText_UsedVar2WildRepelled[];
 extern const u8 gText_BoxFull[];
+extern const u8 gText_BallsCannotBeUsedNuz[];
+extern const u8 gText_WontHaveEffectNuzlocke[];
 extern const u8 gText_WontHaveEffect[];
 
 extern const u8 gText_LevelSymbol[];
 extern const u8 gText_PkmnInfo[];

------------------------------ src/battle_main.c ------------------------------
index 54ff4d614..4f82d8d75 100644
@@ -3669,8 +3669,18 @@ static void BattleIntroPrintWildMonAttacked(void)
         PrepareStringBattle(STRINGID_INTROMSG, 0);
     }
 }
 
+
+static void BattleLostNuzlocke(void)
+{
+    if (gBattleControllerExecFlags == 0)
+    {
+        gBattleMainFunc = HandleEndTurn_FinishBattle;
+        PrepareStringBattle(STRINGID_NUZLOCKELOST, 0);
+        FlagClear(FLAG_NUZLOCKE);
+    }
+}
+
 static void BattleIntroPrintOpponentSendsOut(void)
 {
     u32 position;
 
@@ -5126,8 +5136,13 @@ static void HandleEndTurn_BattleLost(void)
     }
     else
     {
         gBattlescriptCurrInstr = BattleScript_LocalBattleLost;
+        if (FlagGet(FLAG_NUZLOCKE) && FlagGet(FLAG_SYS_POKEDEX_GET)){
+            gBattleMainFunc = BattleLostNuzlocke;
+            return;
+        }
     }
 
     gBattleMainFunc = HandleEndTurn_FinishBattle;
 }
@@ -5177,8 +5192,9 @@ static void HandleEndTurn_MonFled(void)
 }
 
 static void HandleEndTurn_FinishBattle(void)
 {
+    gNuzlockeCannotCatch = 0;  // While not necissary, resetting this is nice to stay deterministic
     if (gCurrentActionFuncId == B_ACTION_TRY_FINISH || gCurrentActionFuncId == B_ACTION_FINISHED)
     {
         if (!(gBattleTypeFlags & (BATTLE_TYPE_LINK
                                   | BATTLE_TYPE_RECORDED_LINK

-------------------------------- src/strings.c --------------------------------
index 0ee1a8896..69c4ccdbb 100644
@@ -248,8 +248,9 @@ const u8 gText_RepelEffectsLingered[] = _("But the effects of a REPEL\nlingered
 const u8 gText_UsedVar2WildLured[] = _("{PLAYER} used the\n{STR_VAR_2}.\pWild POKéMON will be lured.{PAUSE_UNTIL_PRESS}");
 const u8 gText_UsedVar2WildRepelled[] = _("{PLAYER} used the\n{STR_VAR_2}.\pWild POKéMON will be repelled.{PAUSE_UNTIL_PRESS}");
 const u8 gText_BoxFull[] = _("The BOX is full.{PAUSE_UNTIL_PRESS}");
 const u8 gText_PowderQty[] = _("POWDER QTY: {STR_VAR_1}{PAUSE_UNTIL_PRESS}");
+const u8 gText_BallsCannotBeUsedNuz[] = _("You already saw a POKéMON\non {STR_VAR_1}!{PAUSE_UNTIL_PRESS}");
 const u8 gText_TheField[] = _("the field");
 const u8 gText_TheBattle[] = _("the battle");
 const u8 gText_ThePokemonList[] = _("the POKéMON LIST");
 const u8 gText_TheShop[] = _("the shop");
@@ -374,8 +375,9 @@ const u8 gText_Read2[] = _("READ");
 const u8 gText_Trade4[] = _("TRADE");
 const u8 gText_HP3[] = _("HP");
 const u8 gText_SpAtk3[] = _("SP. ATK");
 const u8 gText_SpDef3[] = _("SP. DEF");
+const u8 gText_WontHaveEffectNuzlocke[] = _("It won't have any effect due to\nrunning Nuzlocke challenge.{PAUSE_UNTIL_PRESS}");
 const u8 gText_WontHaveEffect[] = _("It won't have any effect.{PAUSE_UNTIL_PRESS}");
 const u8 gText_CantBeUsedOnPkmn[] = _("This can't be used on\nthat POKéMON.{PAUSE_UNTIL_PRESS}");
 const u8 gText_PkmnCantSwitchOut[] = _("{STR_VAR_1} can't be switched\nout!{PAUSE_UNTIL_PRESS}");
 const u8 gText_PkmnAlreadyInBattle[] = _("{STR_VAR_1} is already\nin battle!{PAUSE_UNTIL_PRESS}");

 -------------------------------- src/new_game.c --------------------------------
index a398d8457..dc7f8365b 100644
@@ -152,8 +152,9 @@ void ResetMenuAndMonGlobals(void)
 
 void NewGameInitData(void)
 {
+    bool8 nuzlockePrev = FlagGet(FLAG_NUZLOCKE);  // A function lower down here clears this, so retain it and reset it at the end
     if (gSaveFileStatus == SAVE_STATUS_EMPTY || gSaveFileStatus == SAVE_STATUS_CORRUPT)
         RtcReset();
 
     gDifferentSaveFile = TRUE;
@@ -211,8 +212,9 @@ void NewGameInitData(void)
     WipeTrainerNameRecords();
     ResetTrainerHillResults();
     ResetContestLinkResults();
+    nuzlockePrev ? FlagSet(FLAG_NUZLOCKE) : FlagClear(FLAG_NUZLOCKE);
 }

Pokemon that Faint Cannot be Revived

------------------------------- src/party_menu.c -------------------------------

static void SetPartyMonFieldSelectionActions(struct Pokemon *mons, u8 slotId)
{
    u8 i, j;

    sPartyMenuInternal->numActions = 0;
    AppendToList(sPartyMenuInternal->actions, &sPartyMenuInternal->numActions, MENU_SUMMARY);
+   // This if statement causes dead pokemon to only be able to show summary, switch, and cancel. No field moves or items.
+   if (GetMonData(&mons[slotId], MON_DATA_DEAD) && FlagGet(FLAG_NUZLOCKE))
+   {
+       if (GetMonData(&mons[1], MON_DATA_SPECIES) != SPECIES_NONE)
+           AppendToList(sPartyMenuInternal->actions, &sPartyMenuInternal->numActions, MENU_SWITCH);
+       AppendToList(sPartyMenuInternal->actions, &sPartyMenuInternal->numActions, MENU_CANCEL1);
+       return;
+   }

    // Add field moves to action list
    for (i = 0; i < MAX_MON_MOVES; i++)
    {
        for (j = 0; sFieldMoves[j] != FIELD_MOVES_COUNT; j++)
        {
            if (GetMonData(&mons[slotId], i + MON_DATA_MOVE1) == sFieldMoves[j])



index 9dfe5d306..3840ca57d 100755
@@ -4435,9 +4435,9 @@ void ItemUseCB_Medicine(u8 taskId, TaskFunc task)
         canHeal = IsHPRecoveryItem(item);
         if (canHeal == TRUE)
         {
             hp = GetMonData(mon, MON_DATA_HP);
             if (hp == GetMonData(mon, MON_DATA_MAX_HP))
                 canHeal = FALSE;
         }
         cannotUse = ExecuteTableBasedItemEffect_(gPartyMenu.slotId, item, 0);
     }
@@ -4445,9 +4445,12 @@ void ItemUseCB_Medicine(u8 taskId, TaskFunc task)
     if (cannotUse != FALSE)
     {
         gPartyMenuUseExitCallback = FALSE;
         PlaySE(SE_SELECT);
-        DisplayPartyMenuMessage(gText_WontHaveEffect, TRUE);
+        if (canHeal && FlagGet(FLAG_NUZLOCKE) && GetMonData(mon, MON_DATA_DEAD))
+            DisplayPartyMenuMessage(gText_WontHaveEffectNuzlocke, TRUE);
+        else
+            DisplayPartyMenuMessage(gText_WontHaveEffect, TRUE);
         ScheduleBgCopyTilemapToVram(2);
         gTasks[taskId].func = task;
     }
     else
------------------------------ include/pokemon.h ------------------------------
struct BoxPokemon
{
    u32 personality;
    u32 otId;
    u8 nickname[POKEMON_NAME_LENGTH];
    u8 language;
    u8 isBadEgg:1;
    u8 hasSpecies:1;
    u8 isEgg:1;
-    u8 unused:5;
+    u8 dead:1;
+    u8 unused:4;
    u8 otName[PLAYER_NAME_LENGTH];
    u8 markings;
    u16 checksum;
    u16 unknown;

    union
    {
        u32 raw[(NUM_SUBSTRUCT_BYTES * 4) / 4]; // *4 because there are 4 substructs, /4 because it's u32, not u8
        union PokemonSubstruct substructs[4];
    } secure;
};

-------------------------------- src/pokemon.c --------------------------------
void BoxMonToMon(const struct BoxPokemon *src, struct Pokemon *dest)
{
    u32 value = 0;
    dest->box = *src;
    SetMonData(dest, MON_DATA_STATUS, &value);
    SetMonData(dest, MON_DATA_HP, &value);
    SetMonData(dest, MON_DATA_MAX_HP, &value);
    value = MAIL_NONE;
    SetMonData(dest, MON_DATA_MAIL, &value);
    CalculateMonStats(dest);
+   if (GetMonData(dest, MON_DATA_DEAD) && FlagGet(FLAG_NUZLOCKE))
+   {
+       value = 0;
+       SetMonData(dest, MON_DATA_HP, &value);
+   }
}


@@ -4150,13 +4150,15 @@ u32 GetBoxMonData(struct BoxPokemon *boxMon, s32 field, u8 *data)
    }
    case MON_DATA_LANGUAGE:
        retVal = boxMon->language;
        break;
+    case MON_DATA_DEAD:
+        retVal = boxMon->dead;
+        break;
    case MON_DATA_SANITY_IS_BAD_EGG:
        retVal = boxMon->isBadEgg;
        break;

index 67ae85f13..583a79d43 100644
@@ -4150,13 +4150,15 @@ void SetBoxMonData(struct BoxPokemon *boxMon, s32 field, const void *dataArg)
         break;
     }
     case MON_DATA_LANGUAGE:
         SET8(boxMon->language);
         break;
+     case MON_DATA_DEAD:
+         SET8(boxMon->dead);
+         break;
     case MON_DATA_SANITY_IS_BAD_EGG:
         SET8(boxMon->isBadEgg);
         break;

-------------------------- src/script_pokemon_util.c --------------------------
index b18202096..056e1c5db 100755
@@ -35,11 +35,19 @@ void HealPlayerParty(void)
 
     // restore HP.
     for(i = 0; i < gPlayerPartyCount; i++)
     {
+        u16 maxHP;
+        if (GetMonData(&gPlayerParty[i], MON_DATA_DEAD)){
+            if (!FlagGet(FLAG_NUZLOCKE) || !FlagGet(FLAG_SYS_POKEDEX_GET)){
+                bool8 dead = FALSE;
+                SetMonData(&gPlayerParty[i], MON_DATA_DEAD, &dead);
+            }
+            else{
+                continue;
+            }
+        }
         maxHP = GetMonData(&gPlayerParty[i], MON_DATA_MAX_HP);
         arg[0] = maxHP;
         arg[1] = maxHP >> 8;
         SetMonData(&gPlayerParty[i], MON_DATA_HP, arg);
         ppBonuses = GetMonData(&gPlayerParty[i], MON_DATA_PP_BONUSES);

------------------------- src/battle_script_commands.c -------------------------
index 7bbb5e186..adbe50754 100644
@@ -2995,9 +2995,9 @@ static void Cmd_tryfaintmon(void)
             BattleScriptPush(gBattlescriptCurrInstr + 7);
             gBattlescriptCurrInstr = BS_ptr;
             if (GetBattlerSide(gActiveBattler) == B_SIDE_PLAYER)
             {
+                 if (FlagGet(FLAG_NUZLOCKE) && FlagGet(FLAG_SYS_POKEDEX_GET)){
+                     bool8 dead = TRUE;
+                     SetMonData(&gPlayerParty[gBattlerPartyIndexes[gActiveBattler]], MON_DATA_DEAD, &dead);
+                 }
                 gHitMarker |= HITMARKER_PLAYER_FAINTED;

--------------------------------- src/trade.c ---------------------------------
index a115d778d..d7454c35d 100644
@@ -1556,8 +1556,9 @@ static void ConfirmOrCancelTrade(void)
 {
     switch (Menu_ProcessInputNoWrapClearOnChoose())
     {
     case 0: // Confirm Trade
         if (!CheckMonsBeforeTrade())
             sTradeMenuData->tradeMenuFunc = TRADEMENUFUNC_STANDBY;
         else
             sTradeMenuData->tradeMenuFunc = TRADEMENUFUNC_PARTNER_MON_INVALID;
@@ -3014,15 +3015,19 @@ static void TryEnableNationalDexFromLinkPartner(void)
 
 static void TradeMons(u8 playerPartyIdx, u8 partnerPartyIdx)
 {
     u8 friendship;
+    bool8 dead = FALSE;  // Resets if Pokemon was considered dead through Nuzlocke
 
     struct Pokemon *playerMon = &gPlayerParty[playerPartyIdx];
     u16 playerMail = GetMonData(playerMon, MON_DATA_MAIL);
 
     struct Pokemon *partnerMon = &gEnemyParty[partnerPartyIdx];
     u16 partnerMail = GetMonData(partnerMon, MON_DATA_MAIL);
 
+    SetMonData(playerMon, MON_DATA_DEAD, &dead);
     if (playerMail != MAIL_NONE)
         ClearMail(&gSaveBlock1Ptr->mail[playerMail]);
 
     SWAP(*playerMon, *partnerMon, sTradeData->tempMon);

-------------------------------- src/scrcmd.c --------------------------------
bool8 ScrCmd_checkpartymove(struct ScriptContext *ctx)
{
    u8 i;
    u16 moveId = ScriptReadHalfword(ctx);

    gSpecialVar_Result = PARTY_SIZE;
    for (i = 0; i < PARTY_SIZE; i++)
    {
        u16 species = GetMonData(&gPlayerParty[i], MON_DATA_SPECIES, NULL);
        if (!species)
            break;
-       if (!GetMonData(&gPlayerParty[i], MON_DATA_IS_EGG) && MonKnowsMove(&gPlayerParty[i], moveId) == TRUE)
+       if (!GetMonData(&gPlayerParty[i], MON_DATA_IS_EGG) && MonKnowsMove(&gPlayerParty[i], moveId) == TRUE
+           && !(GetMonData(&gPlayerParty[i], MON_DATA_DEAD) && FlagGet(FLAG_NUZLOCKE)))
+           // Nuzlocke check to stop a dead Pokemon from using field moves.
        {
            gSpecialVar_Result = i;

Can Only Catch First Pokemon

---------------------------- include/battle_setup.h ----------------------------
index 5a1d5bab3..9126aeeb4 100644
@@ -16,8 +16,9 @@ extern const struct RematchTrainer gRematchTable[REMATCH_TABLE_ENTRIES];
 
 extern u16 gTrainerBattleOpponent_A;
 extern u16 gTrainerBattleOpponent_B;
 extern u16 gPartnerTrainerId;
+extern bool8 gNuzlockeCannotCatch;
 
 void BattleSetup_StartWildBattle(void);
 void BattleSetup_StartBattlePikeWildBattle(void);
 void BattleSetup_StartRoamerBattle(void);
@@ -62,8 +63,11 @@ u16 GetLastBeatenRematchTrainerId(u16 trainerId);
 bool8 ShouldTryRematchBattle(void);
 bool8 IsTrainerReadyForRematch(void);
 void ShouldTryGetTrainerScript(void);
 u16 CountBattledRematchTeams(u16 trainerId);
+u8 HasWildPokmnOnThisRouteBeenSeen(u8 currLocation, bool8 setVarForThisEnc);
+u8 currLocConvertForNuzlocke(u8 currLocation);
 
 void DoStandardWildBattle_Debug(void);
 void BattleSetup_StartTrainerBattle_Debug(void);

--------------------------- include/constants/vars.h ---------------------------
index 7457d6236..214b80d1b 100644
@@ -262,13 +262,13 @@
 #define VAR_SCOTT_FORTREE_CALL_STEP_COUNTER              0x40F3
 #define VAR_ROXANNE_CALL_STEP_COUNTER                    0x40F4
 #define VAR_SCOTT_BF_CALL_STEP_COUNTER                   0x40F5
 #define VAR_RIVAL_RAYQUAZA_CALL_STEP_COUNTER             0x40F6
-#define VAR_UNUSED_0x40F7                                0x40F7 // Unused Var
-#define VAR_UNUSED_0x40F8                                0x40F8 // Unused Var
-#define VAR_UNUSED_0x40F9                                0x40F9 // Unused Var
-#define VAR_UNUSED_0x40FA                                0x40FA // Unused Var
-#define VAR_UNUSED_0x40FB                                0x40FB // Unused Var
+#define VAR_WILD_PKMN_ROUTE_SEEN_0                       0x40F7
+#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_UNUSED_0x40FD                                0x40FD // Unused Var
 #define VAR_UNUSED_0x40FE                                0x40FE // Unused Var
 #define VAR_UNUSED_0x40FF                                0x40FF // Unused Var

----------------------------- src/battle_message.c -----------------------------
index 0c31b6e2a..4b977a229 100644
@@ -27,8 +27,9 @@
 #include "constants/moves.h"
 #include "constants/trainers.h"
 #include "constants/trainer_hill.h"
 #include "constants/weather.h"
+#include "overworld.h"
 
 struct BattleWindowText
 {
     u8 fillValue;
@@ -77,8 +78,9 @@ static const u8 sText_AttackerFainted[] = _("{B_ATK_NAME_WITH_PREFIX}\nfainted!\
 static const u8 sText_TargetFainted[] = _("{B_DEF_NAME_WITH_PREFIX}\nfainted!\p");
 static const u8 sText_PlayerGotMoney[] = _("{B_PLAYER_NAME} got ¥{B_BUFF1}\nfor winning!\p");
 static const u8 sText_PlayerWhiteout[] = _("{B_PLAYER_NAME} is out of\nusable POKéMON!\p");
 static const u8 sText_PlayerWhiteout2[] = _("{B_PLAYER_NAME} panicked and lost ¥{B_BUFF1}…\p{B_PLAYER_NAME} whited out!{PAUSE_UNTIL_PRESS}");
+static const u8 sText_PlayerFailedNuzlocke[] = _("{B_PLAYER_NAME} failed the\nNuzlocke challenge.\pThe Nuzlocke setting\nhas been turned off.\p");
 static const u8 sText_PreventsEscape[] = _("{B_SCR_ACTIVE_NAME_WITH_PREFIX} prevents\nescape with {B_SCR_ACTIVE_ABILITY}!\p");
 static const u8 sText_CantEscape2[] = _("Can't escape!\p");
 static const u8 sText_AttackerCantEscape[] = _("{B_ATK_NAME_WITH_PREFIX} can't escape!");
 static const u8 sText_HitXTimes[] = _("Hit {B_BUFF1} time(s)!");
@@ -2260,8 +2262,9 @@ void BufferStringBattle(u16 stringID)
                 stringPtr = sText_YourFoesWeakGetEmPkmn;
         }
         else
         {
+            gNuzlockeCannotCatch = HasWildPokmnOnThisRouteBeenSeen(GetCurrentRegionMapSectionId(), FALSE);
             if (gBattleTypeFlags & (BATTLE_TYPE_LINK | BATTLE_TYPE_RECORDED_LINK))
             {
                 if (gBattleTypeFlags & BATTLE_TYPE_TOWER_LINK_MULTI)
                 {
@@ -2375,8 +2378,11 @@ void BufferStringBattle(u16 stringID)
                 }
             }
         }
         break;
+    case STRINGID_NUZLOCKELOST:
+        stringPtr = sText_PlayerFailedNuzlocke;
+        break;
     default: // load a string from the table
         if (stringID >= BATTLESTRINGS_COUNT)
         {
             gDisplayedStringBattle[0] = EOS;


------------------------------ src/battle_setup.c ------------------------------
index b503f8d65..36da06845 100644
@@ -46,8 +46,10 @@
 #include "constants/map_types.h"
 #include "constants/trainers.h"
 #include "constants/trainer_hill.h"
 #include "constants/weather.h"
+#include "overworld.h"
+#include "constants/region_map_sections.h"
 
 enum {
     TRANSITION_TYPE_NORMAL,
     TRANSITION_TYPE_CAVE,
@@ -95,8 +97,9 @@ static const u8 *GetTrainerCantBattleSpeech(void);
 EWRAM_DATA static u16 sTrainerBattleMode = 0;
 EWRAM_DATA u16 gTrainerBattleOpponent_A = 0;
 EWRAM_DATA u16 gTrainerBattleOpponent_B = 0;
 EWRAM_DATA u16 gPartnerTrainerId = 0;
+EWRAM_DATA bool8 gNuzlockeCannotCatch = 0;
 EWRAM_DATA static u16 sTrainerObjectEventLocalId = 0;
 EWRAM_DATA static u8 *sTrainerAIntroSpeech = NULL;
 EWRAM_DATA static u8 *sTrainerBIntroSpeech = NULL;
 EWRAM_DATA static u8 *sTrainerADefeatSpeech = NULL;
@@ -416,8 +419,9 @@ static void CreateBattleStartTask_Debug(u8 transition, u16 song)
 #undef tTransition
 
 void BattleSetup_StartWildBattle(void)
 {
+    gNuzlockeCannotCatch = HasWildPokmnOnThisRouteBeenSeen(GetCurrentRegionMapSectionId(), TRUE);
     if (GetSafariZoneFlag())
         DoSafariBattle();
     else
         DoStandardWildBattle();
@@ -1318,8 +1322,9 @@ void ClearTrainerFlag(u16 trainerId)
 }
 
 void BattleSetup_StartTrainerBattle(void)
 {
+    gNuzlockeCannotCatch = HasWildPokmnOnThisRouteBeenSeen(GetCurrentRegionMapSectionId(), FALSE);
     if (gNoOfApproachingTrainers == 2)
         gBattleTypeFlags = (BATTLE_TYPE_DOUBLE | BATTLE_TYPE_TWO_OPPONENTS | BATTLE_TYPE_TRAINER);
     else
         gBattleTypeFlags = (BATTLE_TYPE_TRAINER);
@@ -1947,4 +1952,396 @@ u16 CountBattledRematchTeams(u16 trainerId)
     }
 
     return i;
 }
+
+u8 HasWildPokmnOnThisRouteBeenSeen(u8 currLocation, bool8 setVarForThisEnc){
+    u8 varToCheck, bitToCheck;
+    u16 varValue;
+    const u16 pkmnSeenVars[] = {
+    VAR_WILD_PKMN_ROUTE_SEEN_0,
+    VAR_WILD_PKMN_ROUTE_SEEN_1,
+    VAR_WILD_PKMN_ROUTE_SEEN_2,
+    VAR_WILD_PKMN_ROUTE_SEEN_3,
+    VAR_WILD_PKMN_ROUTE_SEEN_4,
+    };
+    currLocation = currLocConvertForNuzlocke(currLocation);
+    if (!FlagGet(FLAG_NUZLOCKE) || !FlagGet(FLAG_SYS_POKEDEX_GET)){
+        VarSet(VAR_WILD_PKMN_ROUTE_SEEN_0, 0);
+        VarSet(VAR_WILD_PKMN_ROUTE_SEEN_1, 0);
+        VarSet(VAR_WILD_PKMN_ROUTE_SEEN_2, 0);
+        VarSet(VAR_WILD_PKMN_ROUTE_SEEN_3, 0);
+        VarSet(VAR_WILD_PKMN_ROUTE_SEEN_4, 0);
+        return 0;
+    }
+    switch (currLocation)
+    {
+    case MAPSEC_LITTLEROOT_TOWN:
+        varToCheck = 0;
+        bitToCheck = 0;
+        break;
+    case MAPSEC_OLDALE_TOWN:
+        varToCheck = 0;
+        bitToCheck = 1;
+        break;
+        break;
+    case MAPSEC_DEWFORD_TOWN:
+        varToCheck = 0;
+        bitToCheck = 2;
+        break;
+    case MAPSEC_LAVARIDGE_TOWN:
+        varToCheck = 0;
+        bitToCheck = 3;
+        break;
+    case MAPSEC_FALLARBOR_TOWN:
+        varToCheck = 0;
+        bitToCheck = 4;
+        break;
+    case MAPSEC_VERDANTURF_TOWN:
+        varToCheck = 0;
+        bitToCheck = 5;
+        break;
+    case MAPSEC_PACIFIDLOG_TOWN:
+        varToCheck = 0;
+        bitToCheck = 6;
+        break;
+    case MAPSEC_PETALBURG_CITY:
+        varToCheck = 0;
+        bitToCheck = 7;
+        break;
+    case MAPSEC_SLATEPORT_CITY:
+        varToCheck = 0;
+        bitToCheck = 8;
+        break;
+    case MAPSEC_MAUVILLE_CITY:
+        varToCheck = 0;
+        bitToCheck = 9;
+        break;
+    case MAPSEC_RUSTBORO_CITY:
+        varToCheck = 0;
+        bitToCheck = 10;
+        break;
+    case MAPSEC_FORTREE_CITY:
+        varToCheck = 0;
+        bitToCheck = 11;
+        break;
+    case MAPSEC_LILYCOVE_CITY:
+        varToCheck = 0;
+        bitToCheck = 12;
+        break;
+    case MAPSEC_MOSSDEEP_CITY:
+        varToCheck = 0;
+        bitToCheck = 13;
+        break;
+    case MAPSEC_SOOTOPOLIS_CITY:
+        varToCheck = 0;
+        bitToCheck = 14;
+        break;
+    case MAPSEC_EVER_GRANDE_CITY:
+        varToCheck = 0;
+        bitToCheck = 15;   
+        break;
+        
+    case MAPSEC_ROUTE_101:
+        varToCheck = 1;
+        bitToCheck = 0;
+        break;
+    case MAPSEC_ROUTE_102:
+        varToCheck = 1;
+        bitToCheck = 1;
+        break;
+    case MAPSEC_ROUTE_103:
+        varToCheck = 1;
+        bitToCheck = 2;
+        break;
+    case MAPSEC_ROUTE_104:
+        varToCheck = 1;
+        bitToCheck = 3;
+        break;
+    case MAPSEC_ROUTE_105:
+        varToCheck = 1;
+        bitToCheck = 4;
+        break;
+    case MAPSEC_ROUTE_106:
+        varToCheck = 1;
+        bitToCheck = 5;
+        break;
+    case MAPSEC_ROUTE_107:
+        varToCheck = 1;
+        bitToCheck = 6;
+        break;
+    case MAPSEC_ROUTE_108:
+        varToCheck = 1;
+        bitToCheck = 7;
+        break;
+    case MAPSEC_ROUTE_109:
+        varToCheck = 1;
+        bitToCheck = 8;
+        break;
+    case MAPSEC_ROUTE_110:
+        varToCheck = 1;
+        bitToCheck = 9;
+        break;
+    case MAPSEC_ROUTE_111:
+        varToCheck = 1;
+        bitToCheck = 10;
+        break;
+    case MAPSEC_ROUTE_112:
+        varToCheck = 1;
+        bitToCheck = 11;
+        break;
+    case MAPSEC_ROUTE_113:
+        varToCheck = 1;
+        bitToCheck = 12;
+        break;
+    case MAPSEC_ROUTE_114:
+        varToCheck = 1;
+        bitToCheck = 13;
+        break;
+    case MAPSEC_ROUTE_115:
+        varToCheck = 1;
+        bitToCheck = 14;
+        break;
+    case MAPSEC_ROUTE_116:
+        varToCheck = 1;
+        bitToCheck = 15;
+        break;
+        
+    case MAPSEC_ROUTE_117:
+        varToCheck = 2;
+        bitToCheck = 0;
+        break;
+    case MAPSEC_ROUTE_118:
+        varToCheck = 2;
+        bitToCheck = 1;
+        break;
+    case MAPSEC_ROUTE_119:
+        varToCheck = 2;
+        bitToCheck = 2;
+        break;
+    case MAPSEC_ROUTE_120:
+        varToCheck = 2;
+        bitToCheck = 3;
+        break;
+    case MAPSEC_ROUTE_121:
+        varToCheck = 2;
+        bitToCheck = 4;
+        break;
+    case MAPSEC_ROUTE_122:
+        varToCheck = 2;
+        bitToCheck = 5;
+        break;
+    case MAPSEC_ROUTE_123:
+        varToCheck = 2;
+        bitToCheck = 6;
+        break;
+    case MAPSEC_ROUTE_124:
+        varToCheck = 2;
+        bitToCheck = 7;
+        break;
+    case MAPSEC_ROUTE_125:
+        varToCheck = 2;
+        bitToCheck = 8;
+        break;
+    case MAPSEC_ROUTE_126:
+        varToCheck = 2;
+        bitToCheck = 9;
+        break;
+    case MAPSEC_ROUTE_127:
+        varToCheck = 2;
+        bitToCheck = 10;
+        break;
+    case MAPSEC_ROUTE_128:
+        varToCheck = 2;
+        bitToCheck = 11;
+        break;
+    case MAPSEC_ROUTE_129:
+        varToCheck = 2;
+        bitToCheck = 12;
+        break;
+    case MAPSEC_ROUTE_130:
+        varToCheck = 2;
+        bitToCheck = 13;	
+        break;
+    case MAPSEC_ROUTE_131:
+        varToCheck = 2;
+        bitToCheck = 14;
+        break;
+    case MAPSEC_ROUTE_132:
+        varToCheck = 2;
+        bitToCheck = 15;
+        break;
+
+    case MAPSEC_ROUTE_133:
+        varToCheck = 3;
+        bitToCheck = 0;
+        break;
+    case MAPSEC_ROUTE_134:
+        varToCheck = 3;
+        bitToCheck = 1;
+        break;
+    case MAPSEC_GRANITE_CAVE:
+        varToCheck = 3;
+        bitToCheck = 2;
+        break;
+    case MAPSEC_MT_CHIMNEY:
+        varToCheck = 3;
+        bitToCheck = 3;
+        break;
+    case MAPSEC_SAFARI_ZONE:
+        varToCheck = 3;
+        bitToCheck = 4;
+        break;
+    case MAPSEC_BATTLE_FRONTIER:
+        varToCheck = 3;
+        bitToCheck = 5;
+        break;
+    case MAPSEC_PETALBURG_WOODS:
+        varToCheck = 3;
+        bitToCheck = 6;
+        break;
+    case MAPSEC_RUSTURF_TUNNEL:
+        varToCheck = 3;
+        bitToCheck = 7;
+        break;
+    case MAPSEC_ABANDONED_SHIP:
+        varToCheck = 3;
+        bitToCheck = 8;
+        break;
+    case MAPSEC_METEOR_FALLS:
+        varToCheck = 3;
+        bitToCheck = 9;
+        break;
+    case MAPSEC_MT_PYRE:
+        varToCheck = 3;
+        bitToCheck = 10;
+        break;
+    case MAPSEC_AQUA_HIDEOUT_OLD:
+        varToCheck = 3;
+        bitToCheck = 11;
+        break;
+    case MAPSEC_SHOAL_CAVE:
+        varToCheck = 3;
+        bitToCheck = 12;
+        break;
+    case MAPSEC_SEAFLOOR_CAVERN:
+        varToCheck = 3;
+        bitToCheck = 13;
+        break;
+    case MAPSEC_VICTORY_ROAD:
+        varToCheck = 3;
+        bitToCheck = 14;
+        break;
+    case MAPSEC_MIRAGE_ISLAND:
+        varToCheck = 3;
+        bitToCheck = 15;
+        break;
+        
+    case MAPSEC_CAVE_OF_ORIGIN:
+        varToCheck = 4;
+        bitToCheck = 0;
+        break;
+    case MAPSEC_FIERY_PATH:
+        varToCheck = 4;
+        bitToCheck = 1;
+        break;
+    case MAPSEC_JAGGED_PASS:
+        varToCheck = 4;
+        bitToCheck = 2;
+        break;
+    case MAPSEC_SEALED_CHAMBER:
+        varToCheck = 4;
+        bitToCheck = 3;
+        break;
+    case MAPSEC_SCORCHED_SLAB:
+        varToCheck = 4;
+        bitToCheck = 4;
+        break;
+    case MAPSEC_AQUA_HIDEOUT:
+        varToCheck = 4;
+        bitToCheck = 5;
+        break;
+    case MAPSEC_MAGMA_HIDEOUT:
+        varToCheck = 4;
+        bitToCheck = 6;
+        break;
+    case MAPSEC_FARAWAY_ISLAND:
+        varToCheck = 4;
+        bitToCheck = 7;
+        break;
+    case MAPSEC_ARTISAN_CAVE:
+        varToCheck = 4;
+        bitToCheck = 8;
+        break;
+    case MAPSEC_DESERT_UNDERPASS:
+        varToCheck = 4;
+        bitToCheck = 9;
+        break;
+    case MAPSEC_ALTERING_CAVE:
+        varToCheck = 4;
+        bitToCheck = 10;
+    default:
+        return 0;
+    }
+    varValue = VarGet(pkmnSeenVars[varToCheck]);
+    if ((varValue & (1 << bitToCheck)) != 0){
+        return 1;
+    }
+    else if (setVarForThisEnc){
+        VarSet(pkmnSeenVars[varToCheck], varValue | (1 << bitToCheck));
+    }
+    return 0;
+}
+
+u8 currLocConvertForNuzlocke(u8 currLocation){
+    switch (currLocation)
+    {
+    case MAPSEC_MAUVILLE_CITY:
+    case MAPSEC_NEW_MAUVILLE:
+		return MAPSEC_MAUVILLE_CITY;
+    case MAPSEC_SOOTOPOLIS_CITY:
+    case MAPSEC_UNDERWATER_SOOTOPOLIS:
+        return MAPSEC_SOOTOPOLIS_CITY;
+    case MAPSEC_ROUTE_105:
+    case MAPSEC_UNDERWATER_105:
+		return MAPSEC_ROUTE_105;
+    case MAPSEC_ROUTE_111:
+    case MAPSEC_DESERT_RUINS:
+    case MAPSEC_MIRAGE_TOWER:
+		return MAPSEC_ROUTE_111;
+    case MAPSEC_ROUTE_124:
+    case MAPSEC_UNDERWATER_124:
+		return MAPSEC_ROUTE_124;
+    case MAPSEC_ROUTE_125:
+    case MAPSEC_UNDERWATER_125:
+        return MAPSEC_ROUTE_125;
+    case MAPSEC_ROUTE_126:
+    case MAPSEC_UNDERWATER_126:
+        return MAPSEC_ROUTE_126;
+    case MAPSEC_ROUTE_127:
+    case MAPSEC_UNDERWATER_127:
+        return MAPSEC_ROUTE_127;
+    case MAPSEC_ROUTE_128:
+    case MAPSEC_UNDERWATER_128:
+        return MAPSEC_ROUTE_128;
+    case MAPSEC_ROUTE_129:
+    case MAPSEC_UNDERWATER_129:
+        return MAPSEC_ROUTE_129;
+    case MAPSEC_METEOR_FALLS:
+    case MAPSEC_METEOR_FALLS2:
+        return MAPSEC_METEOR_FALLS;
+    case MAPSEC_SEAFLOOR_CAVERN:
+    case MAPSEC_UNDERWATER_SEAFLOOR_CAVERN:
+        return MAPSEC_SEAFLOOR_CAVERN;
+    case MAPSEC_FIERY_PATH:
+    case MAPSEC_FIERY_PATH2:
+        return MAPSEC_FIERY_PATH;
+    case MAPSEC_JAGGED_PASS:
+    case MAPSEC_JAGGED_PASS2:
+        return MAPSEC_JAGGED_PASS;
+    case MAPSEC_SEALED_CHAMBER:
+    case MAPSEC_UNDERWATER_SEALED_CHAMBER:
+        return MAPSEC_SEALED_CHAMBER;
+    default:
+        return currLocation;
+    }
+}

-------------------------------- src/item_use.c --------------------------------
index d663bbeb6..403ff54fa 100755
@@ -41,8 +41,10 @@
 #include "constants/event_objects.h"
 #include "constants/item_effects.h"
 #include "constants/items.h"
 #include "constants/songs.h"
+#include "battle_setup.h"
+#include "region_map.h"
 
 static void SetUpItemUseCallback(u8);
 static void FieldCB_UseItemOnField(void);
 static void Task_CallItemUseOnFieldCallback(u8);
@@ -949,9 +951,13 @@ void ItemUseInBattle_PokeBall(u8 taskId)
         DisplayItemMessage(taskId, 1, sText_BallsCannotBeUsed, CloseItemMessage);
         return;
     }
 #endif

+    if (gNuzlockeCannotCatch == 1){
+        GetMapNameHandleAquaHideout(gStringVar1, currLocConvertForNuzlocke(GetCurrentRegionMapSectionId()));
+        DisplayItemMessage(taskId, FONT_NORMAL, gText_BallsCannotBeUsedNuz, CloseItemMessage);
+        return;
+    }
     if (IsPlayerPartyAndPokemonStorageFull() == FALSE) // have room for mon?
     {
         RemoveBagItem(gSpecialVar_ItemId, 1);
         if (!InBattlePyramid())

Dupes Clause:

-------------------- include/constants/battle_string_ids.h --------------------
index 26cd431d1..f96aa3333 100644
@@ -7,8 +7,9 @@
 #define STRINGID_SWITCHINMON    3
 #define STRINGID_USEDMOVE       4
 #define STRINGID_BATTLEEND      5
 #define STRINGID_NUZLOCKELOST   6
+#define STRINGID_NUZLOCKEDUPS   7
 
 // todo: make some of those names less vague: attacker/target vs pkmn, etc.
 #define STRINGID_TRAINER1LOSETEXT           12
 #define STRINGID_PKMNGAINEDEXP              13

------------------------------ src/battle_main.c ------------------------------
index fd3986fe8..4d534b447 100644
@@ -3675,12 +3675,18 @@ static void BattleIntroPrintWildMonAttacked(void)
static void BattleIntroPrintWildMonAttacked(void)
{
    if (gBattleControllerExecFlags == 0)
    {
-       gBattleMainFunc = BattleIntroPrintPlayerSendsOut;
+       gBattleMainFunc = BattleIntroNuzlockDups;
        PrepareStringBattle(STRINGID_INTROMSG, 0);
    }
}

+static void BattleIntroNuzlockDups(void)
+{
+    if (gBattleControllerExecFlags == 0)
+    {
+        if (gNuzlockeCannotCatch == 2){  // If Pokemon was first in this route and was already caught
+            PrepareStringBattle(STRINGID_NUZLOCKEDUPS, 0);
+        }
+        gBattleMainFunc = BattleIntroPrintPlayerSendsOut;
+    }
+}
+
static void BattleIntroPrintOpponentSendsOut(void)
{
    u32 position;

    if (gBattleControllerExecFlags)
        return;
----------------------------- src/battle_message.c -----------------------------
index 4b977a229..e6d5a871f 100644
@@ -78,9 +78,10 @@ static const u8 sText_AttackerFainted[] = _("{B_ATK_NAME_WITH_PREFIX}\nfainted!\
 static const u8 sText_TargetFainted[] = _("{B_DEF_NAME_WITH_PREFIX}\nfainted!\p");
 static const u8 sText_PlayerGotMoney[] = _("{B_PLAYER_NAME} got ¥{B_BUFF1}\nfor winning!\p");
 static const u8 sText_PlayerWhiteout[] = _("{B_PLAYER_NAME} is out of\nusable POKéMON!\p");
 static const u8 sText_PlayerWhiteout2[] = _("{B_PLAYER_NAME} panicked and lost ¥{B_BUFF1}…\p{B_PLAYER_NAME} whited out!{PAUSE_UNTIL_PRESS}");
 static const u8 sText_PlayerFailedNuzlocke[] = _("{B_PLAYER_NAME} failed the\nNuzlocke challenge.\pThe Nuzlocke setting\nhas been turned off.\p");
+static const u8 sText_PlayerDuplicateMon[] = _("Since this type has already been\ncaught, it will not count towards\pthe Nuzlocke challenge.\p");
 static const u8 sText_PreventsEscape[] = _("{B_SCR_ACTIVE_NAME_WITH_PREFIX} prevents\nescape with {B_SCR_ACTIVE_ABILITY}!\p");
 static const u8 sText_CantEscape2[] = _("Can't escape!\p");
 static const u8 sText_AttackerCantEscape[] = _("{B_ATK_NAME_WITH_PREFIX} can't escape!");
 static const u8 sText_HitXTimes[] = _("Hit {B_BUFF1} time(s)!");
@@ -2381,8 +2382,11 @@ void BufferStringBattle(u16 stringID)
         break;
     case STRINGID_NUZLOCKELOST:
         stringPtr = sText_PlayerFailedNuzlocke;
         break;
+    case STRINGID_NUZLOCKEDUPS:
+        stringPtr = sText_PlayerDuplicateMon;
+        break;
     default: // load a string from the table
         if (stringID >= BATTLESTRINGS_COUNT)
         {
             gDisplayedStringBattle[0] = EOS;

------------------------------ src/battle_setup.c ------------------------------
index 36da06845..19bed4e96 100644
@@ -48,8 +48,10 @@
 #include "constants/trainer_hill.h"
 #include "constants/weather.h"
 #include "overworld.h"
 #include "constants/region_map_sections.h"
+#include "battle_anim.h"
+#include "pokedex.h"
 
 enum {
     TRANSITION_TYPE_NORMAL,
     TRANSITION_TYPE_CAVE,
@@ -2279,18 +2282,22 @@ u8 HasWildPokmnOnThisRouteBeenSeen(u8 currLocation, bool8 setVarForThisEnc){
     varValue = VarGet(pkmnSeenVars[varToCheck]);
     if ((varValue & (1 << bitToCheck)) != 0){
         return 1;
     }
     else if (setVarForThisEnc){
+        u16 species_enemy = GetMonData(&gEnemyParty[gBattlerPartyIndexes[GetBattlerAtPosition(B_POSITION_OPPONENT_LEFT)]], MON_DATA_SPECIES2);
+        if (GetSetPokedexFlag(SpeciesToNationalPokedexNum(species_enemy), FLAG_GET_CAUGHT)){
+            return 2;  // If it's a duplicate Pokemon
+        }
         VarSet(pkmnSeenVars[varToCheck], varValue | (1 << bitToCheck));
     }
     return 0;
 }

Can't Overlevel Leaders

------------------------------ src/battle_setup.c ------------------------------
index 6e6f5886e..c5f9baa57 100644
@@ -1957,8 +1957,44 @@ u16 CountBattledRematchTeams(u16 trainerId)
 
     return i;
 }
 
+static u8 getLevelCap(void){
+    u8 levelCap = 0;
+    u16 nextLeader, i;
+    const struct TrainerMonItemCustomMoves *partyData;
+    if (!FlagGet(FLAG_NUZLOCKE) || FlagGet(FLAG_IS_CHAMPION))
+        return 100;
+    if (!FlagGet(FLAG_BADGE01_GET))
+        nextLeader = TRAINER_ROXANNE_1;
+    else if (!FlagGet(FLAG_BADGE02_GET))
+        nextLeader = TRAINER_BRAWLY_1;
+    else if (!FlagGet(FLAG_BADGE03_GET))
+        nextLeader = TRAINER_WATTSON_1;
+    else if (!FlagGet(FLAG_BADGE04_GET))
+        nextLeader = TRAINER_FLANNERY_1;
+    else if (!FlagGet(FLAG_BADGE05_GET))
+        nextLeader = TRAINER_NORMAN_1;
+    else if (!FlagGet(FLAG_BADGE06_GET))
+        nextLeader = TRAINER_WINONA_1;
+    else if (!FlagGet(FLAG_BADGE07_GET))
+        nextLeader = TRAINER_TATE_AND_LIZA_1;
+    else if (!FlagGet(FLAG_BADGE08_GET))
+        nextLeader = TRAINER_JUAN_1;
+    else if (!FlagGet(FLAG_IS_CHAMPION))
+        nextLeader = TRAINER_WALLACE;
+
+    partyData = gTrainers[nextLeader].party.ItemCustomMoves;
+    for (i = 0; i < gTrainers[nextLeader].partySize; i++){
+        if (partyData[i].lvl > levelCap)
+            levelCap = partyData[i].lvl;
+    }
+    return levelCap;
+}
+
+bool8 levelCappedNuzlocke(u8 level){
+    u8 levelCap = getLevelCap();
+    if (!FlagGet(FLAG_NUZLOCKE) || FlagGet(FLAG_IS_CHAMPION))
+        return FALSE;  //Redundant since getLevelCap would already return 100 for these, but better to be explicit
+    if (level >= levelCap)
+        return TRUE;
+    return FALSE;
+}
+
 u8 HasWildPokmnOnThisRouteBeenSeen(u8 currLocation, bool8 setVarForThisEnc){
     u8 varToCheck, bitToCheck;
     u16 varValue;
     const u16 pkmnSeenVars[] = {

---------------------------- include/battle_setup.h ----------------------------
index 5541cbac6..26e66761e 100644
@@ -66,8 +66,9 @@ bool8 IsTrainerReadyForRematch(void);
 void ShouldTryGetTrainerScript(void);
 u16 CountBattledRematchTeams(u16 trainerId);
 u8 HasWildPokmnOnThisRouteBeenSeen(u8 currLocation, bool8 setVarForThisEnc);
 u8 currLocConvertForNuzlocke(u8 currLocation);
+bool8 levelCappedNuzlocke(u8 level);
 
 
 void DoStandardWildBattle_Debug(void);
 void BattleSetup_StartTrainerBattle_Debug(void);

------------------------ src/battle_controller_player.c ------------------------
index b7f9c9bd7..257b244ac 100644
@@ -2793,9 +2793,10 @@ static void PlayerHandleHealthBarUpdate(void)
 static void PlayerHandleExpUpdate(void)
 {
     u8 monId = gBattleBufferA[gActiveBattler][1];
 
-    if (GetMonData(&gPlayerParty[monId], MON_DATA_LEVEL) >= MAX_LEVEL)
+    if (GetMonData(&gPlayerParty[monId], MON_DATA_LEVEL) >= MAX_LEVEL
+    || levelCappedNuzlocke(GetMonData(&gPlayerParty[monId], MON_DATA_LEVEL)))
     {
         PlayerBufferExecCompleted();
     }
     else

-------------------- src/battle_controller_player_partner.c --------------------
index 99cfa5e1e..bf5aa3b65 100644
@@ -1593,9 +1593,10 @@ static void PlayerPartnerHandleHealthBarUpdate(void)
 static void PlayerPartnerHandleExpUpdate(void)
 {
     u8 monId = gBattleBufferA[gActiveBattler][1];
 
-    if (GetMonData(&gPlayerParty[monId], MON_DATA_LEVEL) >= MAX_LEVEL)
+    if (GetMonData(&gPlayerParty[monId], MON_DATA_LEVEL) >= MAX_LEVEL
+    || levelCappedNuzlocke(GetMonData(&gPlayerParty[monId], MON_DATA_LEVEL)))
     {
         PlayerPartnerBufferExecCompleted();
     }
     else

---------------------------- src/battle_interface.c ----------------------------
index fd1e78dcd..7afa4ad61 100644
@@ -26,8 +26,9 @@
 #include "constants/battle_anim.h"
 #include "constants/rgb.h"
 #include "constants/songs.h"
 #include "constants/items.h"
+#include "battle_setup.h"
 
 struct TestingBar
 {
     s32 maxValue;
@@ -2324,9 +2325,9 @@ static void MoveBattleBarGraphically(u8 battlerId, u8 whichBar)
                     gBattleSpritesDataPtr->battleBars[battlerId].receivedValue,
                     &gBattleSpritesDataPtr->battleBars[battlerId].currValue,
                     array, B_EXPBAR_PIXELS / 8);
         level = GetMonData(&gPlayerParty[gBattlerPartyIndexes[battlerId]], MON_DATA_LEVEL);
-        if (level == MAX_LEVEL)
+        if (level == MAX_LEVEL || levelCappedNuzlocke(level))
         {
             for (i = 0; i < 8; i++)
                 array[i] = 0;
         }

------------------------- src/battle_script_commands.c -------------------------
index e6e0721e1..f73cae167 100644
@@ -3345,9 +3363,11 @@ static void Cmd_getexp(void)
                 *(&gBattleStruct->sentInPokes) >>= 1;
                 gBattleScripting.getexpState = 5;
                 gBattleMoveDamage = 0; // used for exp
             }
-            else if (GetMonData(&gPlayerParty[gBattleStruct->expGetterMonId], MON_DATA_LEVEL) == MAX_LEVEL)
+            else if (GetMonData(&gPlayerParty[gBattleStruct->expGetterMonId], MON_DATA_LEVEL) == MAX_LEVEL
+            || levelCappedNuzlocke(GetMonData(&gPlayerParty[gBattleStruct->expGetterMonId], MON_DATA_LEVEL)))
             {
                 *(&gBattleStruct->sentInPokes) >>= 1;
                 gBattleScripting.getexpState = 5;
                 gBattleMoveDamage = 0; // used for exp
@@ -3428,9 +3448,10 @@ static void Cmd_getexp(void)
     case 3: // Set stats and give exp
         if (gBattleControllerExecFlags == 0)
         {
             gBattleBufferB[gBattleStruct->expGetterBattlerId][0] = 0;
-            if (GetMonData(&gPlayerParty[gBattleStruct->expGetterMonId], MON_DATA_HP) && GetMonData(&gPlayerParty[gBattleStruct->expGetterMonId], MON_DATA_LEVEL) != MAX_LEVEL)
+            if (GetMonData(&gPlayerParty[gBattleStruct->expGetterMonId], MON_DATA_HP) && GetMonData(&gPlayerParty[gBattleStruct->expGetterMonId], MON_DATA_LEVEL) != MAX_LEVEL
+            && !levelCappedNuzlocke(GetMonData(&gPlayerParty[gBattleStruct->expGetterMonId], MON_DATA_LEVEL)))
             {
                 gBattleResources->beforeLvlUp->stats[STAT_HP]    = GetMonData(&gPlayerParty[gBattleStruct->expGetterMonId], MON_DATA_MAX_HP);
                 gBattleResources->beforeLvlUp->stats[STAT_ATK]   = GetMonData(&gPlayerParty[gBattleStruct->expGetterMonId], MON_DATA_ATK);
                 gBattleResources->beforeLvlUp->stats[STAT_DEF]   = GetMonData(&gPlayerParty[gBattleStruct->expGetterMonId], MON_DATA_DEF);

-------------------------------- src/daycare.c --------------------------------
index 7119941b9..b820e207c 100644
@@ -21,8 +21,9 @@
 #include "overworld.h"
 #include "constants/items.h"
 #include "constants/moves.h"
 #include "constants/region_map_sections.h"
+#include "battle_setup.h"
 
 extern const struct Evolution gEvolutionTable[][EVOS_PER_MON];
 
 static void ClearDaycareMonMail(struct DaycareMail *mail);
@@ -253,9 +254,10 @@ static u16 TakeSelectedPokemonFromDaycare(struct DaycareMon *daycareMon)
     GetBoxMonNickname(&daycareMon->mon, gStringVar1);
     species = GetBoxMonData(&daycareMon->mon, MON_DATA_SPECIES);
     BoxMonToMon(&daycareMon->mon, &pokemon);
 
-    if (GetMonData(&pokemon, MON_DATA_LEVEL) != MAX_LEVEL)
+    if (GetMonData(&pokemon, MON_DATA_LEVEL) != MAX_LEVEL 
+    && !levelCappedNuzlocke(GetMonData(&pokemon, MON_DATA_LEVEL)))
     {
         experience = GetMonData(&pokemon, MON_DATA_EXP) + (stepMult * daycareMon->steps);
         SetMonData(&pokemon, MON_DATA_EXP, &experience);
         ApplyDaycareExperience(&pokemon);

------------------------------- src/party_menu.c -------------------------------
index 55a0ea68b..802398bd7 100755
@@ -72,8 +72,9 @@
 #include "constants/party_menu.h"
 #include "constants/rgb.h"
 #include "constants/songs.h"
 #include "naming_screen.h"
+#include "battle_setup.h"
 
 enum {
     MENU_SUMMARY,
     MENU_SWITCH,
@@ -5004,9 +5005,10 @@ void ItemUseCB_RareCandy(u8 taskId, TaskFunc task)
     s16 *arrayPtr = ptr->data;
     u16 *itemPtr = &gSpecialVar_ItemId;
     bool8 cannotUseEffect;
 
-    if (GetMonData(mon, MON_DATA_LEVEL) != MAX_LEVEL)
+    if (GetMonData(mon, MON_DATA_LEVEL) != MAX_LEVEL
+    && !levelCappedNuzlocke(GetMonData(mon, MON_DATA_LEVEL)))
     {
         BufferMonStatsToTaskData(mon, arrayPtr);
         cannotUseEffect = ExecuteTableBasedItemEffect_(gPartyMenu.slotId, *itemPtr, 0);
         BufferMonStatsToTaskData(mon, &ptr->data[NUM_STATS]);

-------------------------------- src/pokemon.c --------------------------------
index e92bba303..efad1c4d4 100644
@@ -4907,9 +4907,10 @@ bool8 PokemonUseItemEffects(struct Pokemon *mon, u16 item, u8 partyIndex, u8 mov
             }
 
             // Rare Candy
             if ((itemEffect[i] & ITEM3_LEVEL_UP)
-             && GetMonData(mon, MON_DATA_LEVEL, NULL) != MAX_LEVEL)
+             && GetMonData(mon, MON_DATA_LEVEL, NULL) != MAX_LEVEL
+             && !levelCappedNuzlocke(GetMonData(mon, MON_DATA_LEVEL, NULL)))
             {
                 dataUnsigned = gExperienceTables[gSpeciesInfo[GetMonData(mon, MON_DATA_SPECIES, NULL)].growthRate][GetMonData(mon, MON_DATA_LEVEL, NULL) + 1];
                 SetMonData(mon, MON_DATA_EXP, &dataUnsigned);
                 CalculateMonStats(mon);

------------------------- src/pokemon_summary_screen.c -------------------------
index 2b522ebea..b94bbc344 100644
@@ -45,8 +45,9 @@
 #include "constants/party_menu.h"
 #include "constants/region_map_sections.h"
 #include "constants/rgb.h"
 #include "constants/songs.h"
+#include "battle_setup.h"
 
 enum {
     PSS_PAGE_INFO,
     PSS_PAGE_SKILLS,
@@ -2609,9 +2610,9 @@ static void DrawExperienceProgressBar(struct Pokemon *unused)
     struct PokeSummary *summary = &sMonSummaryScreen->summary;
     u16 *dst;
     u8 i;
 
-    if (summary->level < MAX_LEVEL)
+    if (summary->level < MAX_LEVEL && !levelCappedNuzlocke(summary->level))
     {
         u32 expBetweenLevels = gExperienceTables[gSpeciesInfo[summary->species].growthRate][summary->level + 1] - gExperienceTables[gSpeciesInfo[summary->species].growthRate][summary->level];
         u32 expSinceLastLevel = summary->exp - gExperienceTables[gSpeciesInfo[summary->species].growthRate][summary->level];
 
@@ -3411,9 +3412,9 @@ static void PrintExpPointsNextLevel(void)
     ConvertIntToDecimalStringN(gStringVar1, sum->exp, STR_CONV_MODE_RIGHT_ALIGN, 7);
     x = GetStringRightAlignXOffset(FONT_NORMAL, gStringVar1, 42) + 2;
     PrintTextOnWindow(windowId, gStringVar1, x, 1, 0, 0);
 
-    if (sum->level < MAX_LEVEL)
+    if (sum->level < MAX_LEVEL && !levelCappedNuzlocke(sum->level))
         expToNextLevel = gExperienceTables[gSpeciesInfo[sum->species].growthRate][sum->level + 1] - sum->exp;
     else
         expToNextLevel = 0;

Have Nurse Joy Tell You the Level Cap

---------------------- data/scripts/pkmn_center_nurse.inc ----------------------
index 546385218..978b9eeec 100644
@@ -51,8 +51,14 @@ EventScript_PkmnCenterNurse_CheckTrainerHillAndUnionRoom::
 
 @ VAR_0x8004 is 1 when player has Gold Card
 EventScript_PkmnCenterNurse_ReturnPkmn::
 	goto_if_eq VAR_0x8004, 1, EventScript_PkmnCenterNurse_ReturnPkmn2
+	goto_if_unset FLAG_NUZLOCKE, EventScript_PkmnCenterNurse_ReturnPkmnDefault
+	special LevelCapToString
+	message gText_WeHopeToSeeYouAgain4
+	return
+EventScript_PkmnCenterNurse_ReturnPkmnDefault::	
 	message gText_WeHopeToSeeYouAgain3
 	return
 
 EventScript_PkmnCenterNurse_ReturnPkmn2::

------------------------------ data/specials.inc ------------------------------
index a863b6e13..671716581 100644
@@ -534,4 +534,5 @@ gSpecials::
 	def_special TryPrepareSecondApproachingTrainer
 	def_special RemoveRecordsWindow
 	def_special CloseDeptStoreElevatorWindow
 	def_special TrySetBattleTowerLinkType
+	def_special LevelCapToString

----------------------- data/text/pkmn_center_nurse.inc -----------------------
index cf2046b76..0d6ad541f 100644
@@ -53,4 +53,8 @@ gText_WeHopeToSeeYouAgain2::
 gText_WeHopeToSeeYouAgain3::
 	.string "Your POKéMON are healed.\n"
 	.string "We hope to see you again!$"
 
+gText_WeHopeToSeeYouAgain4::
+	.string "Your POKéMON are healed, but cannot\n"
+	.string "level over {STR_VAR_1} due to Nuzlocke.$"
+

---------------------------- include/battle_setup.h ----------------------------
index 26e66761e..32a4e3be0 100644
@@ -67,8 +67,9 @@ void ShouldTryGetTrainerScript(void);
 u16 CountBattledRematchTeams(u16 trainerId);
 u8 HasWildPokmnOnThisRouteBeenSeen(u8 currLocation, bool8 setVarForThisEnc);
 u8 currLocConvertForNuzlocke(u8 currLocation);
 bool8 levelCappedNuzlocke(u8 level);
+void LevelCapToString(void);
 
 
 void DoStandardWildBattle_Debug(void);
 void BattleSetup_StartTrainerBattle_Debug(void);

------------------------------ src/battle_setup.c ------------------------------
index 3110bd0b6..2f86c26a5 100644    
@@ -1982,19 +1980,32 @@ bool8 levelCappedNuzlocke(u8 level){
bool8 levelCappedNuzlocke(u8 level){
     u8 levelCap = getLevelCap();
     if (level >= levelCap)
         return TRUE;
     return FALSE;
 }
 
+void LevelCapToString(void){
+    u8 lvl_txt[3];
+    ConvertIntToDecimalStringN(lvl_txt, getLevelCap(), STR_CONV_MODE_LEFT_ALIGN, 3);
+    StringCopy(gStringVar1, lvl_txt);
+}
+
 u8 HasWildPokmnOnThisRouteBeenSeen(u8 currLocation, bool8 setVarForThisEnc){
     u8 varToCheck, bitToCheck;
     u16 varValue;

No Held Items

------------------------------ src/battle_util.c ------------------------------
index 7a0ebda61..7e4c2bc0f 100644
@@ -3243,8 +3243,11 @@ u8 ItemBattleEffects(u8 caseID, u8 battlerId, bool8 moveTurn)
     u8 battlerHoldEffect, atkHoldEffect, defHoldEffect;
     u8 battlerHoldEffectParam, atkHoldEffectParam, defHoldEffectParam;
     u16 atkItem, defItem;
 
+    if (GetBattlerSide(battlerId) == B_SIDE_PLAYER && FlagGet(FLAG_NUZLOCKE))
+        return ITEM_NO_EFFECT;
+
     gLastUsedItem = gBattleMons[battlerId].item;
     if (gLastUsedItem == ITEM_ENIGMA_BERRY)
     {
         battlerHoldEffect = gEnigmaBerries[battlerId].holdEffect;

No Battle Items

------------------------------- src/item_menu.c -------------------------------
index da0369c12..5d4cc7cbc 100755
@@ -1532,9 +1532,10 @@ static void OpenContextMenu(u8 taskId)
     switch (gBagPosition.location)
     {
     case ITEMMENULOCATION_BATTLE:
     case ITEMMENULOCATION_WALLY:
-        if (ItemId_GetBattleUsage(gSpecialVar_ItemId))
+        if (ItemId_GetBattleUsage(gSpecialVar_ItemId) && (!FlagGet(FLAG_NUZLOCKE) 
+        || gBagPosition.pocket == BALLS_POCKET || gBagPosition.location != ITEMMENULOCATION_BATTLE))
         {
             gBagMenu->contextMenuItemsPtr = sContextMenuItems_BattleUse;
             gBagMenu->contextMenuNumItems = ARRAY_COUNT(sContextMenuItems_BattleUse);
         }
@@ -1650,9 +1651,13 @@ static void OpenContextMenu(u8 taskId)
     }
     else
     {
         CopyItemName(gSpecialVar_ItemId, gStringVar1);
-        StringExpandPlaceholders(gStringVar4, gText_Var1IsSelected);
+        if (ItemId_GetBattleUsage(gSpecialVar_ItemId) && FlagGet(FLAG_NUZLOCKE) 
+        && gBagPosition.pocket != BALLS_POCKET && gBagPosition.location == ITEMMENULOCATION_BATTLE)
+            StringExpandPlaceholders(gStringVar4, gText_Var1NuzlockePrevents);
+        else
+            StringExpandPlaceholders(gStringVar4, gText_Var1IsSelected);
         FillWindowPixelBuffer(WIN_DESCRIPTION, PIXEL_FILL(0));
         BagMenu_Print(WIN_DESCRIPTION, FONT_NORMAL, gStringVar4, 3, 1, 0, 0, 0, COLORID_NORMAL);
     }
     if (gBagMenu->contextMenuNumItems == 1)

------------------------------ include/strings.h ------------------------------
index 52097f4fe..59263096a 100644
@@ -434,8 +434,9 @@ extern const u8 gText_xVar1[];
 extern const u8 gText_ReturnToVar1[];
 extern const u8 gText_SelectorArrow2[];
 extern const u8 gText_MoveVar1Where[];
 extern const u8 gText_Var1IsSelected[];
+extern const u8 gText_Var1NuzlockePrevents[];
 extern const u8 gText_TossHowManyVar1s[];
 extern const u8 gText_ConfirmTossItems[];
 extern const u8 gText_ThrewAwayVar2Var1s[];
 extern const u8 gText_CantWriteMail[];

-------------------------------- src/strings.c --------------------------------
index 41441d6cd..399052cfb 100644
@@ -220,8 +220,9 @@ const u8 gText_xVar1[] = _("×{STR_VAR_1}");
 const u8 gText_Berry2[] = _(" BERRY"); // Unused
 const u8 gText_Coins[] = _("{STR_VAR_1} COINS");
 const u8 gText_CloseBag[] = _("CLOSE BAG");
 const u8 gText_Var1IsSelected[] = _("{STR_VAR_1} is\nselected.");
+const u8 gText_Var1NuzlockePrevents[] = _("Nuzlocke challenge\nprevents using\n{STR_VAR_1}.");
 const u8 gText_CantWriteMail[] = _("You can't write\nMAIL here.");
 const u8 gText_NoPokemon[] = _("There is no\nPOKéMON.");
 const u8 gText_MoveVar1Where[] = _("Move the\n{STR_VAR_1}\nwhere?");
 const u8 gText_Var1CantBeHeld[] = _("The {STR_VAR_1} can't be held.");

Force Set Mode

------------------------------ src/battle_main.c ------------------------------
index fc00c2ee5..838c6ecf9 100644
@@ -3184,9 +3184,9 @@ static void BattleStartClearSetData(void)
     }
     else if (!(gBattleTypeFlags & (BATTLE_TYPE_LINK | BATTLE_TYPE_RECORDED_LINK)) && GetBattleSceneInRecordedBattle())
         gHitMarker |= HITMARKER_NO_ANIMATIONS;
 
-    gBattleScripting.battleStyle = gSaveBlock2Ptr->optionsBattleStyle;
+    gBattleScripting.battleStyle = gSaveBlock2Ptr->optionsBattleStyle || FlagGet(FLAG_NUZLOCKE));
 
     gMultiHitCounter = 0;
     gBattleOutcome = 0;
     gBattleControllerExecFlags = 0;

------------------------------ src/option_menu.c ------------------------------
index d69320b14..09736d410 100644
@@ -504,8 +504,14 @@ static void BattleStyle_DrawChoices(u8 selection)
     u8 styles[2];
 
     styles[0] = 0;
     styles[1] = 0;
+
+    if (FlagGet(FLAG_NUZLOCKE)){
+        styles[1] = 1;
+         DrawOptionMenuChoice(gText_BattleStyleSet, 104, YPOS_BATTLESTYLE, styles[1]);
+        return;
+    }
     styles[selection] = 1;
 
     DrawOptionMenuChoice(gText_BattleStyleShift, 104, YPOS_BATTLESTYLE, styles[0]);
     DrawOptionMenuChoice(gText_BattleStyleSet, GetStringRightAlignXOffset(FONT_SHORT, gText_BattleStyleSet, 198), YPOS_BATTLESTYLE, styles[1]);

Indicate If Pokemon Is Catchable

Credit for the icon and idea come from Deokishisu with their FRLG-Plus hack.

Copy this image of a one into /graphics/battle/interfaces/ and name it nuzlocke_indicator.png: nuzlocke_indicator

-------------------------------- src/graphics.c --------------------------------
index 8a6bbc88a..e78770237 100644
@@ -353,8 +353,10 @@ const u32 gUnusedTilemap_BasicFrame[] = INCBIN_U32("graphics/unused/basic_frame.
 const u16 gBattleInterface_BallStatusBarPal[] = INCBIN_U16("graphics/battle_interface/ball_status_bar.gbapal");
 
 const u16 gBattleInterface_BallDisplayPal[] = INCBIN_U16("graphics/battle_interface/ball_display.gbapal");
 
+const u8 gNuzlockeFirstEncounterIndicator[] = INCBIN_U8("graphics/battle_interface/nuzlocke_indicator.4bpp");
+
 const u8 gHealthboxElementsGfxTable[] = INCBIN_U8("graphics/battle_interface/hpbar.4bpp",
                                                   "graphics/battle_interface/expbar.4bpp",
                                                   "graphics/battle_interface/status_psn.4bpp",
                                                   "graphics/battle_interface/status_par.4bpp",
------------------------------ include/graphics.h ------------------------------
index ff1048d3c..67f6379dd 100644
@@ -4194,8 +4194,9 @@ extern const u32 gBattleInterface_BallStatusBarGfx[];
 extern const u8 gBattleInterface_BallDisplayGfx[];
 extern const u16 gBattleInterface_BallStatusBarPal[];
 extern const u16 gBattleInterface_BallDisplayPal[];
 extern const u8 gHealthboxElementsGfxTable[][32];
+extern const u8 gNuzlockeFirstEncounterIndicator[];
 
 extern const u16 gNamingScreenMenu_Pal[6][16];
 extern const u32 gNamingScreenMenu_Gfx[];
 extern const u32 gNamingScreenBackground_Tilemap[];
---------------------------- src/battle_interface.c ----------------------------
index 7afa4ad61..dd5473822 100644
@@ -27,8 +27,9 @@
 #include "constants/rgb.h"
 #include "constants/songs.h"
 #include "constants/items.h"
 #include "battle_setup.h"
+#include "event_data.h"
 
 struct TestingBar
 {
     s32 maxValue;
@@ -158,8 +159,9 @@ enum
     HEALTHBOX_GFX_114,
     HEALTHBOX_GFX_115,
     HEALTHBOX_GFX_FRAME_END,
     HEALTHBOX_GFX_FRAME_END_BAR,
+    HEALTHBOX_GFX_118, //Nuzlocke indicator
 };
 
 static const u8 *GetHealthboxElementGfxPtr(u8);
 static u8 *AddTextPrinterAndCreateWindowOnHealthbox(const u8 *, u32, u32, u32, u32 *);
@@ -1991,15 +1993,20 @@ static void TryAddPokeballIconToHealthbox(u8 healthboxSpriteId, bool8 noStatus)
 
     battlerId = gSprites[healthboxSpriteId].hMain_Battler;
     if (GetBattlerSide(battlerId) == B_SIDE_PLAYER)
         return;
-    if (!GetSetPokedexFlag(SpeciesToNationalPokedexNum(GetMonData(&gEnemyParty[gBattlerPartyIndexes[battlerId]], MON_DATA_SPECIES)), FLAG_GET_CAUGHT))
+    if (!GetSetPokedexFlag(SpeciesToNationalPokedexNum(GetMonData(&gEnemyParty[gBattlerPartyIndexes[battlerId]], MON_DATA_SPECIES)), FLAG_GET_CAUGHT) 
+    && (gNuzlockeCannotCatch || !FlagGet(FLAG_NUZLOCKE) || !FlagGet(FLAG_SYS_POKEDEX_GET)))
         return;
 
     healthBarSpriteId = gSprites[healthboxSpriteId].hMain_HealthBarSpriteId;
 
-    if (noStatus)
-        CpuCopy32(GetHealthboxElementGfxPtr(HEALTHBOX_GFX_STATUS_BALL_CAUGHT), (void *)(OBJ_VRAM0 + (gSprites[healthBarSpriteId].oam.tileNum + 8) * TILE_SIZE_4BPP), 32);
+    if (noStatus){
+        if(GetSetPokedexFlag(SpeciesToNationalPokedexNum(GetMonData(&gEnemyParty[gBattlerPartyIndexes[battlerId]], MON_DATA_SPECIES)), FLAG_GET_CAUGHT))
+            CpuCopy32(GetHealthboxElementGfxPtr(HEALTHBOX_GFX_STATUS_BALL_CAUGHT), (void *)(OBJ_VRAM0 + (gSprites[healthBarSpriteId].oam.tileNum + 8) * TILE_SIZE_4BPP), 32);
+        else if(!gNuzlockeCannotCatch && FlagGet(FLAG_NUZLOCKE) && FlagGet(FLAG_SYS_POKEDEX_GET))
+            CpuCopy32(gNuzlockeFirstEncounterIndicator, (void*)(OBJ_VRAM0 + (gSprites[healthBarSpriteId].oam.tileNum + 8) * TILE_SIZE_4BPP), 32);
+    }
     else
         CpuFill32(0, (void *)(OBJ_VRAM0 + (gSprites[healthBarSpriteId].oam.tileNum + 8) * TILE_SIZE_4BPP), 32);
 }
Clone this wiki locally