Skip to content

Add A PokeVial Item

voloved edited this page Mar 17, 2023 · 3 revisions

By devolov. Item completely inspired by one in the Radical Red and soupercell gets the credit for the idea.

Goal: Make an item that can heal your party up to 6 times. After 6 times, a Pokemon Center must be used to replenish.

pokeemerald_modern-0

Note: In my hack, the goal is to allow a vanilla save file to use play the game, so I swapped Dream Mail for this item. You'd probably want to keep Dream Mail in your hack, so just use this as a basis on how the logic was done.

Note 2: I have not yet got to the PokeVial in my playthrough of Radical Red. I just watched a YouTube video before playing it and thought "That's a nice feature."

Add an icon to graphics/items/icons/dream_mail.png: dream_mail
If you're creating a new item, add it with a different name copy the dream mail pallette in graphics/items/icon_palettes too.

Add the PokeVial as an item.

------------------------------- src/data/items.h -------------------------------
index 7cd180a2b..d8305cbe3 100644
@@ -1577,18 +1577,21 @@ const struct Item gItems[] =
         .fieldUseFunc = ItemUseOutOfBattle_Mail,
         .secondaryId = ITEM_TO_MAIL(ITEM_TROPIC_MAIL),
     },
 
     [ITEM_DREAM_MAIL] =
     {
-        .name = _("DREAM MAIL"),
+        .name = _("PokéVial"),
         .itemId = ITEM_DREAM_MAIL,
-        .price = 50,
+        .price = 0,
         .description = sDreamMailDesc,
-        .pocket = POCKET_ITEMS,
-        .type = ITEM_USE_MAIL,
+        .importance = 1,
+        .holdEffectParam = 6,  //Set to max usages before Pokemon Center
+        .registrability = TRUE,
+        .pocket = POCKET_KEY_ITEMS,
+        .type = ITEM_USE_FIELD,
         .fieldUseFunc = ItemUseOutOfBattle_Mail,
-        .secondaryId = ITEM_TO_MAIL(ITEM_DREAM_MAIL),
+        .fieldUseFunc = ItemUseOutOfBattle_PokeVial,
     },
 
     [ITEM_FAB_MAIL] =
     {

Give it a description.

---------------------- src/data/text/item_descriptions.h ----------------------
index 26fc8d086..62404b54a 100644
@@ -480,12 +480,12 @@ static const u8 sTropicMailDesc[] = _(
     "A BELLOSSOM-print\n"
     "MAIL to be held by\n"
     "a POKéMON.");
 
static const u8 sDreamMailDesc[] = _(
-    "MAIL featuring a\n"
-    "sketch of the\n"
-    "holding POKéMON.");
+    "Use to heal party.\n"
+    "Replenishes at\n"
+    "POKéMON Centers.");
 
 static const u8 sFabMailDesc[] = _(
     "A gorgeous-print\n"
     "MAIL to be held\n"

Add a variable tyhat tracks its usage.

--------------------------- include/constants/vars.h ---------------------------
index f26ee2186..e95e14b18 100644
@@ -269,9 +269,9 @@
 #define VAR_WILD_PKMN_ROUTE_SEEN_3                       0x40FA
 #define VAR_WILD_PKMN_ROUTE_SEEN_4                       0x40FB
 #define VAR_DIFFICULTY                                   0x40FC
 #define VAR_EXP_MULT                                     0x40FD
-#define VAR_UNUSED_0x40FE                                0x40FE // Unused Var
+#define VAR_POKEVIAL_USAGES                              0x40FE
 #define VAR_UNUSED_0x40FF                                0x40FF // Unused Var
 
 #define VARS_END                                         0x40FF
 #define VARS_COUNT                                       (VARS_END - VARS_START + 1)

Add functionality for when it's used.

 -------------------------------- src/item_use.c --------------------------------
index 5a07392b8..a6c0fdf8a 100755
@@ -43,8 +43,9 @@
 #include "constants/items.h"
 #include "constants/songs.h"
 #include "battle_setup.h"
 #include "region_map.h"
+#include "script_pokemon_util.h"
 
 static void SetUpItemUseCallback(u8);
 static void FieldCB_UseItemOnField(void);
 static void Task_CallItemUseOnFieldCallback(u8);
@@ -1165,8 +1166,32 @@ void ItemUseOutOfBattle_CannotUse(u8 taskId)
    DisplayDadsAdviceCannotUseItemMessage(taskId, gTasks[taskId].tUsingRegisteredKeyItem);
}
 
