4 Make the Bag Able to Hold 120 Items Instead of 30
Scyrous edited this page 2024-11-10 02:16:46 +01:00

This makes the Item Pocket able to hold 120 unique items, rather than 30. For adding more space for the same item, check out this tutorial.

Warning: This will break your save-file compatibility.

This can be used as a crash course for modifying the SaveBlocks for the first time. One important note is to try to keep the SaveBlock the same size, so when you add space, you'll need to remove space.

Ghoulslash has a good hack with a repo that goes more into the specifics that allows for easy trimming by modifying #defines. For more challenging save data projects, that would be recommended.

For this one, what we'll do is just add 90 more items than the default.
Then find a place to remove 90*4 bytes (each item needs 4 bytes since the ItemSlot struct contains two u16s).

In include/constants/global.h:

#define SECRET_BASES_COUNT 20
#define TV_SHOWS_COUNT 25
#define POKE_NEWS_COUNT 16
#define PC_ITEMS_COUNT 50
-#define BAG_ITEMS_COUNT 30
+#define BAG_ITEMS_COUNT 120
#define BAG_KEYITEMS_COUNT 30
#define BAG_POKEBALLS_COUNT 16
#define BAG_TMHM_COUNT 64

In include/global.h, modify SaveBlock1 to still fit the new size. Since unused_3598[0x180] isn't used, we'll take from it.
As mentioned, each item needs 4 bytes, so we need to take away 360 bytes.

0x180 in hex is 384.
384 - 360 is 24
24 is 0x18 in hex

So we change the following:

-    /*0x3598*/ u8 unused_3598[0x180];
+    /*0x3598*/ u8 unused_3598[0x18];

If the number of unique item slots is 102 or higher, it's possible for the player to hold more than 9999 of a single item (for example, 102 item slots * 99 antidotes = over 10,000 antidotes). When the player goes to buy an item at the Poké Mart, the game displays the amount the player already owns. However, the window wasn't designed to handle values over 9999, which causes an overflow when the player possesses 10,000 or more of a single item.

