mirror of https://github.com/zeldaret/tmc.git
597 lines
16 KiB
C
597 lines
16 KiB
C
/**
|
|
* @file itemUtils.c
|
|
*
|
|
* @brief Item Utils
|
|
*/
|
|
#include "item.h"
|
|
#include "game.h"
|
|
#include "itemMetaData.h"
|
|
#include "sound.h"
|
|
#include "save.h"
|
|
#include "object.h"
|
|
#include "enemy.h"
|
|
#include "message.h"
|
|
|
|
const Wallet gWalletSizes[] = {
|
|
{ 100, 0xf060 },
|
|
{ 300, 0xf064 },
|
|
{ 500, 0xf068 },
|
|
{ 999, 0xf06c },
|
|
};
|
|
const u8 gBombBagSizes[] = {
|
|
10,
|
|
30,
|
|
50,
|
|
99,
|
|
};
|
|
const u8 gQuiverSizes[] = {
|
|
30,
|
|
50,
|
|
70,
|
|
99,
|
|
};
|
|
const u16 gUnk_080FD5A8[] = { 1, 5, 20, 50, 100, 200 };
|
|
|
|
u32 GetSaleItemConfirmMessageID(u32 item) {
|
|
const struct_080FD964* ptr = &gUnk_080FD964[item];
|
|
return ptr->saleItemConfirmMessageId;
|
|
}
|
|
|
|
s32 GetItemPrice(u32 item) {
|
|
const struct_080FD964* ptr = &gUnk_080FD964[item];
|
|
return ptr->itemPrice;
|
|
}
|
|
|
|
u32 GiveItem(Item item, u32 param_2) {
|
|
u32 uVar4;
|
|
u32 result;
|
|
u32 uVar9;
|
|
const ItemMetaData* metaData;
|
|
|
|
uVar4 = GetInventoryValue(item);
|
|
metaData = &gItemMetaData[item];
|
|
|
|
if (uVar4 == 0) {
|
|
result = *(u16*)&metaData->textId;
|
|
} else {
|
|
result = *(u16*)&metaData->unk6;
|
|
}
|
|
if (!ItemIsBottle(item)) {
|
|
PutItemOnSlot(item);
|
|
if (uVar4 == 0) {
|
|
SetInventoryValue(item, 1);
|
|
}
|
|
}
|
|
switch (metaData->unk1) {
|
|
case 0:
|
|
default:
|
|
break;
|
|
case 1:
|
|
ModHealth(metaData->unk2);
|
|
SoundReq(SFX_HEART_GET);
|
|
break;
|
|
case 2:
|
|
ModRupees((u32)gUnk_080FD5A8[metaData->unk2]);
|
|
SoundReq(SFX_RUPEE_GET);
|
|
break;
|
|
case 3:
|
|
uVar9 = 0;
|
|
while (TRUE) {
|
|
if (3 < uVar9) {
|
|
return result;
|
|
}
|
|
if (GetInventoryValue(ITEM_BOTTLE1 + uVar9) == 0)
|
|
break;
|
|
uVar9++;
|
|
}
|
|
item = ITEM_BOTTLE1 + uVar9;
|
|
if (gSave.stats.bottles[uVar9] == 0) {
|
|
gSave.stats.bottles[uVar9] = 0x20;
|
|
}
|
|
SetInventoryValue(item, 1);
|
|
PutItemOnSlot(item);
|
|
break;
|
|
case 4:
|
|
uVar9 = 0;
|
|
while (gSave.stats.bottles[uVar9] != 0x20) {
|
|
uVar9++;
|
|
if (3 < uVar9) {
|
|
return result;
|
|
}
|
|
}
|
|
if (3 < uVar9) {
|
|
break;
|
|
}
|
|
gSave.stats.bottles[uVar9] = (u8)item;
|
|
SetInventoryValue(item, 1);
|
|
SoundReq(SFX_ITEM_GET);
|
|
break;
|
|
case 5:
|
|
ModDungeonKeys(1);
|
|
SoundReq(SFX_103);
|
|
break;
|
|
case 6: // TODO dungeon item?
|
|
gSave.dungeonItems[gArea.dungeon_idx] |= metaData->unk2;
|
|
break;
|
|
case 0xd:
|
|
AddKinstoneToBag(0x72);
|
|
break;
|
|
case 0x12:
|
|
if (item == ITEM_BOOMERANG) {
|
|
SetInventoryValue(ITEM_MAGIC_BOOMERANG, 0);
|
|
} else {
|
|
SetInventoryValue(ITEM_BOOMERANG, 0);
|
|
}
|
|
case 0x11:
|
|
LoadItemGfx();
|
|
break;
|
|
case 7:
|
|
if (item == ITEM_BOMBS) {
|
|
SetInventoryValue(ITEM_REMOTE_BOMBS, 0);
|
|
} else {
|
|
SetInventoryValue(ITEM_BOMBS, 0);
|
|
}
|
|
ModBombs(99);
|
|
LoadItemGfx();
|
|
break;
|
|
case 8:
|
|
if (uVar4 == 0) {
|
|
SetInventoryValue(ITEM_BOMBS, 1);
|
|
PutItemOnSlot(7);
|
|
} else {
|
|
gSave.stats.bombBagType++;
|
|
if (3 < gSave.stats.bombBagType) {
|
|
gSave.stats.bombBagType = 3;
|
|
}
|
|
}
|
|
ModBombs(99);
|
|
break;
|
|
case 9:
|
|
ModBombs(metaData->unk2);
|
|
SoundReq(SFX_103);
|
|
break;
|
|
case 0xb:
|
|
if (GetInventoryValue(ITEM_LARGE_QUIVER) == 0) {
|
|
SetInventoryValue(ITEM_LARGE_QUIVER, 1);
|
|
}
|
|
ModArrows(99);
|
|
LoadItemGfx();
|
|
break;
|
|
case 0xa:
|
|
gSave.stats.quiverType++;
|
|
if (3 < gSave.stats.quiverType) {
|
|
gSave.stats.quiverType = 3;
|
|
}
|
|
ModArrows(99);
|
|
break;
|
|
case 0xc:
|
|
ModArrows(metaData->unk2);
|
|
SoundReq(SFX_103);
|
|
break;
|
|
case 0x0e:
|
|
SetInventoryValue(ITEM_SHELLS, 1);
|
|
ModShells(param_2);
|
|
SoundReq(SFX_103);
|
|
break;
|
|
case 0x0f:
|
|
AddKinstoneToBag(param_2);
|
|
break;
|
|
case 0x10:
|
|
gSave.stats.walletType++;
|
|
if (gSave.stats.walletType < 4) {
|
|
break;
|
|
}
|
|
gSave.stats.walletType = 3;
|
|
break;
|
|
}
|
|
return result;
|
|
}
|
|
|
|
// TODO Adapt ItemMetaData struct
|
|
void sub_08054288(s32 param_1) {
|
|
const ItemMetaData* ptr1 = gItemMetaData;
|
|
u16* ptr = (u16*)&((ptr1)[param_1]);
|
|
MessageFromTarget(ptr[3]);
|
|
}
|
|
|
|
u32 getItemMetaDataGetTextIndex(s32 itemIndex) {
|
|
const ItemMetaData* ptr1 = gItemMetaData;
|
|
u16* ptr = (u16*)&((ptr1)[itemIndex]);
|
|
return ptr[2];
|
|
}
|
|
|
|
u32 sub_080542AC(u32 param_1) {
|
|
const ItemMetaData* ptr1 = gItemMetaData;
|
|
u8* ptr = (u8*)&((ptr1)[param_1]);
|
|
return ptr[3] & 1;
|
|
}
|
|
|
|
void ModShells(s32 shells) {
|
|
if (shells == 0) {
|
|
shells = 1;
|
|
}
|
|
shells += gSave.stats.shells;
|
|
if (shells < 0) {
|
|
shells = 0;
|
|
} else if (999 < shells) {
|
|
shells = 999;
|
|
}
|
|
gSave.stats.shells = shells;
|
|
}
|
|
|
|
void ModBombs(s32 bombs) {
|
|
s32 bombCount = bombs;
|
|
s32 bagSize;
|
|
bombCount += gSave.stats.bombCount;
|
|
bagSize = gBombBagSizes[gSave.stats.bombBagType];
|
|
if (bombCount < 0) {
|
|
bombCount = 0;
|
|
} else if (bagSize < bombCount) {
|
|
bombCount = bagSize;
|
|
}
|
|
gSave.stats.bombCount = bombCount;
|
|
}
|
|
|
|
void ModArrows(s32 arrows) {
|
|
s32 quiverSize;
|
|
s32 arrowCount = arrows;
|
|
arrowCount += gSave.stats.arrowCount;
|
|
quiverSize = gQuiverSizes[gSave.stats.quiverType];
|
|
if (arrowCount < 0) {
|
|
arrowCount = 0;
|
|
} else if (quiverSize < arrowCount) {
|
|
arrowCount = quiverSize;
|
|
}
|
|
gSave.stats.arrowCount = arrowCount;
|
|
}
|
|
|
|
/**
|
|
* @brief Returns the slot the item is equipped in.
|
|
*
|
|
* 0: A
|
|
* 1: B
|
|
* 2: Not equipped
|
|
*/
|
|
EquipSlot IsItemEquipped(u32 itemId) {
|
|
EquipSlot equipSlot;
|
|
|
|
if (itemId == gSave.stats.itemButtons[SLOT_A]) {
|
|
equipSlot = EQUIP_SLOT_A;
|
|
} else if (itemId == gSave.stats.itemButtons[SLOT_B]) {
|
|
equipSlot = EQUIP_SLOT_B;
|
|
} else {
|
|
equipSlot = EQUIP_SLOT_NONE;
|
|
}
|
|
return equipSlot;
|
|
}
|
|
|
|
void PutItemOnSlot(u32 itemId) {
|
|
EquipSlot equipSlot;
|
|
u32 itemId2 = itemId;
|
|
if (itemId2 < 0x47) {
|
|
SetInventoryValue(0, 1);
|
|
}
|
|
if (itemId2 - 1 < 0x1f) {
|
|
equipSlot = EQUIP_SLOT_NONE;
|
|
if (gSave.stats.itemButtons[SLOT_A] == ITEM_NONE) {
|
|
equipSlot = EQUIP_SLOT_A;
|
|
} else if (gSave.stats.itemButtons[SLOT_B] == ITEM_NONE) {
|
|
equipSlot = EQUIP_SLOT_B;
|
|
}
|
|
if (equipSlot == EQUIP_SLOT_NONE) {
|
|
u32 temp = gItemMetaData[itemId2].menuSlot;
|
|
if (temp == gItemMetaData[gSave.stats.itemButtons[SLOT_A]].menuSlot) {
|
|
equipSlot = EQUIP_SLOT_A;
|
|
} else {
|
|
if (temp == gItemMetaData[gSave.stats.itemButtons[SLOT_B]].menuSlot) {
|
|
equipSlot = EQUIP_SLOT_B;
|
|
}
|
|
}
|
|
if (equipSlot == EQUIP_SLOT_NONE) {
|
|
return;
|
|
}
|
|
}
|
|
ForceEquipItem(itemId2, equipSlot);
|
|
}
|
|
}
|
|
|
|
void ForceEquipItem(u32 itemId, u32 equipSlot) {
|
|
u32 otherItem;
|
|
u32 otherItemSlot;
|
|
u32 replacedItem;
|
|
|
|
if ((itemId - 1 < 0x1f) && (equipSlot < EQUIP_SLOT_NONE)) {
|
|
otherItemSlot = equipSlot == EQUIP_SLOT_A;
|
|
replacedItem = gSave.stats.itemButtons[equipSlot];
|
|
otherItem = gSave.stats.itemButtons[otherItemSlot];
|
|
if (gItemMetaData[otherItem].menuSlot == gItemMetaData[itemId].menuSlot) {
|
|
otherItem = replacedItem;
|
|
}
|
|
gSave.stats.itemButtons[equipSlot] = itemId;
|
|
gSave.stats.itemButtons[otherItemSlot] = otherItem;
|
|
gUnk_0200AF00.unk_13 = 0x7f;
|
|
gUnk_0200AF00.unk_14 = 0x7f;
|
|
}
|
|
}
|
|
|
|
u32 SetBottleContents(u32 itemId, u32 bottleIndex) {
|
|
if (bottleIndex > 3) {
|
|
bottleIndex = 0;
|
|
for (bottleIndex = 0; gSave.stats.bottles[bottleIndex] != ITEM_BOTTLE_EMPTY;) {
|
|
if (++bottleIndex > 3) {
|
|
return bottleIndex;
|
|
}
|
|
}
|
|
if (bottleIndex > 3) {
|
|
return bottleIndex;
|
|
}
|
|
}
|
|
gSave.stats.bottles[bottleIndex] = itemId;
|
|
return bottleIndex;
|
|
}
|
|
|
|
bool32 ItemIsSword(u32 item) {
|
|
switch (item) {
|
|
case ITEM_SMITH_SWORD:
|
|
case ITEM_GREEN_SWORD:
|
|
case ITEM_RED_SWORD:
|
|
case ITEM_BLUE_SWORD:
|
|
case ITEM_FOURSWORD:
|
|
return TRUE;
|
|
default:
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
bool32 ItemIsShield(u32 id) {
|
|
switch (id) {
|
|
case ITEM_SHIELD:
|
|
case ITEM_MIRROR_SHIELD:
|
|
return TRUE;
|
|
default:
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
bool32 ItemIsBottle(u32 id) {
|
|
switch (id) {
|
|
case ITEM_BOTTLE1:
|
|
case ITEM_BOTTLE2:
|
|
case ITEM_BOTTLE3:
|
|
case ITEM_BOTTLE4:
|
|
return TRUE;
|
|
default:
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
u32 GetBottleContaining(u32 id) {
|
|
if (id == gSave.stats.bottles[0]) {
|
|
return 1;
|
|
} else if (id == gSave.stats.bottles[1]) {
|
|
return 2;
|
|
} else if (id == gSave.stats.bottles[2]) {
|
|
return 3;
|
|
} else if (id == gSave.stats.bottles[3]) {
|
|
return 4;
|
|
} else {
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
extern u8 gUnk_080FE1C6[];
|
|
void sub_08054524(void) {
|
|
u32 bVar1;
|
|
|
|
bVar1 = gArea.locationIndex;
|
|
if (gArea.locationIndex == 0) {
|
|
bVar1 = gRoomTransition.location;
|
|
}
|
|
if (bVar1 > 0x16) {
|
|
bVar1 = 0;
|
|
}
|
|
|
|
bVar1 = gUnk_080FE1C6[bVar1];
|
|
MemCopy(&gAreaDroptables[bVar1], &gRoomVars.currentAreaDroptable, sizeof(Droptable));
|
|
}
|
|
|
|
void DisableRandomDrops(void) {
|
|
gRoomVars.randomDropsDisabled = TRUE;
|
|
}
|
|
|
|
void EnableRandomDrops(void) {
|
|
gRoomVars.randomDropsDisabled = FALSE;
|
|
}
|
|
|
|
extern void sub_08000F14(s16*, const s16*, const s16*, const s16*);
|
|
extern u32 sub_08000F2C(s16*, const s16*, const s16*, const s16*);
|
|
u32 CreateItemDrop(Entity* arg0, u32 itemId, u32 itemParameter);
|
|
u32 CreateRandomItemDrop(Entity* arg0, u32 arg1) {
|
|
extern const u8 gUnk_080FE1B4[] /* = {
|
|
ITEM_NONE, ITEM_RUPEE1, ITEM_RUPEE5, ITEM_RUPEE20, ITEM_HEART, ITEM_FAIRY,
|
|
ITEM_BOMBS5, ITEM_ARROWS5, ITEM_SHELLS, ITEM_KINSTONE_RED, ITEM_KINSTONE_BLUE, ITEM_KINSTONE_GREEN,
|
|
ITEM_ENEMY_BEETLE, ITEM_NONE, ITEM_NONE, ITEM_NONE, ITEM_NONE, ITEM_NONE,
|
|
}*/;
|
|
|
|
int r0, r1, rand, summOdds, item;
|
|
u32 r3;
|
|
const Droptable *ptr2, *ptr3, *ptr4;
|
|
Droptable droptable;
|
|
r3 = arg1;
|
|
if (gRoomVars.randomDropsDisabled != TRUE) {
|
|
ptr2 = &gDroptableModifiers[DROPTABLE_NONE];
|
|
ptr4 = NULL;
|
|
switch (r3) {
|
|
case 1 ... 12:
|
|
ptr4 = &gEnemyDroptables[r3];
|
|
break;
|
|
#ifndef EU
|
|
case 24:
|
|
case 25:
|
|
r0 = gRoomVars.unk2;
|
|
ptr4 = &gUnk_0800191C[0];
|
|
if (r0) {
|
|
ptr4++;
|
|
}
|
|
break;
|
|
#endif
|
|
case 16 ... 23:
|
|
#ifdef EU
|
|
case 24:
|
|
#endif
|
|
ptr2 = &gObjectDroptables[r3 - 16];
|
|
case 15:
|
|
ptr4 = &gRoomVars.currentAreaDroptable;
|
|
break;
|
|
case 0:
|
|
default:
|
|
break;
|
|
}
|
|
if (ptr4 != 0) {
|
|
if ((r1 = gSave.stats.picolyteType) == 0) {
|
|
// nop
|
|
ptr3 = &gDroptableModifiers[DROPTABLE_NONE];
|
|
} else {
|
|
#ifdef EU
|
|
ptr3 = &gEnemyDroptables[r1 + 9];
|
|
#else
|
|
ptr3 = &gEnemyDroptables[r1 + 6];
|
|
#endif
|
|
}
|
|
// vector addition, s0 = ptr4 + ptr2 + ptr3
|
|
sub_08000F14(droptable.a, ptr4->a, ptr2->a, ptr3->a);
|
|
if (gSave.stats.health <= 8) {
|
|
droptable.s.hearts += 5;
|
|
}
|
|
if (gSave.stats.bombCount == 0) {
|
|
droptable.s.bombs += 3;
|
|
}
|
|
if (gSave.stats.arrowCount == 0) {
|
|
droptable.s.arrows += 3;
|
|
}
|
|
if (gSave.stats.rupees <= 10) {
|
|
droptable.s.rupee5++;
|
|
}
|
|
ptr2 = &gDroptableModifiers[DROPTABLE_NONE];
|
|
r0 = gSave.stats.hasAllFigurines;
|
|
ptr3 = &gDroptableModifiers[DROPTABLE_NONE];
|
|
// don't drop shells anymore
|
|
if (r0 != 0) {
|
|
ptr2 = &gDroptableModifiers[DROPTABLE_NO_SHELLS];
|
|
}
|
|
// don't drop kinstones anymore
|
|
if (gSave.didAllFusions != 0) {
|
|
ptr3 = &gDroptableModifiers[DROPTABLE_NO_KINSTONES];
|
|
}
|
|
// vector addition, s0 = s0 + ptr2 + ptr3
|
|
// resulting values are clamped to be >= 0
|
|
// returns sum over s0
|
|
summOdds = sub_08000F2C(droptable.a, droptable.a, ptr2->a, ptr3->a);
|
|
rand = Random();
|
|
item = (rand >> 0x18);
|
|
item &= 0xF;
|
|
rand = rand % summOdds;
|
|
{
|
|
u32 r3;
|
|
for (r3 = 0, r1 = 0; r3 < 0x10; r3++, item = (item + 1) & 0xF) {
|
|
if ((r1 += droptable.a[item]) > rand) {
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
r1 = gUnk_080FE1B4[item];
|
|
if (r1 != ITEM_NONE) {
|
|
return CreateItemDrop(arg0, r1, 0);
|
|
}
|
|
}
|
|
}
|
|
return ITEM_NONE;
|
|
}
|
|
|
|
extern u8 gUnk_080FE1DD[3][0x40];
|
|
u32 CreateItemDrop(Entity* arg0, u32 itemId, u32 itemParameter) {
|
|
u32 adjustedParam = itemParameter;
|
|
Entity* itemEntity;
|
|
|
|
switch (itemId) {
|
|
case ITEM_ENEMY_BEETLE:
|
|
if (!GetInventoryValue(ITEM_SMITH_SWORD)) {
|
|
return ITEM_NONE;
|
|
}
|
|
break;
|
|
case ITEM_BOMBS5:
|
|
if (!GetInventoryValue(ITEM_BOMBBAG)) {
|
|
return ITEM_NONE;
|
|
}
|
|
break;
|
|
case ITEM_ARROWS5:
|
|
if (!GetInventoryValue(ITEM_BOW)) {
|
|
return ITEM_NONE;
|
|
}
|
|
break;
|
|
case ITEM_SHELLS: {
|
|
if (!GetInventoryValue(ITEM_EARTH_ELEMENT)) {
|
|
return ITEM_NONE;
|
|
}
|
|
if (itemParameter == 0) {
|
|
adjustedParam = 1;
|
|
}
|
|
break;
|
|
}
|
|
case ITEM_KINSTONE:
|
|
case ITEM_KINSTONE_RED ... ITEM_KINSTONE_GREEN: {
|
|
u32 rand;
|
|
|
|
if (GetInventoryValue(ITEM_KINSTONE_BAG) == 0) {
|
|
return ITEM_NONE;
|
|
}
|
|
if (3 < gRoomVars.filler1[0]) {
|
|
return ITEM_NONE;
|
|
}
|
|
|
|
if (itemId != ITEM_KINSTONE) {
|
|
adjustedParam = itemId - ITEM_KINSTONE_RED;
|
|
rand = (Random() & 0x3f);
|
|
adjustedParam = gUnk_080FE1DD[adjustedParam][rand];
|
|
if (adjustedParam == 0) {
|
|
itemId = ITEM_NONE;
|
|
} else {
|
|
itemId = ITEM_KINSTONE;
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
if (itemId != ITEM_NONE) {
|
|
if (itemId != ITEM_ENEMY_BEETLE) {
|
|
itemEntity = CreateObject(GROUND_ITEM, itemId, adjustedParam);
|
|
if (itemEntity != NULL) {
|
|
if (arg0 == &gPlayerEntity) {
|
|
itemEntity->timer = 1;
|
|
} else {
|
|
itemEntity->timer = 0;
|
|
}
|
|
if (arg0->kind == OBJECT) {
|
|
if (arg0->id == 99) {
|
|
arg0->child = itemEntity;
|
|
} else if (arg0->id == 0x1e) {
|
|
itemEntity->direction = arg0->animationState << 3 | 0x80;
|
|
itemEntity->speed = 0xc0;
|
|
itemEntity->zVelocity = Q_16_16(1.5);
|
|
}
|
|
}
|
|
CopyPosition(arg0, itemEntity);
|
|
}
|
|
} else {
|
|
itemEntity = CreateEnemy(BEETLE, 0);
|
|
if (itemEntity != NULL) {
|
|
itemEntity->x.HALF.HI = arg0->x.HALF.HI;
|
|
itemEntity->y.HALF.HI = arg0->y.HALF.HI;
|
|
itemEntity->collisionLayer = arg0->collisionLayer;
|
|
UpdateSpriteForCollisionLayer(itemEntity);
|
|
}
|
|
}
|
|
}
|
|
return itemId;
|
|
}
|