summaryrefslogtreecommitdiff
path: root/src/item.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/item.c')
-rw-r--r--src/item.c684
1 files changed, 684 insertions, 0 deletions
diff --git a/src/item.c b/src/item.c
new file mode 100644
index 000000000..3f33f464d
--- /dev/null
+++ b/src/item.c
@@ -0,0 +1,684 @@
+#include "global.h"
+#include "berry.h"
+#include "event_data.h"
+#include "item.h"
+#include "item_use.h"
+#include "load_save.h"
+#include "malloc.h"
+#include "quest_log.h"
+#include "string_util.h"
+#include "strings.h"
+#include "constants/hold_effects.h"
+#include "constants/items.h"
+#include "constants/maps.h"
+
+void SortAndCompactBagPocket(struct BagPocket * pocket);
+
+// Item descriptions and data
+#include "data/items.h"
+
+u16 GetBagItemQuantity(u16 * ptr)
+{
+ return gSaveBlock2Ptr->encryptionKey ^ *ptr;
+}
+
+void SetBagItemQuantity(u16 * ptr, u16 value)
+{
+ *ptr = value ^ gSaveBlock2Ptr->encryptionKey;
+}
+
+u16 GetPcItemQuantity(u16 * ptr)
+{
+ return 0 ^ *ptr;
+}
+
+void SetPcItemQuantity(u16 * ptr, u16 value)
+{
+ *ptr = value ^ 0;
+}
+
+void ApplyNewEncryptionKeyToBagItems(u32 key)
+{
+ u32 i, j;
+
+ for (i = 0; i < NUM_BAG_POCKETS; i++)
+ {
+ for (j = 0; j < gBagPockets[i].capacity; j++)
+ {
+ ApplyNewEncryptionKeyToHword(&gBagPockets[i].itemSlots[j].quantity, key);
+ }
+ }
+}
+
+void ApplyNewEncryptionKeyToBagItems_(u32 key)
+{
+ ApplyNewEncryptionKeyToBagItems(key);
+}
+
+void SetBagPocketsPointers(void)
+{
+ gBagPockets[POCKET_ITEMS - 1].itemSlots = gSaveBlock1Ptr->bagPocket_Items;
+ gBagPockets[POCKET_ITEMS - 1].capacity = BAG_ITEMS_COUNT;
+ gBagPockets[POCKET_KEY_ITEMS - 1].itemSlots = gSaveBlock1Ptr->bagPocket_KeyItems;
+ gBagPockets[POCKET_KEY_ITEMS - 1].capacity = BAG_KEYITEMS_COUNT;
+ gBagPockets[POCKET_POKE_BALLS - 1].itemSlots = gSaveBlock1Ptr->bagPocket_PokeBalls;
+ gBagPockets[POCKET_POKE_BALLS - 1].capacity = BAG_POKEBALLS_COUNT;
+ gBagPockets[POCKET_TM_CASE - 1].itemSlots = gSaveBlock1Ptr->bagPocket_TMHM;
+ gBagPockets[POCKET_TM_CASE - 1].capacity = BAG_TMHM_COUNT;
+ gBagPockets[POCKET_BERRY_POUCH - 1].itemSlots = gSaveBlock1Ptr->bagPocket_Berries;
+ gBagPockets[POCKET_BERRY_POUCH - 1].capacity = BAG_BERRIES_COUNT;
+}
+
+void CopyItemName(u16 itemId, u8 * dest)
+{
+ if (itemId == ITEM_ENIGMA_BERRY)
+ {
+ StringCopy(dest, sub_809C8A0(43)->name);
+ StringAppend(dest, gUnknown_84162BD);
+ }
+ else
+ {
+ StringCopy(dest, ItemId_GetName(itemId));
+ }
+}
+
+s8 BagPocketGetFirstEmptySlot(u8 pocketId)
+{
+ u16 i;
+
+ for (i = 0; i < gBagPockets[pocketId].capacity; i++)
+ {
+ if (gBagPockets[pocketId].itemSlots[i].itemId == ITEM_NONE)
+ return i;
+ }
+
+ return -1;
+}
+
+bool8 IsPocketNotEmpty(u8 pocketId)
+{
+ u8 i;
+
+ for (i = 0; i < gBagPockets[pocketId - 1].capacity; i++)
+ {
+ if (gBagPockets[pocketId - 1].itemSlots[i].itemId != ITEM_NONE)
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+bool8 CheckBagHasItem(u16 itemId, u16 count)
+{
+ u8 i;
+ u8 pocket;
+
+ if (ItemId_GetPocket(itemId) == 0)
+ return FALSE;
+
+ pocket = ItemId_GetPocket(itemId) - 1;
+ // Check for item slots that contain the item
+ for (i = 0; i < gBagPockets[pocket].capacity; i++)
+ {
+ if (gBagPockets[pocket].itemSlots[i].itemId == itemId)
+ {
+ u16 quantity;
+ // Does this item slot contain enough of the item?
+ quantity = GetBagItemQuantity(&gBagPockets[pocket].itemSlots[i].quantity);
+ if (quantity >= count)
+ return TRUE;
+ // RS and Emerald check whether there is enough of the
+ // item across all stacks.
+ // For whatever reason, FR/LG assume there's only one
+ // stack of the item.
+ else
+ return FALSE;
+ }
+ }
+ return FALSE;
+}
+
+bool8 CheckHasAtLeastOneBerry(void)
+{
+ u8 itemId;
+ bool8 exists;
+
+ exists = CheckBagHasItem(ITEM_BERRY_POUCH, 1);
+ if (!exists)
+ {
+ gSpecialVar_Result = FALSE;
+ return FALSE;
+ }
+ for (itemId = FIRST_BERRY_INDEX; itemId <= LAST_BERRY_INDEX; itemId++)
+ {
+ exists = CheckBagHasItem(itemId, 1);
+ if (exists)
+ {
+ gSpecialVar_Result = TRUE;
+ return TRUE;
+ }
+ }
+
+ gSpecialVar_Result = FALSE;
+ return FALSE;
+}
+
+bool8 CheckBagHasSpace(u16 itemId, u16 count)
+{
+ u8 i;
+ u8 pocket;
+
+ if (ItemId_GetPocket(itemId) == 0)
+ return FALSE;
+
+ pocket = ItemId_GetPocket(itemId) - 1;
+ // Check for item slots that contain the item
+ for (i = 0; i < gBagPockets[pocket].capacity; i++)
+ {
+ if (gBagPockets[pocket].itemSlots[i].itemId == itemId)
+ {
+ u16 quantity;
+ // Does this stack have room for more??
+ quantity = GetBagItemQuantity(&gBagPockets[pocket].itemSlots[i].quantity);
+ if (quantity + count <= 999)
+ return TRUE;
+ // RS and Emerald check whether there is enough of the
+ // item across all stacks.
+ // For whatever reason, FR/LG assume there's only one
+ // stack of the item.
+ else
+ return FALSE;
+ }
+ }
+
+ if (BagPocketGetFirstEmptySlot(pocket) != -1)
+ return TRUE;
+
+ return FALSE;
+}
+
+bool8 AddBagItem(u16 itemId, u16 count)
+{
+ u8 i;
+ u8 pocket;
+ s8 idx;
+
+ if (ItemId_GetPocket(itemId) == 0)
+ return FALSE;
+
+ pocket = ItemId_GetPocket(itemId) - 1;
+ for (i = 0; i < gBagPockets[pocket].capacity; i++)
+ {
+ if (gBagPockets[pocket].itemSlots[i].itemId == itemId)
+ {
+ u16 quantity;
+ // Does this stack have room for more??
+ quantity = GetBagItemQuantity(&gBagPockets[pocket].itemSlots[i].quantity);
+ if (quantity + count <= 999)
+ {
+ quantity += count;
+ SetBagItemQuantity(&gBagPockets[pocket].itemSlots[i].quantity, quantity);
+ return TRUE;
+ }
+ // RS and Emerald check whether there is enough of the
+ // item across all stacks.
+ // For whatever reason, FR/LG assume there's only one
+ // stack of the item.
+ else
+ return FALSE;
+ }
+ }
+
+ if (pocket == POCKET_TM_CASE - 1 && !CheckBagHasItem(ITEM_TM_CASE, 1))
+ {
+ idx = BagPocketGetFirstEmptySlot(POCKET_KEY_ITEMS - 1);
+ if (idx == -1)
+ return FALSE;
+ gBagPockets[POCKET_KEY_ITEMS - 1].itemSlots[idx].itemId = ITEM_TM_CASE;
+ SetBagItemQuantity(&gBagPockets[POCKET_KEY_ITEMS - 1].itemSlots[idx].quantity, 1);
+ }
+
+ if (pocket == POCKET_BERRY_POUCH - 1 && !CheckBagHasItem(ITEM_BERRY_POUCH, 1))
+ {
+ idx = BagPocketGetFirstEmptySlot(POCKET_KEY_ITEMS - 1);
+ if (idx == -1)
+ return FALSE;
+ gBagPockets[POCKET_KEY_ITEMS - 1].itemSlots[idx].itemId = ITEM_BERRY_POUCH;
+ SetBagItemQuantity(&gBagPockets[POCKET_KEY_ITEMS - 1].itemSlots[idx].quantity, 1);
+ FlagSet(FLAG_0x847);
+ }
+
+ if (itemId == ITEM_BERRY_POUCH)
+ FlagSet(FLAG_0x847);
+
+ idx = BagPocketGetFirstEmptySlot(pocket);
+ if (idx == -1)
+ return FALSE;
+
+ gBagPockets[pocket].itemSlots[idx].itemId = itemId;
+ SetBagItemQuantity(&gBagPockets[pocket].itemSlots[idx].quantity, count);
+ return TRUE;
+}
+
+bool8 RemoveBagItem(u16 itemId, u16 count)
+{
+ u8 i;
+ u8 pocket;
+
+ if (ItemId_GetPocket(itemId) == 0)
+ return FALSE;
+
+ if (itemId == ITEM_NONE)
+ return FALSE;
+
+ pocket = ItemId_GetPocket(itemId) - 1;
+ // Check for item slots that contain the item
+ for (i = 0; i < gBagPockets[pocket].capacity; i++)
+ {
+ if (gBagPockets[pocket].itemSlots[i].itemId == itemId)
+ {
+ u16 quantity;
+ // Does this item slot contain enough of the item?
+ quantity = GetBagItemQuantity(&gBagPockets[pocket].itemSlots[i].quantity);
+ if (quantity >= count)
+ {
+ quantity -= count;
+ SetBagItemQuantity(&gBagPockets[pocket].itemSlots[i].quantity, quantity);
+ if (quantity == 0)
+ gBagPockets[pocket].itemSlots[i].itemId = ITEM_NONE;
+ return TRUE;
+ }
+ // RS and Emerald check whether there is enough of the
+ // item across all stacks.
+ // For whatever reason, FR/LG assume there's only one
+ // stack of the item.
+ else
+ return FALSE;
+ }
+ }
+ return FALSE;
+}
+
+u8 GetPocketByItemId(u16 itemId)
+{
+ return ItemId_GetPocket(itemId); // wow such important
+}
+
+void ClearItemSlots(struct ItemSlot * slots, u8 capacity)
+{
+ u16 i;
+
+ for (i = 0; i < capacity; i++)
+ {
+ slots[i].itemId = ITEM_NONE;
+ SetBagItemQuantity(&slots[i].quantity, 0);
+ }
+}
+
+void ClearPCItemSlots(void)
+{
+ u16 i;
+
+ for (i = 0; i < PC_ITEMS_COUNT; i++)
+ {
+ gSaveBlock1Ptr->pcItems[i].itemId = ITEM_NONE;
+ SetPcItemQuantity(&gSaveBlock1Ptr->pcItems[i].quantity, 0);
+ }
+}
+
+void ClearItemSlotsInAllBagPockets(void)
+{
+ u16 i;
+
+ for (i = 0; i < 5; i++)
+ {
+ ClearItemSlots(gBagPockets[i].itemSlots, gBagPockets[i].capacity);
+ }
+}
+
+s8 PCItemsGetFirstEmptySlot(void)
+{
+ s8 i;
+
+ for (i = 0; i < PC_ITEMS_COUNT; i++)
+ {
+ if (gSaveBlock1Ptr->pcItems[i].itemId == ITEM_NONE)
+ return i;
+ }
+
+ return -1;
+}
+
+u8 CountItemsInPC(void)
+{
+ u8 count = 0;
+ u8 i;
+
+ for (i = 0; i < PC_ITEMS_COUNT; i++)
+ {
+ if (gSaveBlock1Ptr->pcItems[i].itemId != ITEM_NONE)
+ count++;
+ }
+
+ return count;
+}
+
+bool8 CheckPCHasItem(u16 itemId, u16 count)
+{
+ u8 i;
+ u16 quantity;
+
+ for (i = 0; i < PC_ITEMS_COUNT; i++)
+ {
+ if (gSaveBlock1Ptr->pcItems[i].itemId == itemId)
+ {
+ quantity = GetPcItemQuantity(&gSaveBlock1Ptr->pcItems[i].quantity);
+ if (quantity >= count)
+ return TRUE;
+ }
+ }
+
+ return FALSE;
+}
+
+bool8 AddPCItem(u16 itemId, u16 count)
+{
+ u8 i;
+ u16 quantity;
+ s8 idx;
+
+ for (i = 0; i < PC_ITEMS_COUNT; i++)
+ {
+ if (gSaveBlock1Ptr->pcItems[i].itemId == itemId)
+ {
+ quantity = GetPcItemQuantity(&gSaveBlock1Ptr->pcItems[i].quantity);
+ if (quantity + count <= 999)
+ {
+ quantity += count;
+ SetPcItemQuantity(&gSaveBlock1Ptr->pcItems[i].quantity, quantity);
+ return TRUE;
+ }
+ else
+ return FALSE;
+ }
+ }
+
+ idx = PCItemsGetFirstEmptySlot();
+ if (idx == -1)
+ return FALSE;
+
+ gSaveBlock1Ptr->pcItems[idx].itemId = itemId;
+ SetPcItemQuantity(&gSaveBlock1Ptr->pcItems[idx].quantity, count);
+ return TRUE;
+}
+
+void RemoveItemFromPC(u16 itemId, u16 count)
+{
+ u32 i;
+ u16 quantity;
+
+ if (itemId == ITEM_NONE)
+ return;
+
+ for (i = 0; i < PC_ITEMS_COUNT; i++)
+ {
+ if (gSaveBlock1Ptr->pcItems[i].itemId == itemId)
+ break;
+ }
+
+ if (i != PC_ITEMS_COUNT)
+ {
+ quantity = GetPcItemQuantity(&gSaveBlock1Ptr->pcItems[i].quantity) - count;
+ SetPcItemQuantity(&gSaveBlock1Ptr->pcItems[i].quantity, quantity);
+ if (quantity == 0)
+ gSaveBlock1Ptr->pcItems[i].itemId = ITEM_NONE;
+ }
+}
+
+void ItemPcCompaction(void)
+{
+ u16 i, j;
+ struct ItemSlot tmp;
+
+ for (i = 0; i < PC_ITEMS_COUNT - 1; i++)
+ {
+ for (j = i + 1; j < PC_ITEMS_COUNT; j++)
+ {
+ if (gSaveBlock1Ptr->pcItems[i].itemId == ITEM_NONE)
+ {
+ tmp = gSaveBlock1Ptr->pcItems[i];
+ gSaveBlock1Ptr->pcItems[i] = gSaveBlock1Ptr->pcItems[j];
+ gSaveBlock1Ptr->pcItems[j] = tmp;
+ }
+ }
+ }
+}
+
+void RegisteredItemHandleBikeSwap(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 SwapItemSlots(struct ItemSlot * a, struct ItemSlot * b)
+{
+ struct ItemSlot c;
+ c = *a;
+ *a = *b;
+ *b = c;
+}
+
+void BagPocketCompaction(struct ItemSlot * slots, u8 capacity)
+{
+ u16 i, j;
+
+ for (i = 0; i < capacity - 1; i++)
+ {
+ for (j = i + 1; j < capacity; j++)
+ {
+ if (GetBagItemQuantity(&slots[i].quantity) == 0)
+ {
+ SwapItemSlots(&slots[i], &slots[j]);
+ }
+ }
+ }
+}
+
+void SortPocketAndPlaceHMsFirst(struct BagPocket * pocket)
+{
+ u16 i;
+ u16 j = 0;
+ u16 k;
+ struct ItemSlot * buff;
+
+ SortAndCompactBagPocket(pocket);
+
+ for (i = 0; i < pocket->capacity; i++)
+ {
+ if (pocket->itemSlots[i].itemId == ITEM_NONE && GetBagItemQuantity(&pocket->itemSlots[i].quantity) == 0)
+ return;
+ if (pocket->itemSlots[i].itemId >= ITEM_HM01 && GetBagItemQuantity(&pocket->itemSlots[i].quantity) != 0)
+ {
+ for (j = i + 1; j < pocket->capacity; j++)
+ {
+ if (pocket->itemSlots[j].itemId == ITEM_NONE && GetBagItemQuantity(&pocket->itemSlots[j].quantity) == 0)
+ break;
+ }
+ break;
+ }
+ }
+
+ for (k = 0; k < pocket->capacity; k++)
+ pocket->itemSlots[k].quantity = GetBagItemQuantity(&pocket->itemSlots[k].quantity);
+ buff = AllocZeroed(pocket->capacity * sizeof(struct ItemSlot));
+ CpuCopy16(pocket->itemSlots + i, buff, (j - i) * sizeof(struct ItemSlot));
+ CpuCopy16(pocket->itemSlots, buff + (j - i), i * sizeof(struct ItemSlot));
+ CpuCopy16(buff, pocket->itemSlots, pocket->capacity * sizeof(struct ItemSlot));
+ for (k = 0; k < pocket->capacity; k++)
+ SetBagItemQuantity(&pocket->itemSlots[k].quantity, pocket->itemSlots[k].quantity);
+ Free(buff);
+}
+
+void SortAndCompactBagPocket(struct BagPocket * pocket)
+{
+ u16 i, j;
+
+ for (i = 0; i < pocket->capacity; i++)
+ {
+ for (j = i + 1; j < pocket->capacity; j++)
+ {
+ if (GetBagItemQuantity(&pocket->itemSlots[i].quantity) == 0 || (GetBagItemQuantity(&pocket->itemSlots[j].quantity) != 0 && pocket->itemSlots[i].itemId > pocket->itemSlots[j].itemId))
+ SwapItemSlots(&pocket->itemSlots[i], &pocket->itemSlots[j]);
+ }
+ }
+}
+
+u16 BagGetItemIdByPocketPosition(u8 pocketId, u16 slotId)
+{
+ return gBagPockets[pocketId - 1].itemSlots[slotId].itemId;
+}
+
+u16 BagGetQuantityByPocketPosition(u8 pocketId, u16 slotId)
+{
+ return GetBagItemQuantity(&gBagPockets[pocketId - 1].itemSlots[slotId].quantity);
+}
+
+u16 BagGetQuantityByItemId(u16 itemId)
+{
+ u16 i;
+ struct BagPocket * pocket = &gBagPockets[ItemId_GetPocket(itemId) - 1];
+
+ for (i = 0; i < pocket->capacity; i++)
+ {
+ if (pocket->itemSlots[i].itemId == itemId)
+ return GetBagItemQuantity(&pocket->itemSlots[i].quantity);
+ }
+
+ return 0;
+}
+
+void sub_809A824(u16 itemId)
+{
+ struct QuestLogStruct_809A824
+ {
+ u16 itemId;
+ u8 mapSectionId;
+ } * ptr;
+ if
+ (
+ itemId == ITEM_OAKS_PARCEL
+ || itemId == ITEM_POKE_FLUTE
+ || itemId == ITEM_SECRET_KEY
+ || itemId == ITEM_BIKE_VOUCHER
+ || itemId == ITEM_GOLD_TEETH
+ || itemId == ITEM_OLD_AMBER
+ || itemId == ITEM_CARD_KEY
+ || itemId == ITEM_LIFT_KEY
+ || itemId == ITEM_HELIX_FOSSIL
+ || itemId == ITEM_DOME_FOSSIL
+ || itemId == ITEM_SILPH_SCOPE
+ || itemId == ITEM_BICYCLE
+ || itemId == ITEM_TOWN_MAP
+ || itemId == ITEM_VS_SEEKER
+ || itemId == ITEM_TEACHY_TV
+ || itemId == ITEM_RAINBOW_PASS
+ || itemId == ITEM_TEA
+ || itemId == ITEM_POWDER_JAR
+ || itemId == ITEM_RUBY
+ || itemId == ITEM_SAPPHIRE
+ )
+ {
+ if (itemId != ITEM_TOWN_MAP || (gSaveBlock1Ptr->location.mapGroup == MAP_GROUP(PALLET_TOWN_GARYS_HOUSE) && gSaveBlock1Ptr->location.mapNum == MAP_NUM(PALLET_TOWN_GARYS_HOUSE)))
+ {
+ ptr = malloc(sizeof(*ptr));
+ ptr->itemId = itemId;
+ ptr->mapSectionId = gMapHeader.regionMapSectionId;
+ sub_8113550(40, (void *)ptr);
+ free(ptr);
+ }
+ }
+}
+
+u16 SanitizeItemId(u16 itemId)
+{
+ if (itemId >= ITEM_N_A)
+ return ITEM_NONE;
+ return itemId;
+}
+
+const u8 * ItemId_GetName(u16 itemId)
+{
+ return gItems[SanitizeItemId(itemId)].name;
+}
+
+u16 itemid_get_number(u16 itemId)
+{
+ return gItems[SanitizeItemId(itemId)].itemId;
+}
+
+u16 itemid_get_market_price(u16 itemId)
+{
+ return gItems[SanitizeItemId(itemId)].price;
+}
+
+u8 ItemId_GetHoldEffect(u16 itemId)
+{
+ return gItems[SanitizeItemId(itemId)].holdEffect;
+}
+
+u8 ItemId_GetHoldEffectParam(u16 itemId)
+{
+ return gItems[SanitizeItemId(itemId)].holdEffectParam;
+}
+
+const u8 * ItemId_GetDescription(u16 itemId)
+{
+ return gItems[SanitizeItemId(itemId)].description;
+}
+
+bool8 itemid_is_unique(u16 itemId)
+{
+ return gItems[SanitizeItemId(itemId)].importance;
+}
+
+u8 itemid_get_x19(u16 itemId)
+{
+ return gItems[SanitizeItemId(itemId)].exitsBagOnUse;
+}
+
+u8 ItemId_GetPocket(u16 itemId)
+{
+ return gItems[SanitizeItemId(itemId)].pocket;
+}
+
+u8 ItemId_GetType(u16 itemId)
+{
+ return gItems[SanitizeItemId(itemId)].type;
+}
+
+ItemUseFunc ItemId_GetFieldFunc(u16 itemId)
+{
+ return gItems[SanitizeItemId(itemId)].fieldUseFunc;
+}
+
+bool8 ItemId_GetBattleUsage(u16 itemId)
+{
+ return gItems[SanitizeItemId(itemId)].battleUsage;
+}
+
+ItemUseFunc ItemId_GetBattleFunc(u16 itemId)
+{
+ return gItems[SanitizeItemId(itemId)].battleUseFunc;
+}
+
+u8 ItemId_GetSecondaryId(u16 itemId)
+{
+ return gItems[SanitizeItemId(itemId)].secondaryId;
+}