3 Make Pokemon Not Heal When Going into PC
voloved edited this page 2023-11-04 10:44:29 -04:00

By devolov

Goal: Make it so depositing a Pokemon into the PC doesn't heal them.
This hack was made for me because I allow accessing the PC from the PokeNav, but this can also have a purpose for difficulty hacks where healing is not free.

pc

------------------------------ include/pokemon.h ------------------------------
index 377e03f30..28c8d0207 100644
@@ -11,8 +11,9 @@ enum {
     MON_DATA_LANGUAGE,
     MON_DATA_SANITY_IS_BAD_EGG,
     MON_DATA_SANITY_HAS_SPECIES,
     MON_DATA_SANITY_IS_EGG,
+    MON_DATA_IN_PC,
     MON_DATA_OT_NAME,
     MON_DATA_MARKINGS,
     MON_DATA_CHECKSUM,
     MON_DATA_ENCRYPT_SEPARATOR,
@@ -94,9 +95,8 @@ enum {
     MON_DATA_SPEED2,
     MON_DATA_SPATK2,
     MON_DATA_SPDEF2,
     MON_DATA_DEAD,
     MON_DATA_NATURE,
     MON_DATA_HIDDEN_NATURE,
+    MON_DATA_BOX_HP,
+    MON_DATA_BOX_AILMENT,
 };
 
 struct PokemonSubstruct0
 {
@@ -106,10 +109,10 @@ struct PokemonSubstruct0
     u32 experience;
     u8 ppBonuses;
     u8 friendship;
     u8 hiddenNature:5;  // This is from the Nature Mints hack (https://www.pokecommunity.com/showpost.php?p=10245635&postcount=191)
-    u8 free_sub0:3;
-    u8 filler;
+    u8 box_ailment:3; //Set to zero unless pokemon is in the box
+    u8 box_hp; //Set to zero unless pokemon is in the box, then it's set to the HP is maxHP is <256 o/w currHP/maxHP * 255
 };
 
 struct PokemonSubstruct1
 {
@@ -200,13 +203,14 @@ struct BoxPokemon
     u8 isBadEgg:1;
     u8 hasSpecies:1;
     u8 isEgg:1;
     u8 blockBoxRS:1; // Unused, but Pokémon Box Ruby & Sapphire will refuse to deposit a Pokémon with this flag set
-    u8 unused:4;
+    u8 unused:3;
+    u8 inPC:1;  //Used to retain save compatibility for Pokemon that existed in the PC before adding the "Make Pokemon Not Heal When Going into PC" hack
     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

----------------------- include/pokemon_storage_system.h -----------------------
index bdf2e8ef1..a42fd3ff7 100644
@@ -73,6 +73,9 @@ void SetWaldaPhrase(const u8 *src);
 bool32 IsWaldaPhraseEmpty(void);
 void CB2_ReturnToPokeStorage(void);
 
 void EnterPokeStorage(u8 boxOption);
+u16 GetHPFromBoxHP(struct Pokemon *mon);
+u32 GetStatusFromBoxStatus(struct Pokemon *mon);
 
 #endif // GUARD_POKEMON_STORAGE_SYSTEM_H

-------------------------------- src/pokemon.c --------------------------------
index 602960a2f..76a1486da 100644
@@ -3844,8 +3844,11 @@ u32 GetBoxMonData(struct BoxPokemon *boxMon, s32 field, u8 *data)
         break;
     case MON_DATA_SANITY_IS_EGG:
         retVal = boxMon->isEgg;
         break;
+    case MON_DATA_IN_PC:
+        retVal = boxMon->inPC;
+        break;
     case MON_DATA_OT_NAME:
     {
         retVal = 0;
 
@@ -4111,8 +4114,14 @@ u32 GetBoxMonData(struct BoxPokemon *boxMon, s32 field, u8 *data)
         break; // Tail end of case MON_DATA_NATURE
     case MON_DATA_HIDDEN_NATURE:
         retVal = substruct0->hiddenNature;
         break;
+    case MON_DATA_BOX_HP:
+        retVal = substruct0->box_hp;
+        break;
+    case MON_DATA_BOX_AILMENT:
+        retVal = substruct0->box_ailment;
+        break;
     default:
         break;
     }
 
@@ -4231,8 +4240,11 @@ void SetBoxMonData(struct BoxPokemon *boxMon, s32 field, const void *dataArg)
         break;
     case MON_DATA_SANITY_IS_EGG:
         SET8(boxMon->isEgg);
         break;
+    case MON_DATA_IN_PC:
+        SET8(boxMon->inPC);
+        break;
     case MON_DATA_OT_NAME:
     {
         s32 i;
         for (i = 0; i < PLAYER_NAME_LENGTH; i++)
@@ -4514,8 +4526,14 @@ void SetBoxMonData(struct BoxPokemon *boxMon, s32 field, const void *dataArg)
     } // Tail end of case MON_DATA_NATURE
     case MON_DATA_HIDDEN_NATURE:
         SET8(substruct0->hiddenNature);
         break;
+    case MON_DATA_BOX_HP:
+        SET8(substruct0->box_hp);
+        break;
+    case MON_DATA_BOX_AILMENT:
+        SET8(substruct0->box_ailment);
+        break;
     default:
         break;
     }
 
------------------------- src/pokemon_storage_system.c -------------------------
index 77cf2f9ae..73c32e5d9 100644
@@ -21,8 +21,9 @@
 #include "mon_markings.h"
 #include "naming_screen.h"
 #include "overworld.h"
 #include "palette.h"
+#include "party_menu.h"
 #include "pc_screen_effect.h"
 #include "pokemon.h"
 #include "pokemon_icon.h"
 #include "pokemon_summary_screen.h"
@@ -35,10 +36,12 @@
 #include "text_window.h"
 #include "trig.h"
 #include "walda_phrase.h"
 #include "window.h"
+#include "constants/battle.h"
 #include "constants/items.h"
 #include "constants/moves.h"
+#include "constants/party_menu.h"
 #include "constants/rgb.h"
 #include "constants/songs.h"
 
 /*
@@ -6366,17 +6369,107 @@ static void SetMovingMonData(u8 boxId, u8 position)
     sMovingMonOrigBoxId = boxId;
     sMovingMonOrigBoxPos = position;
 }
 
+static u8 GetBoxHPFromHP(struct Pokemon *mon)
+{
+    u8 hp_box;
+    u16 hp_curr = GetMonData(mon, MON_DATA_HP);
+    u16 hp_max = GetMonData(mon, MON_DATA_MAX_HP);
+    if (hp_max <= 0xFF)  // If the HP is small enough to fully fit into a u8, put in the precise value
+        hp_box = hp_curr;
+    else  // Otherwise, round
+        hp_box = SAFE_DIV(0xFF * hp_curr, hp_max);
+    if(hp_curr != 0 && hp_box == 0)  // to stop accidental fainting
+        hp_box = 1;
+    return hp_box;
+}
+
+u16 GetHPFromBoxHP(struct Pokemon *mon)
+{
+    u16 hp_curr;
+    u8 hp_box = GetMonData(mon, MON_DATA_BOX_HP);
+    u16 hp_max = GetMonData(mon, MON_DATA_MAX_HP);
+    if (hp_max <= 0xFF)
+        hp_curr = hp_box;
+    else
+        hp_curr = SAFE_DIV(hp_max * hp_box, 0xFF);
+    return hp_curr; 
+}
+
+static u8 GetBoxStatusFromStatus(struct Pokemon *mon)
+{
+    u32 status = GetMonData(mon, MON_DATA_STATUS);
+    u8 ailment = GetAilmentFromStatus(status); 
+    if (ailment > 7)  //Chops off anything above what 3 bits can hold
+        ailment = 0;
+    return ailment;
+}
+
+u32 GetStatusFromBoxStatus(struct Pokemon *mon)
+{
+    u8 boxStatus = GetMonData(mon, MON_DATA_BOX_AILMENT);
+    u32 status;
+    switch (boxStatus)
+    {
+    case AILMENT_PSN:
+        status = STATUS1_POISON;
+        break;
+    case AILMENT_PRZ:
+        status = STATUS1_PARALYSIS;
+        break;
+    case AILMENT_SLP:
+        status = STATUS1_SLEEP_TURN(3);
+        break;
+    case AILMENT_FRZ:
+        status = STATUS1_FREEZE;
+        break;
+    case AILMENT_BRN:
+        status = STATUS1_BURN;
+        break;
+    default:
+        status = STATUS1_NONE;
+        break;
+    }
+    return status;
+}
+
 static void SetPlacedMonData(u8 boxId, u8 position)
 {
+    u32 hp;
+    u32 status;
+    u8 value;
     if (boxId == TOTAL_BOXES_COUNT)
     {
+        if(GetMonData(&sStorage->movingMon, MON_DATA_IN_PC))
+        {
+            hp = GetHPFromBoxHP(&sStorage->movingMon);
+            status = GetStatusFromBoxStatus(&sStorage->movingMon);
+            SetMonData(&sStorage->movingMon, MON_DATA_HP, &hp);
+            SetMonData(&sStorage->movingMon, MON_DATA_STATUS, &status);
+        }
+        else
+            MonRestorePP(&sStorage->movingMon);
+        value = 0;
+        SetBoxMonData(&sStorage->movingMon.box, MON_DATA_IN_PC, &value);
+        SetBoxMonData(&sStorage->movingMon.box, MON_DATA_BOX_HP, &value);
+        SetBoxMonData(&sStorage->movingMon.box, MON_DATA_BOX_AILMENT, &value);
         gPlayerParty[position] = sStorage->movingMon;
     }
     else
     {
-        BoxMonRestorePP(&sStorage->movingMon.box);
+        value = TRUE;
+        hp = GetBoxHPFromHP(&sStorage->movingMon);
+        status = GetBoxStatusFromStatus(&sStorage->movingMon);
+        SetBoxMonData(&sStorage->movingMon.box, MON_DATA_IN_PC, &value);
+        SetBoxMonData(&sStorage->movingMon.box, MON_DATA_BOX_HP, &hp);
+        SetBoxMonData(&sStorage->movingMon.box, MON_DATA_BOX_AILMENT, &status);
         SetBoxMonAt(boxId, position, &sStorage->movingMon.box);
     }
 }
 
@@ -6703,12 +6796,24 @@ static void LoadSavedMovingMon(void)
 }
 
 static void InitSummaryScreenData(void)
 {
+    u16 hp;
+    u32 status;
     if (sIsMonBeingMoved)
     {
         SaveMovingMon();
         sStorage->summaryMon.mon = &sSavedMovingMon;
+        if (GetMonData(&sStorage->movingMon, MON_DATA_IN_PC) && sMovingMonOrigBoxId != TOTAL_BOXES_COUNT)
+        // If it did not come from the party
+        {
+            hp = GetHPFromBoxHP(&sStorage->movingMon);
+            status = GetStatusFromBoxStatus(&sStorage->movingMon);
+            SetMonData(sStorage->summaryMon.mon, MON_DATA_HP, &hp);
+            SetMonData(sStorage->summaryMon.mon, MON_DATA_STATUS, &status);
+        }
+        else
+            MonRestorePP(sStorage->summaryMon.mon);
         sStorage->summaryStartPos = 0;
         sStorage->summaryMaxPos = 0;
         sStorage->summaryScreenMode = SUMMARY_MODE_NORMAL;
     }

------------------------- src/pokemon_summary_screen.c -------------------------
index 8b6051561..63a2964fd 100644
@@ -1404,8 +1404,17 @@ static void CopyMonToSummaryStruct(struct Pokemon *mon)
     else
     {
         struct BoxPokemon *boxMon = sMonSummaryScreen->monList.boxMons;
         BoxMonToMon(&boxMon[sMonSummaryScreen->curMonIndex], mon);
+        if (GetMonData(mon, MON_DATA_IN_PC))  // If the Pokemon's summary is coming from the PC as a BoxMon
+        {
+            u16 hp = GetHPFromBoxHP(mon);
+            u32 status = GetStatusFromBoxStatus(mon);
+            SetMonData(mon, MON_DATA_HP, &hp);
+            SetMonData(mon, MON_DATA_STATUS, &status);
+        }
+        else
+            MonRestorePP(mon);
     }
 }
 
 static bool8 ExtractMonDataToSummaryStruct(struct Pokemon *mon)