+void ItemUseOutOfBattle_PokeVial(u8 taskId)
+{
+    u16 vialUsages = VarGet(VAR_POKEVIAL_USAGES);
+    u16 vialUsagesMax = ItemId_GetHoldEffectParam(ITEM_POKEVIAL);
+    if (vialUsages >= vialUsagesMax){
+        if (gTasks[taskId].tUsingRegisteredKeyItem) // to account for pressing select in the overworld
+            DisplayItemMessageOnField(taskId, gText_PokeVial_Failure, Task_CloseCantUseKeyItemMessage);
+        else
+            DisplayItemMessage(taskId, 1, gText_PokeVial_Failure, CloseItemMessage);
+
+    }
+    else{
+        PlaySE(SE_RG_POKE_JUMP_SUCCESS);
+        HealPlayerParty();
+        vialUsages++;
+        VarSet(VAR_POKEVIAL_USAGES, vialUsages);
+        ConvertIntToDecimalStringN(gStringVar1, vialUsagesMax - vialUsages, STR_CONV_MODE_LEFT_ALIGN, 2);
+        if (gTasks[taskId].tUsingRegisteredKeyItem) // to account for pressing select in the overworld
+            DisplayItemMessageOnField(taskId, gText_PokeVial_Success, Task_CloseCantUseKeyItemMessage);
+        else
+            DisplayItemMessage(taskId, 1, gText_PokeVial_Success, CloseItemMessage);
+    }
+}
+

Replenish it at Pokemon Centers

---------------------- data/scripts/pkmn_center_nurse.inc ----------------------
index 233e0a736..546385218 100644
@@ -15,8 +15,9 @@ EventScript_PkmnCenterNurse_Goodbye::
 EventScript_PkmnCenterNurse_HealPkmn::
 	incrementgamestat GAME_STAT_USED_POKECENTER
 	playfanfare MUS_HEAL
 	special HealPlayerParty
+	setvar VAR_POKEVIAL_USAGES, 0
 	goto_if_unset FLAG_POKERUS_EXPLAINED, EventScript_PkmnCenterNurse_CheckPokerus
 	goto EventScript_PkmnCenterNurse_CheckTrainerHillAndUnionRoom
 	end

Add strings for when it's used.

