Bag refactor3 + Ghoulslash's sorting feature port (#7330)

This commit is contained in:
Nephrite 2025-07-29 11:47:32 +03:00 committed by GitHub
parent 689bf0bcbc
commit 2eeb346387
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
8 changed files with 1141 additions and 191 deletions

View File

@ -9,10 +9,9 @@ enum Pocket
POCKET_BERRIES,
POCKET_KEY_ITEMS,
POCKETS_COUNT,
POCKET_DUMMY = POCKETS_COUNT,
};
#define POCKET_DUMMY POCKETS_COUNT
#define REPEL_LURE_MASK (1 << 15)
#define IS_LAST_USED_LURE(var) (var & REPEL_LURE_MASK)
#define REPEL_LURE_STEPS(var) (var & (REPEL_LURE_MASK - 1))

View File

@ -44,6 +44,46 @@ enum TMHMIndex
#undef UNPACK_TM_HM_ENUM
enum PACKED ItemSortType
{
ITEM_TYPE_UNCATEGORIZED,
ITEM_TYPE_FIELD_USE,
ITEM_TYPE_LEVEL_UP_ITEM,
ITEM_TYPE_HEALTH_RECOVERY,
ITEM_TYPE_STATUS_RECOVERY,
ITEM_TYPE_PP_RECOVERY,
ITEM_TYPE_NATURE_MINT,
ITEM_TYPE_STAT_BOOST_DRINK,
ITEM_TYPE_STAT_BOOST_FEATHER,
ITEM_TYPE_STAT_BOOST_MOCHI,
ITEM_TYPE_BATTLE_ITEM,
ITEM_TYPE_FLUTE,
ITEM_TYPE_X_ITEM,
ITEM_TYPE_AUX_ITEM,
ITEM_TYPE_EVOLUTION_STONE,
ITEM_TYPE_EVOLUTION_ITEM,
ITEM_TYPE_SPECIAL_HELD_ITEM,
ITEM_TYPE_MEGA_STONE,
ITEM_TYPE_Z_CRYSTAL,
ITEM_TYPE_TERA_SHARD,
ITEM_TYPE_HELD_ITEM,
ITEM_TYPE_TYPE_BOOST_HELD_ITEM,
ITEM_TYPE_CONTEST_HELD_ITEM,
ITEM_TYPE_EV_BOOST_HELD_ITEM,
ITEM_TYPE_GEM,
ITEM_TYPE_PLATE,
ITEM_TYPE_MEMORY,
ITEM_TYPE_DRIVE,
ITEM_TYPE_INCENSE,
ITEM_TYPE_NECTAR,
ITEM_TYPE_GROWTH,
ITEM_TYPE_SHARD,
ITEM_TYPE_SELLABLE,
ITEM_TYPE_RELIC,
ITEM_TYPE_FOSSIL,
ITEM_TYPE_MAIL,
};
typedef void (*ItemUseFunc)(u8);
struct Item
@ -60,7 +100,7 @@ struct Item
u8 importance:2;
u8 notConsumed:1;
enum Pocket pocket:5;
u8 padding;
enum ItemSortType sortType;
u8 type;
u8 battleUsage;
u8 flingPower;
@ -139,16 +179,29 @@ static inline u16 GetTMHMMoveId(enum TMHMIndex index)
return gTMHMItemMoveIds[index].moveId;
}
enum SortPocket
{
SORT_NONE,
SORT_POCKET_BY_ITEM_ID,
SORT_POCKET_TM_HM,
};
void BagPocket_SetSlotData(struct BagPocket *pocket, u32 pocketPos, struct ItemSlot newSlot);
struct ItemSlot BagPocket_GetSlotData(struct BagPocket *pocket, u32 pocketPos);
static inline void BagPocket_SetSlotItemIdAndCount(struct BagPocket *pocket, u32 pocketPos, u16 itemId, u16 quantity)
{
BagPocket_SetSlotData(pocket, pocketPos, (struct ItemSlot) {itemId, quantity});
}
static inline u16 GetBagItemId(enum Pocket pocketId, u32 pocketPos)
{
return BagPocket_GetSlotData(&gBagPockets[pocketId], pocketPos).itemId;
}
static inline u16 GetBagItemQuantity(enum Pocket pocketId, u32 pocketPos)
{
return BagPocket_GetSlotData(&gBagPockets[pocketId], pocketPos).quantity;
}
static inline struct ItemSlot GetBagItemIdAndQuantity(enum Pocket pocketId, u32 pocketPos)
{
return BagPocket_GetSlotData(&gBagPockets[pocketId], pocketPos);
}
void GetBagItemIdAndQuantity(enum Pocket pocketId, u32 pocketPos, u16 *itemId, u16 *quantity);
u16 GetBagItemId(enum Pocket pocketId, u32 pocketPos);
u16 GetBagItemQuantity(enum Pocket pocketId, u32 pocketPos);
void ApplyNewEncryptionKeyToBagItems(u32 newKey);
void SetBagItemsPointers(void);
u8 *CopyItemName(u16 itemId, u8 *dst);
@ -168,7 +221,6 @@ void RemovePCItem(u8 index, u16 count);
void CompactPCItems(void);
void SwapRegisteredBike(void);
void CompactItemsInBagPocket(enum Pocket pocketId);
void SortPocket(enum Pocket pocketId, enum SortPocket sortPocket);
void MoveItemSlotInPocket(enum Pocket pocketId, u32 from, u32 to);
void MoveItemSlotInPC(struct ItemSlot *itemSlots, u32 from, u32 to);
void ClearBag(void);

View File