Make the following change in src\shop.c:

 static void Task_BuyHowManyDialogueInit(u8 taskId)
 {
     s16 *data = gTasks[taskId].data;

     u16 quantityInBag = CountTotalItemQuantityInBag(tItemId);
     u16 maxQuantity;

     DrawStdFrameWithCustomTileAndPalette(WIN_QUANTITY_IN_BAG, FALSE, 1, 13);
-    ConvertIntToDecimalStringN(gStringVar1, quantityInBag, STR_CONV_MODE_RIGHT_ALIGN, MAX_ITEM_DIGITS + 1);
+    ConvertIntToDecimalStringN(gStringVar1, quantityInBag, STR_CONV_MODE_RIGHT_ALIGN, MAX_ITEM_DIGITS + 2);

As an alternative, you can use + 6 instead of + 2 to align the string to the right side of the window instead.


And that's it. Obviously this can also be a reference for when you want to widen other save data, or only make the item pocket hold 60 unique items instead of 120. Though note that having more than 255 items can break some places, as some locations may be using u8 to hold the size.

Since the data is shifted, the byte order for everything between bagPocket_Items and unused_3598 is in a new byte location in the struct. This is what breaks using old save files.
I changed the comments in my SaveBlock to match their new byte location. Here they are below if you want them:

struct SaveBlock1
{
    /*0x00*/ struct Coords16 pos;
    /*0x04*/ struct WarpData location;
    /*0x0C*/ struct WarpData continueGameWarp;
    /*0x14*/ struct WarpData dynamicWarp;
    /*0x1C*/ struct WarpData lastHealLocation; // used by white-out and teleport
    /*0x24*/ struct WarpData escapeWarp; // used by Dig and Escape Rope
    /*0x2C*/ u16 savedMusic;
    /*0x2E*/ u8 weather;
    /*0x2F*/ u8 weatherCycleStage;
    /*0x30*/ u8 flashLevel;
    /*0x32*/ u16 mapLayoutId;
    /*0x34*/ u16 mapView[0x100];
    /*0x234*/ u8 playerPartyCount;
    /*0x238*/ struct Pokemon playerParty[PARTY_SIZE];
    /*0x490*/ u32 money;
    /*0x494*/ u16 coins;
    /*0x496*/ u16 registeredItem; // registered for use with SELECT button
    /*0x498*/ struct ItemSlot pcItems[PC_ITEMS_COUNT];
    /*0x560*/ struct ItemSlot bagPocket_Items[BAG_ITEMS_COUNT];
    /*0x740*/ struct ItemSlot bagPocket_KeyItems[BAG_KEYITEMS_COUNT];
    /*0x7B8*/ struct ItemSlot bagPocket_PokeBalls[BAG_POKEBALLS_COUNT];
    /*0x7F8*/ struct ItemSlot bagPocket_TMHM[BAG_TMHM_COUNT];
    /*0x8F8*/ struct ItemSlot bagPocket_Berries[BAG_BERRIES_COUNT];
    /*0x9B0*/ struct Pokeblock pokeblocks[POKEBLOCKS_COUNT];
    /*0xAF0*/ u8 seen1[NUM_DEX_FLAG_BYTES];
    /*0xB24*/ u16 berryBlenderRecords[3];
    /*0xB2A*/ u8 unused_9C2[6];
    /*0xB30*/ u16 trainerRematchStepCounter;
    /*0xB32*/ u8 trainerRematches[MAX_REMATCH_ENTRIES];
    /*0xB98*/ struct ObjectEvent objectEvents[OBJECT_EVENTS_COUNT];
    /*0xDD8*/ struct ObjectEventTemplate objectEventTemplates[OBJECT_EVENT_TEMPLATES_COUNT];
    /*0x13D8*/ u8 flags[NUM_FLAG_BYTES];
    /*0x1504*/ u16 vars[VARS_COUNT];
    /*0x1704*/ u32 gameStats[NUM_GAME_STATS];
    /*0x1804*/ struct BerryTree berryTrees[BERRY_TREES_COUNT];
    /*0x1C04*/ struct SecretBase secretBases[SECRET_BASES_COUNT];
    /*0x2884*/ u8 playerRoomDecorations[DECOR_MAX_PLAYERS_HOUSE];
    /*0x2890*/ u8 playerRoomDecorationPositions[DECOR_MAX_PLAYERS_HOUSE];
    /*0x289C*/ u8 decorationDesks[10];
    /*0x28A6*/ u8 decorationChairs[10];
    /*0x28B0*/ u8 decorationPlants[10];
    /*0x28BA*/ u8 decorationOrnaments[30];
    /*0x28D8*/ u8 decorationMats[30];
    /*0x28F6*/ u8 decorationPosters[10];
    /*0x2900*/ u8 decorationDolls[40];
    /*0x2928*/ u8 decorationCushions[10];
    /*0x2934*/ TVShow tvShows[TV_SHOWS_COUNT];
    /*0x2CB8*/ PokeNews pokeNews[POKE_NEWS_COUNT];
    /*0x2CF8*/ u16 outbreakPokemonSpecies;
    /*0x2CFA*/ u8 outbreakLocationMapNum;
    /*0x2CFB*/ u8 outbreakLocationMapGroup;
    /*0x2CFC*/ u8 outbreakPokemonLevel;
    /*0x2CFD*/ u8 outbreakUnused1;
    /*0x2CFE*/ u16 outbreakUnused2;
    /*0x2D00*/ u16 outbreakPokemonMoves[MAX_MON_MOVES];
    /*0x2D08*/ u8 outbreakUnused3;
    /*0x2D09*/ u8 outbreakPokemonProbability;
    /*0x2D0A*/ u16 outbreakDaysLeft;
    /*0x2D0C*/ struct GabbyAndTyData gabbyAndTyData;
    /*0x2D18*/ u16 easyChatProfile[EASY_CHAT_BATTLE_WORDS_COUNT];
    /*0x2D24*/ u16 easyChatBattleStart[EASY_CHAT_BATTLE_WORDS_COUNT];
    /*0x2D30*/ u16 easyChatBattleWon[EASY_CHAT_BATTLE_WORDS_COUNT];
    /*0x2D3C*/ u16 easyChatBattleLost[EASY_CHAT_BATTLE_WORDS_COUNT];
    /*0x2D48*/ struct Mail mail[MAIL_COUNT];
    /*0x2F88*/ u8 additionalPhrases[NUM_ADDITIONAL_PHRASE_BYTES]; // bitfield for 33 additional phrases in easy chat system
    /*0x2F90*/ OldMan oldMan;
    /*0x2FCC*/ struct DewfordTrend dewfordTrends[SAVED_TRENDS_COUNT];
    /*0x2FF8*/ struct ContestWinner contestWinners[NUM_CONTEST_WINNERS]; // see CONTEST_WINNER_*
    /*0x3198*/ struct DayCare daycare;
    /*0x32B8*/ struct LinkBattleRecords linkBattleRecords;
    /*0x3310*/ u8 giftRibbons[GIFT_RIBBONS_COUNT];
    /*0x331B*/ struct ExternalEventData externalEventData;
    /*0x332F*/ struct ExternalEventFlags externalEventFlags;
    /*0x3344*/ struct Roamer roamer;
    /*0x3360*/ struct EnigmaBerry enigmaBerry;
    /*0x3394*/ struct MysteryGiftSave mysteryGift;
    /*0x3700*/ u8 unused_3700[0x18];
    /*0x3718*/ u32 trainerHillTimes[NUM_TRAINER_HILL_MODES];
    /*0x3728*/ struct RamScript ramScript;
    /*0x3B14*/ struct RecordMixingGift recordMixingGift;
    /*0x3B24*/ u8 seen2[NUM_DEX_FLAG_BYTES];
    /*0x3B58*/ LilycoveLady lilycoveLady;
    /*0x3B98*/ struct TrainerNameRecord trainerNameRecords[20];
    /*0x3C88*/ u8 registeredTexts[UNION_ROOM_KB_ROW_COUNT][21];
    /*0x3D5A*/ u8 unused_3D5A[10];
    /*0x3D64*/ struct TrainerHillSave trainerHill;
    /*0x3D70*/ struct WaldaPhrase waldaPhrase;
    // sizeof: 0x3D88
};