Bag refactor3 + Ghoulslash's sorting feature port (#7330)
This commit is contained in:
parent
689bf0bcbc
commit
2eeb346387
@ -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))
|
||||
|
||||
@ -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);
|
||||
|
||||
@ -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
|
||||
|
||||
550
src/data/items.h
550
src/data/items.h
File diff suppressed because it is too large
Load Diff
244
src/item.c
244
src/item.c
@ -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;
|
||||
|
||||
397
src/item_menu.c
397
src/item_menu.c
@ -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
|
||||
}
|
||||
|
||||
@ -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'
|
||||
|
||||
49
test/bag.c
49
test/bag.c
@ -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);
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user