@ -36,6 +36,15 @@ enum {
ITEMWIN_COUNT
};
//bag sort
enum BagSortOptions
{
SORT_ALPHABETICALLY,
SORT_BY_TYPE,
SORT_BY_AMOUNT, //greatest->least
SORT_BY_INDEX,
};
#define ITEMMENU_SWAP_LINE_LENGTH 8 // Swap line is 8 sprites long
enum {
ITEMMENUSPRITE_BAG,
@ -105,10 +114,11 @@ void CB2_ChooseBerry(void);
void CB2_ChooseMulch(void);
void Task_FadeAndCloseBagMenu(u8 taskId);
void BagMenu_YesNo(u8 taskId, u8 windowType, const struct YesNoFuncTable *funcTable);
void UpdatePocketItemList(u8 pocketId);
void UpdatePocketItemList(enum Pocket pocketId);
void DisplayItemMessage(u8 taskId, u8 fontId, const u8 *str, void (*callback)(u8 taskId));
void DisplayItemMessageOnField(u8 taskId, const u8 *string, TaskFunc callback);
void CloseItemMessage(u8 taskId);
void ItemMenu_RotomCatalog(u8 taskId);
void SortItemsInBag(struct BagPocket *pocket, enum BagSortOptions type);
#endif //GUARD_ITEM_MENU_H

File diff suppressed because it is too large Load Diff

View File

@ -32,8 +32,6 @@ static bool32 CheckPyramidBagHasItem(u16 itemId, u16 count);
static bool32 CheckPyramidBagHasSpace(u16 itemId, u16 count);
static const u8 *GetItemPluralName(u16);
static bool32 DoesItemHavePluralName(u16);
static void BagPocket_GetSetSlotDataGeneric(struct BagPocket *pocket, u32 pocketPos, u16 *itemId, u16 *quantity, bool32 isSetting);
static void BagPocket_GetSetSlotDataPC(struct BagPocket *pocket, u32 pocketPos, u16 *itemId, u16 *quantity, bool32 isSetting);
EWRAM_DATA struct BagPocket gBagPockets[POCKETS_COUNT] = {0};
@ -61,71 +59,72 @@ const struct TmHmIndexKey gTMHMItemMoveIds[NUM_ALL_MACHINES + 1] =
#undef UNPACK_TM_ITEM_ID
#undef UNPACK_HM_ITEM_ID
static void (*const sBagPocket_GetSetSlotDataFuncs[])(struct BagPocket *pocket, u32 pocketPos, u16 *itemId, u16 *quantity, bool32 isSetting) =
static inline struct ItemSlot NONNULL BagPocket_GetSlotDataGeneric(struct BagPocket *pocket, u32 pocketPos)
{
[POCKET_ITEMS] = BagPocket_GetSetSlotDataGeneric,
[POCKET_KEY_ITEMS] = BagPocket_GetSetSlotDataGeneric,
[POCKET_POKE_BALLS] = BagPocket_GetSetSlotDataGeneric,
[POCKET_TM_HM] = BagPocket_GetSetSlotDataGeneric,
[POCKET_BERRIES] = BagPocket_GetSetSlotDataGeneric,
[POCKET_DUMMY] = BagPocket_GetSetSlotDataPC,
};
return (struct ItemSlot) {
.itemId = pocket->itemSlots[pocketPos].itemId,
.quantity = pocket->itemSlots[pocketPos].quantity ^ gSaveBlock2Ptr->encryptionKey,
};
}
static void NONNULL BagPocket_GetSetSlotDataGeneric(struct BagPocket *pocket, u32 pocketPos, u16 *itemId, u16 *quantity, bool32 isSetting)
static inline struct ItemSlot NONNULL BagPocket_GetSlotDataPC(struct BagPocket *pocket, u32 pocketPos)
{
if (isSetting)
return (struct ItemSlot) {
.itemId = pocket->itemSlots[pocketPos].itemId,
.quantity = pocket->itemSlots[pocketPos].quantity,
};
}
static inline void NONNULL BagPocket_SetSlotDataGeneric(struct BagPocket *pocket, u32 pocketPos, struct ItemSlot newSlot)
{
pocket->itemSlots[pocketPos].itemId = newSlot.itemId;
pocket->itemSlots[pocketPos].quantity = newSlot.quantity ^ gSaveBlock2Ptr->encryptionKey;
}
static inline void NONNULL BagPocket_SetSlotDataPC(struct BagPocket *pocket, u32 pocketPos, struct ItemSlot newSlot)
{
pocket->itemSlots[pocketPos].itemId = newSlot.itemId;
pocket->itemSlots[pocketPos].quantity = newSlot.quantity;
}
struct ItemSlot NONNULL BagPocket_GetSlotData(struct BagPocket *pocket, u32 pocketPos)
{
switch (pocket->id)
{
pocket->itemSlots[pocketPos].itemId = *quantity ? *itemId : ITEM_NONE; // Sets to zero if quantity is zero
pocket->itemSlots[pocketPos].quantity = *quantity ^ gSaveBlock2Ptr->encryptionKey;
case POCKET_ITEMS:
case POCKET_KEY_ITEMS:
case POCKET_POKE_BALLS:
case POCKET_TM_HM:
case POCKET_BERRIES:
return BagPocket_GetSlotDataGeneric(pocket, pocketPos);
case POCKET_DUMMY:
return BagPocket_GetSlotDataPC(pocket, pocketPos);
}
else
return (struct ItemSlot) {0}; // Because compiler complains
}
void NONNULL BagPocket_SetSlotData(struct BagPocket *pocket, u32 pocketPos, struct ItemSlot newSlot)
{
if (newSlot.itemId == ITEM_NONE || newSlot.quantity == 0) // Sets to zero if quantity or itemId is zero
{
*itemId = pocket->itemSlots[pocketPos].itemId;
*quantity = pocket->itemSlots[pocketPos].quantity ^ gSaveBlock2Ptr->encryptionKey;
newSlot.itemId = ITEM_NONE;
newSlot.quantity = 0;
}
}
static void NONNULL BagPocket_GetSetSlotDataPC(struct BagPocket *pocket, u32 pocketPos, u16 *itemId, u16 *quantity, bool32 isSetting)
{
if (isSetting)
switch (pocket->id)
{
pocket->itemSlots[pocketPos].itemId = *quantity ? *itemId : ITEM_NONE; // Sets to zero if quantity is zero
pocket->itemSlots[pocketPos].quantity = *quantity;
case POCKET_ITEMS:
case POCKET_KEY_ITEMS:
case POCKET_POKE_BALLS:
case POCKET_TM_HM:
case POCKET_BERRIES:
BagPocket_SetSlotDataGeneric(pocket, pocketPos, newSlot);
break;
case POCKET_DUMMY:
BagPocket_SetSlotDataPC(pocket, pocketPos, newSlot);
break;
}
else
{
*itemId = pocket->itemSlots[pocketPos].itemId;
*quantity = pocket->itemSlots[pocketPos].quantity;
}
}
static inline void NONNULL BagPocket_GetSlotData(struct BagPocket *pocket, u32 pocketPos, u16 *itemId, u16 *quantity)
{
sBagPocket_GetSetSlotDataFuncs[pocket->id](pocket, pocketPos, itemId, quantity, FALSE);
}
static inline void NONNULL BagPocket_SetSlotData(struct BagPocket *pocket, u32 pocketPos, u16 *itemId, u16 *quantity)
{
sBagPocket_GetSetSlotDataFuncs[pocket->id](pocket, pocketPos, itemId, quantity, TRUE);
}
void GetBagItemIdAndQuantity(enum Pocket pocketId, u32 pocketPos, u16 *itemId, u16 *quantity)
{
BagPocket_GetSlotData(&gBagPockets[pocketId], pocketPos, itemId, quantity);
}
u16 GetBagItemId(enum Pocket pocketId, u32 pocketPos)
{
u16 itemId, quantity;
BagPocket_GetSlotData(&gBagPockets[pocketId], pocketPos, &itemId, &quantity);
return itemId;
}
u16 GetBagItemQuantity(enum Pocket pocketId, u32 pocketPos)
{
u16 itemId, quantity;
BagPocket_GetSlotData(&gBagPockets[pocketId], pocketPos, &itemId, &quantity);
return quantity;
}
void ApplyNewEncryptionKeyToBagItems(u32 newKey)
@ -200,14 +199,14 @@ bool32 IsBagPocketNonEmpty(enum Pocket pocketId)
static bool32 NONNULL BagPocket_CheckHasItem(struct BagPocket *pocket, u16 itemId, u16 count)
{
u16 tempItemId, tempQuantity;
struct ItemSlot tempItem;
// Check for item slots that contain the item
for (u32 i = 0; i < pocket->capacity && count > 0; i++)
{
BagPocket_GetSlotData(pocket, i, &tempItemId, &tempQuantity);
if (tempItemId == itemId)
count -= min(count, tempQuantity);
tempItem = BagPocket_GetSlotData(pocket, i);
if (tempItem.itemId == itemId)
count -= min(count, tempItem.quantity);
}
return count == 0;
@ -257,14 +256,14 @@ bool32 CheckBagHasSpace(u16 itemId, u16 count)
static u32 NONNULL BagPocket_GetFreeSpaceForItem(struct BagPocket *pocket, u16 itemId)
{
u32 spaceForItem = 0;
u16 tempItemId, tempQuantity;
struct ItemSlot tempItem;
// Check space in any existing item slots that already contain this item
for (u32 i = 0; i < pocket->capacity; i++)
{
BagPocket_GetSlotData(pocket, i, &tempItemId, &tempQuantity);
if (tempItemId == ITEM_NONE || tempItemId == itemId)
spaceForItem += (tempItemId ? (MAX_BAG_ITEM_CAPACITY - tempQuantity) : MAX_BAG_ITEM_CAPACITY);
tempItem = BagPocket_GetSlotData(pocket, i);
if (tempItem.itemId == ITEM_NONE || tempItem.itemId == itemId)
spaceForItem += (tempItem.itemId ? (MAX_BAG_ITEM_CAPACITY - tempItem.quantity) : MAX_BAG_ITEM_CAPACITY);
}
return spaceForItem;
@ -280,17 +279,16 @@ u32 GetFreeSpaceForItemInBag(u16 itemId)
static inline bool32 NONNULL CheckSlotAndUpdateCount(struct BagPocket *pocket, u16 itemId, u32 pocketPos, u32 *nextPocketPos, u16 *count, u16 *tempPocketSlotQuantities)
{
u16 tempItemId, tempQuantity;
BagPocket_GetSlotData(pocket, pocketPos, &tempItemId, &tempQuantity);
if (tempItemId == ITEM_NONE || tempItemId == itemId)
struct ItemSlot tempItem = BagPocket_GetSlotData(pocket, pocketPos);
if (tempItem.itemId == ITEM_NONE || tempItem.itemId == itemId)
{
// The quantity already at the slot - zero if an empty slot
if (!tempItemId)
tempQuantity = 0;
if (tempItem.itemId == ITEM_NONE)
tempItem.quantity = 0;
// Record slot quantity in tempPocketSlotQuantities, adjust count
tempPocketSlotQuantities[pocketPos] = min(MAX_BAG_ITEM_CAPACITY, *count + tempQuantity);
*count -= min(*count, MAX_BAG_ITEM_CAPACITY - tempQuantity);
tempPocketSlotQuantities[pocketPos] = min(MAX_BAG_ITEM_CAPACITY, *count + tempItem.quantity);
*count -= min(*count, MAX_BAG_ITEM_CAPACITY - tempItem.quantity);
// Set the starting index for the next loop to set items (shifted by one)
if (!*nextPocketPos)
@ -336,7 +334,7 @@ static bool32 NONNULL BagPocket_AddItem(struct BagPocket *pocket, u16 itemId, u1
for (--itemAddIndex; itemAddIndex < itemLookupIndex; itemAddIndex++)
{
if (tempPocketSlotQuantities[itemAddIndex] > 0)
BagPocket_SetSlotData(pocket, itemAddIndex, &itemId, &tempPocketSlotQuantities[itemAddIndex]);
BagPocket_SetSlotItemIdAndCount(pocket, itemAddIndex, itemId, tempPocketSlotQuantities[itemAddIndex]);
}
}
@ -359,21 +357,21 @@ bool32 AddBagItem(u16 itemId, u16 count)
static bool32 NONNULL BagPocket_RemoveItem(struct BagPocket *pocket, u16 itemId, u16 count)
{
u32 itemLookupIndex, itemRemoveIndex = 0, totalQuantity = 0;
u16 tempItemId, tempQuantity;
struct ItemSlot tempItem;
u16 *tempPocketSlotQuantities = AllocZeroed(sizeof(u16) * pocket->capacity);
for (itemLookupIndex = 0; itemLookupIndex < pocket->capacity && totalQuantity < count; itemLookupIndex++)
{
BagPocket_GetSlotData(pocket, itemLookupIndex, &tempItemId, &tempQuantity);
if (tempItemId == itemId)
tempItem = BagPocket_GetSlotData(pocket, itemLookupIndex);
if (tempItem.itemId == itemId)
{
// Index for the next loop - where we should start removing items
if (!itemRemoveIndex)
itemRemoveIndex = itemLookupIndex + 1;
// Gather quantities (+ 1 to tempPocketSlotQuantities so that even if setting to 0 we know which indices to target)
totalQuantity += tempQuantity;
tempPocketSlotQuantities[itemLookupIndex] = (tempQuantity <= count ? 0 : tempQuantity - count) + 1;
totalQuantity += tempItem.quantity;
tempPocketSlotQuantities[itemLookupIndex] = (tempItem.quantity <= count ? 0 : tempItem.quantity - count) + 1;
}
}
@ -389,10 +387,7 @@ static bool32 NONNULL BagPocket_RemoveItem(struct BagPocket *pocket, u16 itemId,
for (--itemRemoveIndex; itemRemoveIndex < itemLookupIndex; itemRemoveIndex++)
{
if (tempPocketSlotQuantities[itemRemoveIndex] > 0)
{
tempPocketSlotQuantities[itemRemoveIndex]--; // Reverse the +1 shift
BagPocket_SetSlotData(pocket, itemRemoveIndex, &itemId, &tempPocketSlotQuantities[itemRemoveIndex]);
}
BagPocket_SetSlotItemIdAndCount(pocket, itemRemoveIndex, itemId, tempPocketSlotQuantities[itemRemoveIndex] - 1);
}
}
@ -415,12 +410,10 @@ bool32 RemoveBagItem(u16 itemId, u16 count)
static u8 NONNULL BagPocket_CountUsedItemSlots(struct BagPocket *pocket)
{
u8 usedSlots = 0;
u16 tempItemId, tempQuantity;
for (u32 i = 0; i < pocket->capacity; i++)
{
BagPocket_GetSlotData(pocket, i, &tempItemId, &tempQuantity);
if (tempItemId)
if (BagPocket_GetSlotData(pocket, i).itemId != ITEM_NONE)
usedSlots++;
}
return usedSlots;
@ -434,12 +427,12 @@ u8 CountUsedPCItemSlots(void)
static bool32 NONNULL BagPocket_CheckPocketForItemCount(struct BagPocket *pocket, u16 itemId, u16 count)
{
u16 tempItemId, tempQuantity;
struct ItemSlot tempItem;
for (u32 i = 0; i < pocket->capacity; i++)
{
BagPocket_GetSlotData(pocket, i, &tempItemId, &tempQuantity);
if (tempItemId == itemId && tempQuantity >= count)
tempItem = BagPocket_GetSlotData(pocket, i);
if (tempItem.itemId == itemId && tempItem.quantity >= count)
return TRUE;
}
return FALSE;
@ -459,20 +452,20 @@ bool32 AddPCItem(u16 itemId, u16 count)
static void NONNULL BagPocket_CompactItems(struct BagPocket *pocket)
{
u16 itemId, quantity, zero = 0, slotCursor = 0;
struct ItemSlot tempItem;
u32 slotCursor = 0;
for (u32 i = 0; i < pocket->capacity; i++)
{
BagPocket_GetSlotData(pocket, i, &itemId, &quantity);
if (itemId == ITEM_NONE)
tempItem = BagPocket_GetSlotData(pocket, i);
if (tempItem.itemId == ITEM_NONE)
{
if (!slotCursor)
slotCursor = i + 1;
}
else if (slotCursor > 0)
{
BagPocket_SetSlotData(pocket, slotCursor - 1, &itemId, &quantity);
BagPocket_SetSlotData(pocket, i, &zero, &zero);
slotCursor++;
BagPocket_SetSlotData(pocket, slotCursor++ - 1, tempItem);
BagPocket_SetSlotItemIdAndCount(pocket, i, ITEM_NONE, 0);
}
}
}
@ -482,15 +475,13 @@ void RemovePCItem(u8 index, u16 count)
struct BagPocket dummyPocket = DUMMY_PC_BAG_POCKET;
// Get id, quantity at slot
u16 tempItemId, tempQuantity;
BagPocket_GetSlotData(&dummyPocket, index, &tempItemId, &tempQuantity);
struct ItemSlot tempItem = BagPocket_GetSlotData(&dummyPocket, index);
// Remove quantity
tempQuantity -= count;
BagPocket_SetSlotData(&dummyPocket, index, &tempItemId, &tempQuantity);
BagPocket_SetSlotItemIdAndCount(&dummyPocket, index, tempItem.itemId, tempItem.quantity - count);
// Compact if necessary
if (tempQuantity == 0)
if (tempItem.quantity == 0)
BagPocket_CompactItems(&dummyPocket);
}
@ -518,42 +509,6 @@ void CompactItemsInBagPocket(enum Pocket pocketId)
BagPocket_CompactItems(&gBagPockets[pocketId]);
}
// Opens the possibility of sorting by other means e.g. ghoulslash's advanced sorting
static inline bool32 ItemIndexCompare(u16 itemA, u16 itemB, enum SortPocket sortPocket)
{
switch (sortPocket)
{
case SORT_POCKET_BY_ITEM_ID:
return itemA > itemB;
case SORT_POCKET_TM_HM:
return GetItemTMHMIndex(itemA) > GetItemTMHMIndex(itemB);
default:
return FALSE;
}
}
void SortPocket(enum Pocket pocketId, enum SortPocket sortPocket)
{
u16 itemId_i, quantity_i, itemId_j, quantity_j;
struct BagPocket *pocket = &gBagPockets[pocketId];
for (u32 i = 0; i < pocket->capacity - 1; i++)
{
BagPocket_GetSlotData(pocket, i, &itemId_i, &quantity_i);
for (u32 j = i + 1; j < pocket->capacity; j++)
{
BagPocket_GetSlotData(pocket, j, &itemId_j, &quantity_j);
if (itemId_j && (!itemId_i || ItemIndexCompare(itemId_i, itemId_j, sortPocket)))
{
BagPocket_SetSlotData(pocket, i, &itemId_j, &quantity_j);
BagPocket_SetSlotData(pocket, j, &itemId_i, &quantity_i);
itemId_i = itemId_j;
quantity_i = quantity_j;
}
}
}
}
static inline void NONNULL BagPocket_MoveItemSlot(struct BagPocket *pocket, u32 from, u32 to)
{
if (from != to)
@ -563,18 +518,14 @@ static inline void NONNULL BagPocket_MoveItemSlot(struct BagPocket *pocket, u32
to--;
// Record the values at "from"
u16 fromItemId, fromQuantity, tempItemId, tempQuantity;
BagPocket_GetSlotData(pocket, from, &fromItemId, &fromQuantity);
struct ItemSlot fromSlot = BagPocket_GetSlotData(pocket, from);
// Shuffle items between "to" and "from"
for (u32 i = from; i != to; i += shift)
{
BagPocket_GetSlotData(pocket, i + shift, &tempItemId, &tempQuantity);
BagPocket_SetSlotData(pocket, i, &tempItemId, &tempQuantity);
}
BagPocket_SetSlotData(pocket, i, BagPocket_GetSlotData(pocket, i + shift));
// Move the saved "from" to "to"
BagPocket_SetSlotData(pocket, to, &fromItemId, &fromQuantity);
BagPocket_SetSlotData(pocket, to, fromSlot);
}
}
@ -596,13 +547,14 @@ void ClearBag(void)
static inline u16 NONNULL BagPocket_CountTotalItemQuantity(struct BagPocket *pocket, u16 itemId)
{
u16 tempItemId, tempQuantity, ownedCount = 0;
u32 ownedCount = 0;
struct ItemSlot tempItem;
for (u32 i = 0; i < pocket->capacity; i++)
{
BagPocket_GetSlotData(pocket, i, &tempItemId, &tempQuantity);
if (tempItemId == itemId)
ownedCount += tempQuantity;
tempItem = BagPocket_GetSlotData(pocket, i);
if (tempItem.itemId == itemId)
ownedCount += tempItem.quantity;
}
return ownedCount;

View File

@ -89,6 +89,10 @@ enum {
ACTION_SHOW,
ACTION_GIVE_FAVOR_LADY,
ACTION_CONFIRM_QUIZ_LADY,
ACTION_BY_NAME,
ACTION_BY_TYPE,
ACTION_BY_AMOUNT,
ACTION_BY_INDEX,
ACTION_DUMMY,
};
@ -218,6 +222,19 @@ static const u8 sText_DepositedVar2Var1s[] = _("Deposited {STR_VAR_2}\n{STR_VAR_
static const u8 sText_NoRoomForItems[] = _("There's no room to\nstore items.");
static const u8 sText_CantStoreImportantItems[] = _("Important items\ncan't be stored in\nthe PC!");
static void Task_LoadBagSortOptions(u8 taskId);
static void ItemMenu_SortByName(u8 taskId);
static void ItemMenu_SortByType(u8 taskId);
static void ItemMenu_SortByAmount(u8 taskId);
static void ItemMenu_SortByIndex(u8 taskId);
static void SortBagItems(u8 taskId);
static void Task_SortFinish(u8 taskId);
static void MergeSort(struct BagPocket *pocket, s32 (*comparator)(enum Pocket, struct ItemSlot, struct ItemSlot));
static s32 CompareItemsAlphabetically(enum Pocket pocketId, struct ItemSlot item1, struct ItemSlot item2);
static s32 CompareItemsByMost(enum Pocket pocketId, struct ItemSlot item1, struct ItemSlot item2);
static s32 CompareItemsByType(enum Pocket pocketId, struct ItemSlot item1, struct ItemSlot item2);
static s32 CompareItemsByIndex(enum Pocket pocketId, struct ItemSlot item1, struct ItemSlot item2);
static const struct BgTemplate sBgTemplates_ItemMenu[] =
{
{
@ -271,6 +288,7 @@ static const struct ListMenuTemplate sItemListMenu =
.cursorKind = CURSOR_BLACK_ARROW
};
static const u8 sText_NothingToSort[] = _("There's nothing to sort!");
static const struct MenuAction sItemMenuActions[] = {
[ACTION_USE] = {gMenuText_Use, {ItemMenu_UseOutOfBattle}},
[ACTION_TOSS] = {gMenuText_Toss, {ItemMenu_Toss}},
@ -286,6 +304,10 @@ static const struct MenuAction sItemMenuActions[] = {
[ACTION_SHOW] = {COMPOUND_STRING("SHOW"), {ItemMenu_Show}},
[ACTION_GIVE_FAVOR_LADY] = {gMenuText_Give2, {ItemMenu_GiveFavorLady}},
[ACTION_CONFIRM_QUIZ_LADY] = {gMenuText_Confirm, {ItemMenu_ConfirmQuizLady}},
[ACTION_BY_NAME] = {COMPOUND_STRING("Name"), {ItemMenu_SortByName}},
[ACTION_BY_TYPE] = {COMPOUND_STRING("Type"), {ItemMenu_SortByType}},
[ACTION_BY_AMOUNT] = {COMPOUND_STRING("Amount"), {ItemMenu_SortByAmount}},
[ACTION_BY_INDEX] = {COMPOUND_STRING("Index"), {ItemMenu_SortByIndex}},
[ACTION_DUMMY] = {gText_EmptyString2, {NULL}}
};
@ -966,7 +988,6 @@ static void BagMenu_ItemPrintCallback(u8 windowId, u32 itemIndex, u8 y)
{
if (itemIndex != LIST_CANCEL)
{
u16 itemId, itemQuantity;
s32 offset;
if (gBagMenu->toSwapPos != NOT_SWAPPING)
@ -978,16 +999,16 @@ static void BagMenu_ItemPrintCallback(u8 windowId, u32 itemIndex, u8 y)
BagMenu_PrintCursorAtPos(y, COLORID_NONE);
}
GetBagItemIdAndQuantity(gBagPosition.pocket, itemIndex, &itemId, &itemQuantity);
struct ItemSlot itemSlot = GetBagItemIdAndQuantity(gBagPosition.pocket, itemIndex);
// Draw HM icon
if (gBagPosition.pocket == POCKET_TM_HM && GetItemTMHMIndex(itemId) > NUM_TECHNICAL_MACHINES)
if (gBagPosition.pocket == POCKET_TM_HM && GetItemTMHMIndex(itemSlot.itemId) > NUM_TECHNICAL_MACHINES)
BlitBitmapToWindow(windowId, gBagMenuHMIcon_Gfx, 8, y - 1, 16, 16);
if (gBagPosition.pocket != POCKET_KEY_ITEMS && GetItemImportance(itemId) == FALSE)
if (gBagPosition.pocket != POCKET_KEY_ITEMS && GetItemImportance(itemSlot.itemId) == FALSE)
{
// Print item quantity
ConvertIntToDecimalStringN(gStringVar1, itemQuantity, STR_CONV_MODE_RIGHT_ALIGN, MAX_ITEM_DIGITS);
ConvertIntToDecimalStringN(gStringVar1, itemSlot.quantity, STR_CONV_MODE_RIGHT_ALIGN, MAX_ITEM_DIGITS);
StringExpandPlaceholders(gStringVar4, gText_xVar1);
offset = GetStringRightAlignXOffset(FONT_NARROW, gStringVar4, 119);
BagMenu_Print(windowId, FONT_NARROW, gStringVar4, offset, y, 0, 0, TEXT_SKIP_DRAW, COLORID_NORMAL);
@ -995,7 +1016,7 @@ static void BagMenu_ItemPrintCallback(u8 windowId, u32 itemIndex, u8 y)
else
{
// Print registered icon
if (gSaveBlock1Ptr->registeredItem != ITEM_NONE && gSaveBlock1Ptr->registeredItem == itemId)
if (gSaveBlock1Ptr->registeredItem != ITEM_NONE && gSaveBlock1Ptr->registeredItem == itemSlot.itemId)
BlitBitmapToWindow(windowId, sRegisteredSelect_Gfx, 96, y - 1, 24, 16);
}
}
@ -1125,16 +1146,17 @@ static void Task_CloseBagMenu(u8 taskId)
}
}
void UpdatePocketItemList(u8 pocketId)
void UpdatePocketItemList(enum Pocket pocketId)
{
u16 i;
if (pocketId >= POCKETS_COUNT)
return; // shouldn't even get here
struct BagPocket *pocket = &gBagPockets[pocketId];
switch (pocketId)
{
case POCKET_TM_HM:
SortPocket(pocketId, SORT_POCKET_TM_HM);
break;
case POCKET_BERRIES:
SortPocket(pocketId, SORT_POCKET_BY_ITEM_ID);
SortItemsInBag(pocket, SORT_BY_INDEX);
break;
default:
CompactItemsInBagPocket(pocketId);
@ -1143,7 +1165,7 @@ void UpdatePocketItemList(u8 pocketId)
gBagMenu->numItemStacks[pocketId] = 0;
for (i = 0; i < gBagPockets[pocketId].capacity && GetBagItemId(pocketId, i); i++)
for (u32 i = 0; i < pocket->capacity && BagPocket_GetSlotData(pocket, i).itemId; i++)
gBagMenu->numItemStacks[pocketId]++;
if (!gBagMenu->hideCloseBagText)
@ -1263,6 +1285,34 @@ static void Task_BagMenu_HandleInput(u8 taskId)
}
return;
}
else if (JOY_NEW(START_BUTTON))
{
if ((gBagMenu->numItemStacks[gBagPosition.pocket] - 1) <= 1) //can't sort with 0 or 1 item in bag
{
static const u8 sText_NothingToSort[] = _("There's nothing to sort!");
PlaySE(SE_FAILURE);
DisplayItemMessage(taskId, 1, sText_NothingToSort, HandleErrorMessage);
break;
}
else
{
struct ItemSlot tempItem;
data[1] = GetItemListPosition(gBagPosition.pocket);
tempItem = GetBagItemIdAndQuantity(gBagPosition.pocket, data[1]);
data[2] = tempItem.quantity;
if (gBagPosition.cursorPosition[gBagPosition.pocket] == gBagMenu->numItemStacks[gBagPosition.pocket])
break;
else
gSpecialVar_ItemId = tempItem.itemId;
PlaySE(SE_SELECT);
BagDestroyPocketScrollArrowPair();
BagMenu_PrintCursor(tListTaskId, COLORID_GRAY_CURSOR);
ListMenuGetScrollAndRow(data[0], scrollPos, cursorPos);
gTasks[taskId].func = Task_LoadBagSortOptions;
return;
}
}
break;
}
@ -1283,12 +1333,16 @@ static void Task_BagMenu_HandleInput(u8 taskId)
gTasks[taskId].func = Task_FadeAndCloseBagMenu;
break;
default: // A_BUTTON
PlaySE(SE_SELECT);
BagDestroyPocketScrollArrowPair();
BagMenu_PrintCursor(tListTaskId, COLORID_GRAY_CURSOR);
tListPosition = listPosition;
GetBagItemIdAndQuantity(gBagPosition.pocket, listPosition, &gSpecialVar_ItemId, (u16*)&tQuantity);
sContextMenuFuncs[gBagPosition.location](taskId);
{
struct ItemSlot itemSlot = GetBagItemIdAndQuantity(gBagPosition.pocket, listPosition);
PlaySE(SE_SELECT);
BagDestroyPocketScrollArrowPair();
BagMenu_PrintCursor(tListTaskId, COLORID_GRAY_CURSOR);
tListPosition = listPosition;
gSpecialVar_ItemId = itemSlot.itemId;
tQuantity = itemSlot.quantity;
sContextMenuFuncs[gBagPosition.location](taskId);
}
break;
}
}
@ -2639,3 +2693,310 @@ static void PrintTMHMMoveData(u16 itemId)
CopyWindowToVram(WIN_TMHM_INFO, COPYWIN_GFX);
}
}
static const u8 sText_SortItemsHow[] = _("Sort items how?");
static const u8 sText_ItemsSorted[] = _("Items sorted by {STR_VAR_1}!");
static const u8 *const sSortTypeStrings[] =
{
[SORT_ALPHABETICALLY] = COMPOUND_STRING("name"),
[SORT_BY_TYPE] = COMPOUND_STRING("type"),
[SORT_BY_AMOUNT] = COMPOUND_STRING("amount"),
[SORT_BY_INDEX] = COMPOUND_STRING("index")
};
static const u8 sBagMenuSortItems[] =
{
ACTION_BY_NAME,
ACTION_BY_TYPE,
ACTION_BY_AMOUNT,
ACTION_CANCEL,
};
static const u8 sBagMenuSortKeyItems[] =
{
ACTION_BY_NAME,
ACTION_CANCEL,
};
static const u8 sBagMenuSortPokeBalls[] =
{
ACTION_BY_NAME,
ACTION_BY_AMOUNT,
ACTION_DUMMY,
ACTION_CANCEL,
};
static const u8 sBagMenuSortBerriesTMsHMs[] =
{
ACTION_BY_NAME,
ACTION_BY_AMOUNT,
ACTION_BY_INDEX,
ACTION_CANCEL,
};
static void AddBagSortSubMenu(void)
{
switch (gBagPosition.pocket)
{
case POCKET_KEY_ITEMS:
gBagMenu->contextMenuItemsPtr = sBagMenuSortKeyItems;
memcpy(&gBagMenu->contextMenuItemsBuffer, &sBagMenuSortKeyItems, NELEMS(sBagMenuSortKeyItems));
gBagMenu->contextMenuNumItems = NELEMS(sBagMenuSortKeyItems);
break;
case POCKET_POKE_BALLS:
gBagMenu->contextMenuItemsPtr = sBagMenuSortPokeBalls;
memcpy(&gBagMenu->contextMenuItemsBuffer, &sBagMenuSortPokeBalls, NELEMS(sBagMenuSortPokeBalls));
gBagMenu->contextMenuNumItems = NELEMS(sBagMenuSortPokeBalls);
break;
case POCKET_BERRIES:
case POCKET_TM_HM:
gBagMenu->contextMenuItemsPtr = sBagMenuSortBerriesTMsHMs;
memcpy(&gBagMenu->contextMenuItemsBuffer, &sBagMenuSortBerriesTMsHMs, NELEMS(sBagMenuSortBerriesTMsHMs));
gBagMenu->contextMenuNumItems = NELEMS(sBagMenuSortBerriesTMsHMs);
break;
default:
gBagMenu->contextMenuItemsPtr = sBagMenuSortItems;
memcpy(&gBagMenu->contextMenuItemsBuffer, &sBagMenuSortItems, NELEMS(sBagMenuSortItems));
gBagMenu->contextMenuNumItems = NELEMS(sBagMenuSortItems);
break;
}
StringExpandPlaceholders(gStringVar4, sText_SortItemsHow);
FillWindowPixelBuffer(1, PIXEL_FILL(0));
BagMenu_Print(1, 1, gStringVar4, 3, 1, 0, 0, 0, 0);
if (gBagMenu->contextMenuNumItems == 2)
PrintContextMenuItems(BagMenu_AddWindow(ITEMWIN_1x2));
else if (gBagMenu->contextMenuNumItems == 4)
PrintContextMenuItemGrid(BagMenu_AddWindow(ITEMWIN_2x2), 2, 2);
else
PrintContextMenuItemGrid(BagMenu_AddWindow(ITEMWIN_2x3), 2, 3);
}
static void Task_LoadBagSortOptions(u8 taskId)
{
AddBagSortSubMenu();
if (gBagMenu->contextMenuNumItems <= 2)
gTasks[taskId].func = Task_ItemContext_SingleRow;
else
gTasks[taskId].func = Task_ItemContext_MultipleRows;
}
#define tSortType data[2]
static void ItemMenu_SortByName(u8 taskId)
{
gTasks[taskId].tSortType = SORT_ALPHABETICALLY;
StringCopy(gStringVar1, sSortTypeStrings[SORT_ALPHABETICALLY]);
gTasks[taskId].func = SortBagItems;
}
static void ItemMenu_SortByType(u8 taskId)
{
gTasks[taskId].tSortType = SORT_BY_TYPE;
StringCopy(gStringVar1, sSortTypeStrings[SORT_BY_TYPE]);
gTasks[taskId].func = SortBagItems;
}
static void ItemMenu_SortByAmount(u8 taskId)
{
gTasks[taskId].tSortType = SORT_BY_AMOUNT; //greatest->least
StringCopy(gStringVar1, sSortTypeStrings[SORT_BY_AMOUNT]);
gTasks[taskId].func = SortBagItems;
}
static void ItemMenu_SortByIndex(u8 taskId)
{
gTasks[taskId].tSortType = SORT_BY_INDEX;
StringCopy(gStringVar1, sSortTypeStrings[SORT_BY_INDEX]);
gTasks[taskId].func = SortBagItems;
}
static void SortBagItems(u8 taskId)
{
s16 *data = gTasks[taskId].data;
u16 *scrollPos = &gBagPosition.scrollPosition[gBagPosition.pocket];
u16 *cursorPos = &gBagPosition.cursorPosition[gBagPosition.pocket];
RemoveContextWindow();
SortItemsInBag(&gBagPockets[gBagPosition.pocket], tSortType);
DestroyListMenuTask(data[0], scrollPos, cursorPos);
UpdatePocketListPosition(gBagPosition.pocket);
LoadBagItemListBuffers(gBagPosition.pocket);
data[0] = ListMenuInit(&gMultiuseListMenuTemplate, *scrollPos, *cursorPos);
ScheduleBgCopyTilemapToVram(0);
StringCopy(gStringVar1, sSortTypeStrings[tSortType]);
StringExpandPlaceholders(gStringVar4, sText_ItemsSorted);
DisplayItemMessage(taskId, 1, gStringVar4, Task_SortFinish);
}
#undef tSortType
static void Task_SortFinish(u8 taskId)
{
if (gMain.newKeys & (A_BUTTON | B_BUTTON))
{
RemoveItemMessageWindow(4);
ReturnToItemList(taskId);
}
}
void SortItemsInBag(struct BagPocket *pocket, enum BagSortOptions type)
{
switch (type)
{
case SORT_ALPHABETICALLY:
MergeSort(pocket, CompareItemsAlphabetically);
break;
case SORT_BY_AMOUNT:
MergeSort(pocket, CompareItemsByMost);
break;
case SORT_BY_INDEX:
MergeSort(pocket, CompareItemsByIndex);
break;
default:
MergeSort(pocket, CompareItemsByType);
break;
}
}
static inline __attribute__((always_inline)) void Merge(struct BagPocket *pocket, u32 iLeft, u32 iRight, u32 iEnd, struct ItemSlot *dummySlots, s32 (*comparator)(enum Pocket, struct ItemSlot, struct ItemSlot))
{
struct ItemSlot item_i, item_j;
u32 i = iLeft, j = iRight;
for (u32 k = iLeft; k < iEnd; k++)
{
item_i = BagPocket_GetSlotData(pocket, i);
item_j = BagPocket_GetSlotData(pocket, j);
if (i < iRight && (j >= iEnd || comparator(pocket->id, item_i, item_j) < 0))
{
dummySlots[k] = item_i;
i++;
}
else
{
dummySlots[k] = item_j;
j++;
}
}
}
// Source: https://en.wikipedia.org/wiki/Merge_sort#Bottom-up_implementation
static void MergeSort(struct BagPocket *pocket, s32 (*comparator)(enum Pocket, struct ItemSlot, struct ItemSlot))
{
struct ItemSlot *dummySlots = AllocZeroed(sizeof(struct ItemSlot) * pocket->capacity);
for (u32 width = 1; width < pocket->capacity; width *= 2)
{
for (u32 i = 0; i < pocket->capacity; i += 2 * width)
Merge(pocket, i, min(i + width, pocket->capacity), min(i + 2 * width, pocket->capacity), dummySlots, comparator);
for (u32 j = 0; j < pocket->capacity; j++)
BagPocket_SetSlotData(pocket, j, dummySlots[j]);
}
Free(dummySlots);
}
static s32 CompareItemsAlphabetically(enum Pocket pocketId, struct ItemSlot item1, struct ItemSlot item2)
{
const u8 *name1, *name2;
if (item1.itemId == ITEM_NONE)
return 1;
else if (item2.itemId == ITEM_NONE)
return -1;
if (pocketId == POCKET_TM_HM)
{
name1 = gMovesInfo[GetTMHMMoveId(GetItemTMHMIndex(item1.itemId))].name;
name2 = gMovesInfo[GetTMHMMoveId(GetItemTMHMIndex(item2.itemId))].name;
}
else
{
name1 = GetItemName(item1.itemId);
name2 = GetItemName(item2.itemId);
}
for (u32 i = 0; ; ++i)
{
if (name1[i] == EOS && name2[i] != EOS)
return -1;
else if (name1[i] != EOS && name2[i] == EOS)
return 1;
else if (name1[i] == EOS && name2[i] == EOS)
return 0;
if (name1[i] < name2[i])
return -1;
else if (name1[i] > name2[i])
return 1;
}
return 0; // Will never be reached
}
static s32 CompareItemsByMost(enum Pocket pocketId, struct ItemSlot item1, struct ItemSlot item2)
{
if (item1.itemId == ITEM_NONE)
return 1;
else if (item2.itemId == ITEM_NONE)
return -1;
if (item1.quantity < item2.quantity)
return 1;
else if (item1.quantity > item2.quantity)
return -1;
return CompareItemsAlphabetically(pocketId, item1, item2); // Items have same quantity so sort alphabetically
}
static s32 CompareItemsByType(enum Pocket pocketId, struct ItemSlot item1, struct ItemSlot item2)
{
enum ItemSortType type1 = gItemsInfo[item1.itemId].sortType;
enum ItemSortType type2 = gItemsInfo[item2.itemId].sortType;
// Null and uncategorized items go last
if (type1 && !type2)
return -1;
else if (type2 && !type1)
return 1;
else if (type1 < type2)
return -1;
else if (type1 > type2)
return 1;
return CompareItemsAlphabetically(pocketId, item1, item2); // Items are of same type so sort alphabetically
}
static s32 CompareItemsByIndex(enum Pocket pocketId, struct ItemSlot item1, struct ItemSlot item2)
{
u16 index1 = 0, index2 = 0;
if (item1.itemId == ITEM_NONE)
return 1;
else if (item2.itemId == ITEM_NONE)
return -1;
switch (pocketId)
{
case POCKET_TM_HM:
index1 = GetItemTMHMIndex(item1.itemId);
index2 = GetItemTMHMIndex(item2.itemId);
break;
case POCKET_BERRIES: // To do - requires #7305
index1 = item1.itemId;
index2 = item2.itemId;
break;
default:
return 0;
}
if (index1 < index2)
return -1;
else if (index1 > index2)
return 1;
return 0; // Cannot have multiple stacks of indexed items
}

View File

@ -322,13 +322,12 @@ bool8 MenuHelpers_ShouldWaitForLinkRecv(void)
void SetItemListPerPageCount(struct ItemSlot *slots, u8 slotsCount, u8 *pageItems, u8 *totalItems, u8 maxPerPage)
{
u16 i;
struct ItemSlot *slots_ = slots;
// Count the number of non-empty item slots
*totalItems = 0;
for (i = 0; i < slotsCount; i++)
{
if (slots_[i].itemId != ITEM_NONE)
if (slots[i].itemId != ITEM_NONE)
(*totalItems)++;
}
(*totalItems)++; // + 1 for 'Cancel'

View File

@ -1,7 +1,7 @@
#include "global.h"
#include "battle.h"
#include "event_data.h"
#include "item.h"
#include "item_menu.h"
#include "pokemon.h"
#include "test/overworld_script.h"
#include "test/test.h"
@ -19,6 +19,11 @@ TEST("TMs and HMs are sorted correctly in the bag")
ASSUME(GetItemPocket(ITEM_TM01) == POCKET_TM_HM);
ASSUME(GetItemPocket(ITEM_HM02) == POCKET_TM_HM);
/*
* Note: I would add a test to make sure that TMs are sorted correctly by move name,
* but downstream users are likely to rearrange TMs so this would just be a nuisance.
*/
RUN_OVERWORLD_SCRIPT(
additem ITEM_HM07;
additem ITEM_TM25;
@ -30,7 +35,7 @@ TEST("TMs and HMs are sorted correctly in the bag")
additem ITEM_HM02;
);
SortPocket(POCKET_TM_HM, SORT_POCKET_TM_HM);
SortItemsInBag(&gBagPockets[POCKET_TM_HM], SORT_BY_INDEX);
EXPECT_EQ(pocket->itemSlots[0].itemId, ITEM_TM01);
EXPECT_EQ(pocket->itemSlots[1].itemId, ITEM_TM05);
@ -67,7 +72,7 @@ TEST("Berries are sorted correctly in the bag")
additem ITEM_CHERI_BERRY;
);
SortPocket(POCKET_BERRIES, SORT_POCKET_BY_ITEM_ID);
SortItemsInBag(&gBagPockets[POCKET_BERRIES], SORT_BY_INDEX);
EXPECT_EQ(pocket->itemSlots[0].itemId, ITEM_CHERI_BERRY);
EXPECT_EQ(pocket->itemSlots[1].itemId, ITEM_ORAN_BERRY);
@ -78,9 +83,21 @@ TEST("Berries are sorted correctly in the bag")
EXPECT_EQ(pocket->itemSlots[6].itemId, ITEM_GANLON_BERRY);
EXPECT_EQ(pocket->itemSlots[7].itemId, ITEM_MICLE_BERRY);
EXPECT_EQ(pocket->itemSlots[8].itemId, ITEM_NONE);
SortItemsInBag(&gBagPockets[POCKET_BERRIES], SORT_ALPHABETICALLY);
EXPECT_EQ(pocket->itemSlots[0].itemId, ITEM_CHARTI_BERRY);
EXPECT_EQ(pocket->itemSlots[1].itemId, ITEM_CHERI_BERRY);
EXPECT_EQ(pocket->itemSlots[2].itemId, ITEM_GANLON_BERRY);
EXPECT_EQ(pocket->itemSlots[3].itemId, ITEM_KELPSY_BERRY);
EXPECT_EQ(pocket->itemSlots[4].itemId, ITEM_MAGOST_BERRY);
EXPECT_EQ(pocket->itemSlots[5].itemId, ITEM_MICLE_BERRY);
EXPECT_EQ(pocket->itemSlots[6].itemId, ITEM_ORAN_BERRY);
EXPECT_EQ(pocket->itemSlots[7].itemId, ITEM_POMEG_BERRY);
EXPECT_EQ(pocket->itemSlots[8].itemId, ITEM_NONE);
}
TEST("Items are correctly compacted in the bag")
TEST("Items are correctly sorted and compacted in the bag")
{
struct BagPocket *pocket = &gBagPockets[POCKET_ITEMS];
memset(pocket->itemSlots, 0, sizeof(gSaveBlock1Ptr->bag.items));
@ -115,21 +132,31 @@ TEST("Items are correctly compacted in the bag")
EXPECT_EQ(pocket->itemSlots[5].quantity, 1);
EXPECT_EQ(pocket->itemSlots[6].itemId, ITEM_NONE);
// Try removing the small items, check that everything is compacted correctly
SortItemsInBag(&gBagPockets[POCKET_ITEMS], SORT_ALPHABETICALLY);
EXPECT_EQ(pocket->itemSlots[0].itemId, ITEM_BIG_MUSHROOM);
EXPECT_EQ(pocket->itemSlots[1].itemId, ITEM_BIG_NUGGET);
EXPECT_EQ(pocket->itemSlots[2].itemId, ITEM_BIG_PEARL);
EXPECT_EQ(pocket->itemSlots[3].itemId, ITEM_NUGGET);
EXPECT_EQ(pocket->itemSlots[4].itemId, ITEM_PEARL);
EXPECT_EQ(pocket->itemSlots[5].itemId, ITEM_TINY_MUSHROOM);
EXPECT_EQ(pocket->itemSlots[6].itemId, ITEM_NONE);
// Try removing the big items, check that everything is compacted correctly
RUN_OVERWORLD_SCRIPT(
removeitem ITEM_NUGGET;
removeitem ITEM_TINY_MUSHROOM;
removeitem ITEM_PEARL;
removeitem ITEM_BIG_NUGGET;
removeitem ITEM_BIG_MUSHROOM;
removeitem ITEM_BIG_PEARL;
);
CompactItemsInBagPocket(POCKET_ITEMS);
EXPECT_EQ(pocket->itemSlots[0].itemId, ITEM_BIG_NUGGET);
EXPECT_EQ(pocket->itemSlots[0].itemId, ITEM_NUGGET);
EXPECT_EQ(pocket->itemSlots[0].quantity, 1);
EXPECT_EQ(pocket->itemSlots[1].itemId, ITEM_BIG_MUSHROOM);
EXPECT_EQ(pocket->itemSlots[1].itemId, ITEM_PEARL);
EXPECT_EQ(pocket->itemSlots[1].quantity, 1);
EXPECT_EQ(pocket->itemSlots[2].itemId, ITEM_BIG_PEARL);
EXPECT_EQ(pocket->itemSlots[2].itemId, ITEM_TINY_MUSHROOM);
EXPECT_EQ(pocket->itemSlots[2].quantity, 1);
EXPECT_EQ(pocket->itemSlots[3].itemId, ITEM_NONE);
EXPECT_EQ(pocket->itemSlots[4].itemId, ITEM_NONE);