pokeemmo/src/item.c

951 lines
28 KiB
C

#include "global.h"
#include "item.h"
#include "berry.h"
#include "pokeball.h"
#include "string_util.h"
#include "text.h"
#include "event_data.h"
#include "malloc.h"
#include "secret_base.h"
#include "item_menu.h"
#include "party_menu.h"
#include "strings.h"
#include "load_save.h"
#include "item_use.h"
#include "battle_pyramid.h"
#include "battle_pyramid_bag.h"
#include "graphics.h"
#include "constants/battle.h"
#include "constants/items.h"
#include "constants/moves.h"
#include "constants/item_effects.h"
#include "constants/hold_effects.h"
#define DUMMY_PC_BAG_POCKET \
{ \
.id = POCKET_DUMMY, \
.capacity = PC_ITEMS_COUNT, \
.itemSlots = gSaveBlock1Ptr->pcItems, \
}
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 NONNULL BagPocket_CompactItems(struct BagPocket *pocket);
EWRAM_DATA struct BagPocket gBagPockets[POCKETS_COUNT] = {0};
#include "data/pokemon/item_effects.h"
#include "data/items.h"
#define UNPACK_TM_ITEM_ID(_tm) [CAT(ENUM_TM_HM_, _tm) + 1] = { CAT(ITEM_TM_, _tm), CAT(MOVE_, _tm) },
#define UNPACK_HM_ITEM_ID(_hm) [CAT(ENUM_TM_HM_, _hm) + 1] = { CAT(ITEM_HM_, _hm), CAT(MOVE_, _hm) },
const struct TmHmIndexKey gTMHMItemMoveIds[NUM_ALL_MACHINES + 1] =
{
[0] = { ITEM_NONE, MOVE_NONE }, // Failsafe
FOREACH_TM(UNPACK_TM_ITEM_ID)
FOREACH_HM(UNPACK_HM_ITEM_ID)
/*
* Expands to the following:
*
* [1] = { ITEM_TM_FOCUS_PUNCH, MOVE_FOCUS_PUNCH },
* [2] = { ITEM_TM_DRAGON_CLAW, MOVE_DRAGON_CLAW },
* [3] = { ITEM_TM_WATER_PULSE, MOVE_WATER_PULSE },
* etc etc
*/
};
#undef UNPACK_TM_ITEM_ID
#undef UNPACK_HM_ITEM_ID
static inline struct ItemSlot NONNULL BagPocket_GetSlotDataGeneric(struct BagPocket *pocket, u32 pocketPos)
{
return (struct ItemSlot) {
.itemId = pocket->itemSlots[pocketPos].itemId,
.quantity = pocket->itemSlots[pocketPos].quantity ^ gSaveBlock2Ptr->encryptionKey,
};
}
static inline struct ItemSlot NONNULL BagPocket_GetSlotDataPC(struct BagPocket *pocket, u32 pocketPos)
{
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)
{
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);
}
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
{
newSlot.itemId = ITEM_NONE;
newSlot.quantity = 0;
}
switch (pocket->id)
{
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;
}
}
void ApplyNewEncryptionKeyToBagItems(u32 newKey)
{
enum Pocket pocketId;
u32 item;
for (pocketId = 0; pocketId < POCKETS_COUNT; pocketId++)
{
for (item = 0; item < gBagPockets[pocketId].capacity; item++)
ApplyNewEncryptionKeyToHword(&(gBagPockets[pocketId].itemSlots[item].quantity), newKey);
}
}
void SetBagItemsPointers(void)
{
gBagPockets[POCKET_ITEMS].itemSlots = gSaveBlock1Ptr->bag.items;
gBagPockets[POCKET_ITEMS].capacity = BAG_ITEMS_COUNT;
gBagPockets[POCKET_ITEMS].id = POCKET_ITEMS;
gBagPockets[POCKET_KEY_ITEMS].itemSlots = gSaveBlock1Ptr->bag.keyItems;
gBagPockets[POCKET_KEY_ITEMS].capacity = BAG_KEYITEMS_COUNT;
gBagPockets[POCKET_KEY_ITEMS].id = POCKET_KEY_ITEMS;
gBagPockets[POCKET_POKE_BALLS].itemSlots = gSaveBlock1Ptr->bag.pokeBalls;
gBagPockets[POCKET_POKE_BALLS].capacity = BAG_POKEBALLS_COUNT;
gBagPockets[POCKET_POKE_BALLS].id = POCKET_POKE_BALLS;
gBagPockets[POCKET_TM_HM].itemSlots = gSaveBlock1Ptr->bag.TMsHMs;
gBagPockets[POCKET_TM_HM].capacity = BAG_TMHM_COUNT;
gBagPockets[POCKET_TM_HM].id = POCKET_TM_HM;
gBagPockets[POCKET_BERRIES].itemSlots = gSaveBlock1Ptr->bag.berries;
gBagPockets[POCKET_BERRIES].capacity = BAG_BERRIES_COUNT;
gBagPockets[POCKET_BERRIES].id = POCKET_BERRIES;
}
u8 *CopyItemName(u16 itemId, u8 *dst)
{
return StringCopy(dst, GetItemName(itemId));
}
const u8 sText_s[] =_("s");
u8 *CopyItemNameHandlePlural(u16 itemId, u8 *dst, u32 quantity)
{
if (quantity == 1)
{
return StringCopy(dst, GetItemName(itemId));
}
else if (DoesItemHavePluralName(itemId))
{
return StringCopy(dst, GetItemPluralName(itemId));
}
else
{
u8 *end = StringCopy(dst, GetItemName(itemId));
return StringCopy(end, sText_s);
}
}
bool32 IsBagPocketNonEmpty(enum Pocket pocketId)
{
u8 i;
for (i = 0; i < gBagPockets[pocketId].capacity; i++)
{
if (GetBagItemId(pocketId, i) != ITEM_NONE)
return TRUE;
}
return FALSE;
}
static bool32 NONNULL BagPocket_CheckHasItem(struct BagPocket *pocket, u16 itemId, u16 count)
{
struct ItemSlot tempItem;
// Check for item slots that contain the item
for (u32 i = 0; i < pocket->capacity && count > 0; i++)
{
tempItem = BagPocket_GetSlotData(pocket, i);
if (tempItem.itemId == itemId)
count -= min(count, tempItem.quantity);
}
return count == 0;
}
bool32 CheckBagHasItem(u16 itemId, u16 count)
{
if (GetItemPocket(itemId) >= POCKETS_COUNT)
return FALSE;
if (CurrentBattlePyramidLocation() != PYRAMID_LOCATION_NONE || FlagGet(FLAG_STORING_ITEMS_IN_PYRAMID_BAG) == TRUE)
return CheckPyramidBagHasItem(itemId, count);
return BagPocket_CheckHasItem(&gBagPockets[GetItemPocket(itemId)], itemId, count);
}
bool32 HasAtLeastOneBerry(void)
{
gSpecialVar_Result = FALSE;
for (u32 i = FIRST_BERRY_INDEX; i <= LAST_BERRY_INDEX && gSpecialVar_Result == FALSE; i++)
gSpecialVar_Result = CheckBagHasItem(i, 1);
return gSpecialVar_Result;
}
bool32 HasAtLeastOnePokeBall(void)
{
for (enum PokeBall ballId = BALL_STRANGE; ballId < POKEBALL_COUNT; ballId++)
{
if (CheckBagHasItem(gBallItemIds[ballId], 1) == TRUE)
return TRUE;
}
return FALSE;
}
bool32 CheckBagHasSpace(u16 itemId, u16 count)
{
if (GetItemPocket(itemId) >= POCKETS_COUNT)
return FALSE;
if (CurrentBattlePyramidLocation() != PYRAMID_LOCATION_NONE || FlagGet(FLAG_STORING_ITEMS_IN_PYRAMID_BAG) == TRUE)
return CheckPyramidBagHasSpace(itemId, count);
return GetFreeSpaceForItemInBag(itemId) >= count;
}
static u32 NONNULL BagPocket_GetFreeSpaceForItem(struct BagPocket *pocket, u16 itemId)
{
u32 spaceForItem = 0;
struct ItemSlot tempItem;
// Check space in any existing item slots that already contain this item
for (u32 i = 0; i < pocket->capacity; i++)
{
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;
}
u32 GetFreeSpaceForItemInBag(u16 itemId)
{
if (GetItemPocket(itemId) >= POCKETS_COUNT)
return 0;
return BagPocket_GetFreeSpaceForItem(&gBagPockets[GetItemPocket(itemId)], itemId);
}
static inline bool32 NONNULL CheckSlotAndUpdateCount(struct BagPocket *pocket, u16 itemId, u32 pocketPos, u32 *nextPocketPos, u16 *count, u16 *tempPocketSlotQuantities)
{
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 (tempItem.itemId == ITEM_NONE)
tempItem.quantity = 0;
// Record slot quantity in tempPocketSlotQuantities, adjust count
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)
*nextPocketPos = pocketPos + 1;
return TRUE;
}
return FALSE;
}
static bool32 NONNULL BagPocket_AddItem(struct BagPocket *pocket, u16 itemId, u16 count)
{
u32 itemLookupIndex, itemAddIndex = 0;
// First, check that there is a free slot for this item
u16 *tempPocketSlotQuantities = AllocZeroed(sizeof(u16) * pocket->capacity);
switch (pocket->id)
{
case POCKET_TM_HM:
case POCKET_BERRIES:
for (itemLookupIndex = 0; itemLookupIndex < pocket->capacity && count > 0; itemLookupIndex++)
{
// Check if we found a slot to store the item but weren't able to reduce count to 0
// This means that we have more than one stack's worth, which isn't allowed in these pockets
if (CheckSlotAndUpdateCount(pocket, itemId, itemLookupIndex, &itemAddIndex, &count, tempPocketSlotQuantities) && count > 0)
{
Free(tempPocketSlotQuantities);
return FALSE;
}
}
break;
default:
for (itemLookupIndex = 0; itemLookupIndex < pocket->capacity && count > 0; itemLookupIndex++)
CheckSlotAndUpdateCount(pocket, itemId, itemLookupIndex, &itemAddIndex, &count, tempPocketSlotQuantities);
}
// If the count is still greater than zero, clearly we have not found enough slots for this...
// Otherwise, we have found slots - update the actual pockets with the updated quantities
if (count == 0)
{
for (--itemAddIndex; itemAddIndex < itemLookupIndex; itemAddIndex++)
{
if (tempPocketSlotQuantities[itemAddIndex] > 0)
BagPocket_SetSlotItemIdAndCount(pocket, itemAddIndex, itemId, tempPocketSlotQuantities[itemAddIndex]);
}
}
Free(tempPocketSlotQuantities);
return count == 0;
}
bool32 AddBagItem(u16 itemId, u16 count)
{
if (GetItemPocket(itemId) >= POCKETS_COUNT)
return FALSE;
// check Battle Pyramid Bag
if (CurrentBattlePyramidLocation() != PYRAMID_LOCATION_NONE || FlagGet(FLAG_STORING_ITEMS_IN_PYRAMID_BAG) == TRUE)
return AddPyramidBagItem(itemId, count);
return BagPocket_AddItem(&gBagPockets[GetItemPocket(itemId)], itemId, count);
}
static bool32 NONNULL BagPocket_RemoveItem(struct BagPocket *pocket, u16 itemId, u16 count)
{
u32 itemLookupIndex, itemRemoveIndex = 0, totalQuantity = 0;
struct ItemSlot tempItem;
u16 *tempPocketSlotQuantities = AllocZeroed(sizeof(u16) * pocket->capacity);
for (itemLookupIndex = 0; itemLookupIndex < pocket->capacity && totalQuantity < count; itemLookupIndex++)
{
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 += tempItem.quantity;
tempPocketSlotQuantities[itemLookupIndex] = (tempItem.quantity <= count ? 0 : tempItem.quantity - count) + 1;
}
}
if (totalQuantity >= count) // We have enough of the item
{
if (CurMapIsSecretBase() == TRUE)
{
VarSet(VAR_SECRET_BASE_LOW_TV_FLAGS, VarGet(VAR_SECRET_BASE_LOW_TV_FLAGS) | SECRET_BASE_USED_BAG);
VarSet(VAR_SECRET_BASE_LAST_ITEM_USED, itemId);
}
// Update the quantities correctly with the items removed
for (--itemRemoveIndex; itemRemoveIndex < itemLookupIndex; itemRemoveIndex++)
{
if (tempPocketSlotQuantities[itemRemoveIndex] > 0)
BagPocket_SetSlotItemIdAndCount(pocket, itemRemoveIndex, itemId, tempPocketSlotQuantities[itemRemoveIndex] - 1);
}
}
if (totalQuantity == count)
BagPocket_CompactItems(pocket);
Free(tempPocketSlotQuantities);
return totalQuantity >= count;
}
bool32 RemoveBagItem(u16 itemId, u16 count)
{
if (GetItemPocket(itemId) >= POCKETS_COUNT || itemId == ITEM_NONE)
return FALSE;
// check Battle Pyramid Bag
if (CurrentBattlePyramidLocation() != PYRAMID_LOCATION_NONE || FlagGet(FLAG_STORING_ITEMS_IN_PYRAMID_BAG) == TRUE)
return RemovePyramidBagItem(itemId, count);
return BagPocket_RemoveItem(&gBagPockets[GetItemPocket(itemId)], itemId, count);
}
// Unsafe function: Only use with functions that already check the slot and count are valid
void RemoveBagItemFromSlot(struct BagPocket *pocket, u16 slotId, u16 count)
{
struct ItemSlot itemSlot = BagPocket_GetSlotData(pocket, slotId);
BagPocket_SetSlotItemIdAndCount(pocket, slotId, itemSlot.itemId, itemSlot.quantity - count);
}
static u8 NONNULL BagPocket_CountUsedItemSlots(struct BagPocket *pocket)
{
u8 usedSlots = 0;
for (u32 i = 0; i < pocket->capacity; i++)
{
if (BagPocket_GetSlotData(pocket, i).itemId != ITEM_NONE)
usedSlots++;
}
return usedSlots;
}
u8 CountUsedPCItemSlots(void)
{
struct BagPocket dummyPocket = DUMMY_PC_BAG_POCKET;
return BagPocket_CountUsedItemSlots(&dummyPocket);
}
static bool32 NONNULL BagPocket_CheckPocketForItemCount(struct BagPocket *pocket, u16 itemId, u16 count)
{
struct ItemSlot tempItem;
for (u32 i = 0; i < pocket->capacity; i++)
{
tempItem = BagPocket_GetSlotData(pocket, i);
if (tempItem.itemId == itemId && tempItem.quantity >= count)
return TRUE;
}
return FALSE;
}
bool32 CheckPCHasItem(u16 itemId, u16 count)
{
struct BagPocket dummyPocket = DUMMY_PC_BAG_POCKET;
return BagPocket_CheckPocketForItemCount(&dummyPocket, itemId, count);
}
bool32 AddPCItem(u16 itemId, u16 count)
{
struct BagPocket dummyPocket = DUMMY_PC_BAG_POCKET;
return BagPocket_AddItem(&dummyPocket, itemId, count);
}
static void NONNULL BagPocket_CompactItems(struct BagPocket *pocket)
{
struct ItemSlot tempItem;
u32 slotCursor = 0;
for (u32 i = 0; i < pocket->capacity; i++)
{
tempItem = BagPocket_GetSlotData(pocket, i);
if (tempItem.itemId == ITEM_NONE)
{
if (!slotCursor)
slotCursor = i + 1;
}
else if (slotCursor > 0)
{
BagPocket_SetSlotData(pocket, slotCursor++ - 1, tempItem);
BagPocket_SetSlotItemIdAndCount(pocket, i, ITEM_NONE, 0);
}
}
}
void RemovePCItem(u8 index, u16 count)
{
struct BagPocket dummyPocket = DUMMY_PC_BAG_POCKET;
// Get id, quantity at slot
struct ItemSlot tempItem = BagPocket_GetSlotData(&dummyPocket, index);
// Remove quantity
BagPocket_SetSlotItemIdAndCount(&dummyPocket, index, tempItem.itemId, tempItem.quantity - count);
// Compact if necessary
if (tempItem.quantity == 0)
BagPocket_CompactItems(&dummyPocket);
}
void CompactPCItems(void)
{
struct BagPocket dummyPocket = DUMMY_PC_BAG_POCKET;
BagPocket_CompactItems(&dummyPocket);
}
void SwapRegisteredBike(void)
{
switch (gSaveBlock1Ptr->registeredItem)
{
case ITEM_MACH_BIKE:
gSaveBlock1Ptr->registeredItem = ITEM_ACRO_BIKE;
break;
case ITEM_ACRO_BIKE:
gSaveBlock1Ptr->registeredItem = ITEM_MACH_BIKE;
break;
}
}
void CompactItemsInBagPocket(enum Pocket pocketId)
{
BagPocket_CompactItems(&gBagPockets[pocketId]);
}
static inline void NONNULL BagPocket_MoveItemSlot(struct BagPocket *pocket, u32 from, u32 to)
{
if (from != to)
{
s8 shift = (to > from) ? 1 : -1;
if (to > from)
to--;
// Record the values at "from"
struct ItemSlot fromSlot = BagPocket_GetSlotData(pocket, from);
// Shuffle items between "to" and "from"
for (u32 i = from; i != to; i += shift)
BagPocket_SetSlotData(pocket, i, BagPocket_GetSlotData(pocket, i + shift));
// Move the saved "from" to "to"
BagPocket_SetSlotData(pocket, to, fromSlot);
}
}
void MoveItemSlotInPocket(enum Pocket pocketId, u32 from, u32 to)
{
BagPocket_MoveItemSlot(&gBagPockets[pocketId], from, to);
}
void MoveItemSlotInPC(struct ItemSlot *itemSlots, u32 from, u32 to)
{
struct BagPocket dummyPocket = DUMMY_PC_BAG_POCKET;
return BagPocket_MoveItemSlot(&dummyPocket, from, to);
}
void ClearBag(void)
{
CpuFastFill(0, &gSaveBlock1Ptr->bag, sizeof(struct Bag));
}
static inline u16 NONNULL BagPocket_CountTotalItemQuantity(struct BagPocket *pocket, u16 itemId)
{
u32 ownedCount = 0;
struct ItemSlot tempItem;
for (u32 i = 0; i < pocket->capacity; i++)
{
tempItem = BagPocket_GetSlotData(pocket, i);
if (tempItem.itemId == itemId)
ownedCount += tempItem.quantity;
}
return ownedCount;
}
u16 CountTotalItemQuantityInBag(u16 itemId)
{
return BagPocket_CountTotalItemQuantity(&gBagPockets[GetItemPocket(itemId)], itemId);
}
static bool32 CheckPyramidBagHasItem(u16 itemId, u16 count)
{
u8 i;
u16 *items = gSaveBlock2Ptr->frontier.pyramidBag.itemId[gSaveBlock2Ptr->frontier.lvlMode];
#if MAX_PYRAMID_BAG_ITEM_CAPACITY > 255
u16 *quantities = gSaveBlock2Ptr->frontier.pyramidBag.quantity[gSaveBlock2Ptr->frontier.lvlMode];
#else
u8 *quantities = gSaveBlock2Ptr->frontier.pyramidBag.quantity[gSaveBlock2Ptr->frontier.lvlMode];
#endif
for (i = 0; i < PYRAMID_BAG_ITEMS_COUNT; i++)
{
if (items[i] == itemId)
{
if (quantities[i] >= count)
return TRUE;
count -= quantities[i];
if (count == 0)
return TRUE;
}
}
return FALSE;
}
static bool32 CheckPyramidBagHasSpace(u16 itemId, u16 count)
{
u8 i;
u16 *items = gSaveBlock2Ptr->frontier.pyramidBag.itemId[gSaveBlock2Ptr->frontier.lvlMode];
#if MAX_PYRAMID_BAG_ITEM_CAPACITY > 255
u16 *quantities = gSaveBlock2Ptr->frontier.pyramidBag.quantity[gSaveBlock2Ptr->frontier.lvlMode];
#else
u8 *quantities = gSaveBlock2Ptr->frontier.pyramidBag.quantity[gSaveBlock2Ptr->frontier.lvlMode];
#endif
for (i = 0; i < PYRAMID_BAG_ITEMS_COUNT; i++)
{
if (items[i] == itemId || items[i] == ITEM_NONE)
{
if (quantities[i] + count <= MAX_PYRAMID_BAG_ITEM_CAPACITY)
return TRUE;
count = (quantities[i] + count) - MAX_PYRAMID_BAG_ITEM_CAPACITY;
if (count == 0)
return TRUE;
}
}
return FALSE;
}
bool32 AddPyramidBagItem(u16 itemId, u16 count)
{
u16 i;
u16 *items = gSaveBlock2Ptr->frontier.pyramidBag.itemId[gSaveBlock2Ptr->frontier.lvlMode];
u16 *newItems = Alloc(PYRAMID_BAG_ITEMS_COUNT * sizeof(*newItems));
#if MAX_PYRAMID_BAG_ITEM_CAPACITY > 255
u16 *quantities = gSaveBlock2Ptr->frontier.pyramidBag.quantity[gSaveBlock2Ptr->frontier.lvlMode];
u16 *newQuantities = Alloc(PYRAMID_BAG_ITEMS_COUNT * sizeof(*newQuantities));
#else
u8 *quantities = gSaveBlock2Ptr->frontier.pyramidBag.quantity[gSaveBlock2Ptr->frontier.lvlMode];
u8 *newQuantities = Alloc(PYRAMID_BAG_ITEMS_COUNT * sizeof(*newQuantities));
#endif
memcpy(newItems, items, PYRAMID_BAG_ITEMS_COUNT * sizeof(*newItems));
memcpy(newQuantities, quantities, PYRAMID_BAG_ITEMS_COUNT * sizeof(*newQuantities));
for (i = 0; i < PYRAMID_BAG_ITEMS_COUNT; i++)
{
if (newItems[i] == itemId && newQuantities[i] < MAX_PYRAMID_BAG_ITEM_CAPACITY)
{
newQuantities[i] += count;
if (newQuantities[i] > MAX_PYRAMID_BAG_ITEM_CAPACITY)
{
count = newQuantities[i] - MAX_PYRAMID_BAG_ITEM_CAPACITY;
newQuantities[i] = MAX_PYRAMID_BAG_ITEM_CAPACITY;
}
else
{
count = 0;
}
if (count == 0)
break;
}
}
if (count > 0)
{
for (i = 0; i < PYRAMID_BAG_ITEMS_COUNT; i++)
{
if (newItems[i] == ITEM_NONE)
{
newItems[i] = itemId;
newQuantities[i] = count;
if (newQuantities[i] > MAX_PYRAMID_BAG_ITEM_CAPACITY)
{
count = newQuantities[i] - MAX_PYRAMID_BAG_ITEM_CAPACITY;
newQuantities[i] = MAX_PYRAMID_BAG_ITEM_CAPACITY;
}
else
{
count = 0;
}
if (count == 0)
break;
}
}
}
if (count == 0)
{
memcpy(items, newItems, PYRAMID_BAG_ITEMS_COUNT * sizeof(*items));
memcpy(quantities, newQuantities, PYRAMID_BAG_ITEMS_COUNT * sizeof(*quantities));
Free(newItems);
Free(newQuantities);
return TRUE;
}
else
{
Free(newItems);
Free(newQuantities);
return FALSE;
}
}
bool32 RemovePyramidBagItem(u16 itemId, u16 count)
{
u16 i;
u16 *items = gSaveBlock2Ptr->frontier.pyramidBag.itemId[gSaveBlock2Ptr->frontier.lvlMode];
#if MAX_PYRAMID_BAG_ITEM_CAPACITY > 255
u16 *quantities = gSaveBlock2Ptr->frontier.pyramidBag.quantity[gSaveBlock2Ptr->frontier.lvlMode];
#else
u8 *quantities = gSaveBlock2Ptr->frontier.pyramidBag.quantity[gSaveBlock2Ptr->frontier.lvlMode];
#endif
i = gPyramidBagMenuState.cursorPosition + gPyramidBagMenuState.scrollPosition;
if (items[i] == itemId && quantities[i] >= count)
{
quantities[i] -= count;
if (quantities[i] == 0)
items[i] = ITEM_NONE;
return TRUE;
}
else
{
u16 *newItems = Alloc(PYRAMID_BAG_ITEMS_COUNT * sizeof(*newItems));
#if MAX_PYRAMID_BAG_ITEM_CAPACITY > 255
u16 *newQuantities = Alloc(PYRAMID_BAG_ITEMS_COUNT * sizeof(*newQuantities));
#else
u8 *newQuantities = Alloc(PYRAMID_BAG_ITEMS_COUNT * sizeof(*newQuantities));
#endif
memcpy(newItems, items, PYRAMID_BAG_ITEMS_COUNT * sizeof(*newItems));
memcpy(newQuantities, quantities, PYRAMID_BAG_ITEMS_COUNT * sizeof(*newQuantities));
for (i = 0; i < PYRAMID_BAG_ITEMS_COUNT; i++)
{
if (newItems[i] == itemId)
{
if (newQuantities[i] >= count)
{
newQuantities[i] -= count;
count = 0;
if (newQuantities[i] == 0)
newItems[i] = ITEM_NONE;
}
else
{
count -= newQuantities[i];
newQuantities[i] = 0;
newItems[i] = ITEM_NONE;
}
if (count == 0)
break;
}
}
if (count == 0)
{
memcpy(items, newItems, PYRAMID_BAG_ITEMS_COUNT * sizeof(*items));
memcpy(quantities, newQuantities, PYRAMID_BAG_ITEMS_COUNT * sizeof(*quantities));
Free(newItems);
Free(newQuantities);
return TRUE;
}
else
{
Free(newItems);
Free(newQuantities);
return FALSE;
}
}
}
static u16 SanitizeItemId(u16 itemId)
{
if (itemId >= ITEMS_COUNT)
return ITEM_NONE;
else
return itemId;
}
const u8 *GetItemName(u16 itemId)
{
const u8 *name = gItemsInfo[SanitizeItemId(itemId)].name;
return name == NULL ? gQuestionMarksItemName : name;
}
u32 GetItemPrice(u16 itemId)
{
return gItemsInfo[SanitizeItemId(itemId)].price;
}
static bool32 DoesItemHavePluralName(u16 itemId)
{
return gItemsInfo[SanitizeItemId(itemId)].pluralName != NULL;
}
static const u8 *GetItemPluralName(u16 itemId)
{
return gItemsInfo[SanitizeItemId(itemId)].pluralName;
}
const u8 *GetItemEffect(u32 itemId)
{
if (itemId == ITEM_ENIGMA_BERRY_E_READER)
#if FREE_ENIGMA_BERRY == FALSE
return gSaveBlock1Ptr->enigmaBerry.itemEffect;
#else
return 0;
#endif //FREE_ENIGMA_BERRY
else
return gItemsInfo[SanitizeItemId(itemId)].effect;
}
enum HoldEffect GetItemHoldEffect(u32 itemId)
{
return gItemsInfo[SanitizeItemId(itemId)].holdEffect;
}
u32 GetItemHoldEffectParam(u32 itemId)
{
return gItemsInfo[SanitizeItemId(itemId)].holdEffectParam;
}
const u8 *GetItemDescription(u16 itemId)
{
return gItemsInfo[SanitizeItemId(itemId)].description;
}
u8 GetItemImportance(u16 itemId)
{
return gItemsInfo[SanitizeItemId(itemId)].importance;
}
u8 GetItemConsumability(u16 itemId)
{
return !gItemsInfo[SanitizeItemId(itemId)].notConsumed;
}
enum Pocket GetItemPocket(u16 itemId)
{
return gItemsInfo[SanitizeItemId(itemId)].pocket;
}
u8 GetItemType(u16 itemId)
{
return gItemsInfo[SanitizeItemId(itemId)].type;
}
ItemUseFunc GetItemFieldFunc(u16 itemId)
{
return gItemsInfo[SanitizeItemId(itemId)].fieldUseFunc;
}
// Returns an item's battle effect script ID.
u8 GetItemBattleUsage(u16 itemId)
{
u16 item = SanitizeItemId(itemId);
// Handle E-Reader berries.
if (item == ITEM_ENIGMA_BERRY_E_READER)
{
switch (GetItemEffectType(gSpecialVar_ItemId))
{
case ITEM_EFFECT_X_ITEM:
return EFFECT_ITEM_INCREASE_STAT;
case ITEM_EFFECT_HEAL_HP:
return EFFECT_ITEM_RESTORE_HP;
case ITEM_EFFECT_CURE_POISON:
case ITEM_EFFECT_CURE_SLEEP:
case ITEM_EFFECT_CURE_BURN:
case ITEM_EFFECT_CURE_FREEZE_FROSTBITE:
case ITEM_EFFECT_CURE_PARALYSIS:
case ITEM_EFFECT_CURE_ALL_STATUS:
case ITEM_EFFECT_CURE_CONFUSION:
case ITEM_EFFECT_CURE_INFATUATION:
return EFFECT_ITEM_CURE_STATUS;
case ITEM_EFFECT_HEAL_PP:
return EFFECT_ITEM_RESTORE_PP;
default:
return 0;
}
}
else
return gItemsInfo[item].battleUsage;
}
u32 GetItemSecondaryId(u32 itemId)
{
return gItemsInfo[SanitizeItemId(itemId)].secondaryId;
}
u32 GetItemFlingPower(u32 itemId)
{
return gItemsInfo[SanitizeItemId(itemId)].flingPower;
}
u32 GetItemStatus1Mask(u16 itemId)
{
const u8 *effect = GetItemEffect(itemId);
switch (effect[3])
{
case ITEM3_PARALYSIS:
return STATUS1_PARALYSIS;
case ITEM3_FREEZE:
return STATUS1_FREEZE | STATUS1_FROSTBITE;
case ITEM3_BURN:
return STATUS1_BURN;
case ITEM3_POISON:
return STATUS1_PSN_ANY | STATUS1_TOXIC_COUNTER;
case ITEM3_SLEEP:
return STATUS1_SLEEP;
case ITEM3_STATUS_ALL:
return STATUS1_ANY | STATUS1_TOXIC_COUNTER;
}
return 0;
}
bool32 ItemHasVolatileFlag(u16 itemId, enum Volatile _volatile)
{
const u8 *effect = GetItemEffect(itemId);
switch (_volatile)
{
case VOLATILE_CONFUSION:
return (effect[3] & ITEM3_STATUS_ALL) || (effect[3] & ITEM3_CONFUSION);
case VOLATILE_INFATUATION:
return (effect[3] & ITEM3_STATUS_ALL) || (effect[0] & ITEM0_INFATUATION);
default:
return FALSE;
}
}
u32 GetItemSellPrice(u32 itemId)
{
return GetItemPrice(itemId) / ITEM_SELL_FACTOR;
}
bool32 IsHoldEffectChoice(enum HoldEffect holdEffect)
{
return holdEffect == HOLD_EFFECT_CHOICE_BAND
|| holdEffect == HOLD_EFFECT_CHOICE_SCARF
|| holdEffect == HOLD_EFFECT_CHOICE_SPECS;
}