tmc/src/itemUtils.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;
}