-------------------------------- src/strings.c --------------------------------
index ecf0fcd80..80e805451 100644
@@ -237,8 +237,10 @@ const u8 gText_ThrewAwayVar2Var1s[] = _("Threw away {STR_VAR_2}\n{STR_VAR_1}(s).
 const u8 gText_ConfirmTossItems[] = _("Is it okay to\nthrow away {STR_VAR_2}\n{STR_VAR_1}(s)?");
 const u8 gText_DadsAdvice[] = _("DAD's advice…\n{PLAYER}, there's a time and place for\leverything!{PAUSE_UNTIL_PRESS}");
 const u8 gText_ExpShareTurnOn[] = _("Turned on the Exp. Share.\pParty will now gain a portion\nof the Experience Points.{PAUSE_UNTIL_PRESS}");
 const u8 gText_ExpShareTurnOff[] = _("Turned off the Exp. Share.\pParty will no longer gain a portion\nof any Experience Points.{PAUSE_UNTIL_PRESS}");
+const u8 gText_PokeVial_Success[] = _("PokéVial successfully healed party.\p{STR_VAR_1} more uses before needing\nto heal at a POKéMON Center.{PAUSE_UNTIL_PRESS}");
+const u8 gText_PokeVial_Failure[] = _("PokéVial is drained.\nHeal at a POKéMON Center to replenish.{PAUSE_UNTIL_PRESS}");
 const u8 gText_CleanseTagTurnOn[] = _("Turned on Cleanse Tag.\nAll wild encounters will be avoided.{PAUSE_UNTIL_PRESS}");
 const u8 gText_CleanseTagTurnOff[] = _("Turned off Cleanse Tag.{PAUSE_UNTIL_PRESS}");
 const u8 gText_CantDismountBike[] = _("You can't dismount your BIKE here.{PAUSE_UNTIL_PRESS}");
 const u8 gText_ItemFinderNearby[] = _("Huh?\nThe ITEMFINDER's responding!\pThere's an item buried around here!{PAUSE_UNTIL_PRESS}");

Declare those strings.

 ------------------------------ include/strings.h ------------------------------
index a0a515fd8..34eb01266 100644
@@ -936,8 +936,10 @@ extern const u8 gText_Gabby[];
 extern const u8 gText_Anna[];
 
 extern const u8 gText_ExpShareTurnOn[];
 extern const u8 gText_ExpShareTurnOff[];
+extern const u8 gText_PokeVial_Success[];
+extern const u8 gText_PokeVial_Failure[];
 extern const u8 gText_CleanseTagTurnOn[];
 extern const u8 gText_CleanseTagTurnOff[];
 extern const u8 gText_DadsAdvice[];
 extern const u8 gText_CantDismountBike[];

If you're swapping Dream Mail for this item, remove checks for the mail.

---------------------------------- src/mail.c ----------------------------------
index 63553f172..b9d50d0c4 100644
@@ -204,16 +204,8 @@ static const struct MailGraphics sMailGraphics[] = {
         .unused = 0x220,
         .textColor = RGB(10, 10, 10),
         .textShadow = RGB(25, 25, 25),
     },
-    [ITEM_TO_MAIL(ITEM_DREAM_MAIL)] = {
-        .palette = gMailPalette_Dream,
-        .tiles = gMailTiles_Dream,
-        .tileMap = gMailTilemap_Dream,
-        .unused = 0x340,
-        .textColor = RGB(10, 10, 10),
-        .textShadow = RGB(25, 25, 25),
-    },
     [ITEM_TO_MAIL(ITEM_FAB_MAIL)] = {
         .palette = gMailPalette_Fab,
         .tiles = gMailTiles_Fab,
         .tileMap = gMailTilemap_Fab,
@@ -309,16 +301,8 @@ static const struct MailLayout sMailLayouts_Wide[] = {
         .wordsYPos = 2,
         .wordsXPos = 4,
         .lines = sLineLayouts_Wide,
     },
-    [ITEM_TO_MAIL(ITEM_DREAM_MAIL)] = {
-        .numLines = ARRAY_COUNT(sLineLayouts_Wide),
-        .signatureYPos = 0,
-        .signatureWidth = 0,
-        .wordsYPos = 2,
-        .wordsXPos = 4,
-        .lines = sLineLayouts_Wide,
-    },
     [ITEM_TO_MAIL(ITEM_FAB_MAIL)] = {
         .numLines = ARRAY_COUNT(sLineLayouts_Wide),
         .signatureYPos = 8,
         .signatureWidth = 0,
@@ -416,16 +400,8 @@ static const struct MailLayout sMailLayouts_Tall[] = {
         .wordsYPos = 9,
         .wordsXPos = 30,
         .lines = sLineLayouts_Tall,
     },
-    [ITEM_TO_MAIL(ITEM_DREAM_MAIL)] = {
-        .numLines = ARRAY_COUNT(sLineLayouts_Tall),
-        .signatureYPos = 9,
-        .signatureWidth = 96,
-        .wordsYPos = 9,
-        .wordsXPos = 30,
-        .lines = sLineLayouts_Tall,
-    },
     [ITEM_TO_MAIL(ITEM_FAB_MAIL)] = {
         .numLines = ARRAY_COUNT(sLineLayouts_Tall),
         .signatureYPos = 17,
         .signatureWidth = 104,
@@ -483,11 +459,8 @@ void ReadMail(struct Mail *mail, void (*exitCallback)(void), bool8 hasText)
             break;
         case ITEM_TO_MAIL(ITEM_BEAD_MAIL):
             sMailRead->iconType = ICON_TYPE_BEAD;
             break;
-        case ITEM_TO_MAIL(ITEM_DREAM_MAIL):
-            sMailRead->iconType = ICON_TYPE_DREAM;
-            break;
         }
     }
     else
     {
------------------------------- src/mail_data.c -------------------------------
index 99cf1f9ed..a8e8ee0ed 100644
@@ -194,9 +194,9 @@ bool8 ItemIsMail(u16 itemId)
     case ITEM_WAVE_MAIL:
     case ITEM_BEAD_MAIL:
     case ITEM_SHADOW_MAIL:
     case ITEM_TROPIC_MAIL:
-    case ITEM_DREAM_MAIL:
     case ITEM_FAB_MAIL:
     case ITEM_RETRO_MAIL:
         return TRUE;
     default:
Clone this wiki locally