#include "toBeSorted/special_item_drop_mgr.h" #include "c/c_lib.h" #include "c/c_math.h" #include "common.h" #include "d/a/d_a_item.h" #include "d/a/d_a_itembase.h" #include "d/a/d_a_player.h" #include "d/d_pouch.h" #include "d/d_sc_game.h" #include "m/m_angle.h" #include "m/m_vec.h" SpecialItemDropMgr *SpecialItemDropMgr::sInstance = nullptr; extern "C" void spawnItem1(u32 itemid, u32 roomid, mVec3_c *pos, mAng3_c *rot, u32 param2, u32 unk); extern "C" void spawnItem2(u32 itemid, u32 roomid, mVec3_c *pos, mAng3_c *rot, u32 param2, u32 unk); extern "C" void spawnItem3(u32 itemid, u32 roomid, mVec3_c *pos, mAng3_c *rot, u32 param2, u32 unk); extern "C" void spawnItem4(u32 itemid, u32, u32 param2); extern "C" void spawnItem5(u32 itemid, u32 roomid, mVec3_c *pos, mAng3_c *rot, u32 param2, u32 unk); extern "C" void spawnItem6(u32 itemid, u32 roomid, mVec3_c *pos, mAng3_c *rot, u32 param2, u32 unk); extern "C" void spawnItem7(u32 itemid, u32 roomid, mVec3_c *pos, mAng3_c *rot, u32 param2, u32 unk); extern "C" void spawnItem8(u32 itemid, u32 roomid, mVec3_c *pos, mAng3_c *rot, void *, void *, u32 param2, u32 unk); extern "C" void spawnItem9(u32 itemid, u32 roomid, mVec3_c *pos, mAng3_c *rot); SpecialItemDropMgr::SpecialItemDropMgr() { SpecialItemDropMgr::sInstance = this; } SpecialItemDropMgr *SpecialItemDropMgr::create() { return new SpecialItemDropMgr(); } struct DropEntry { /* 0x0 */ u8 mEntryIndex; /* 0x1 */ u8 mDropChance; }; struct DropList { /* 0x0 */ u16 mLen; /* 0x2 */ u16 mFlags; /* 0x4 */ const DropEntry *mpEntries; }; static const DropEntry e01[] = { {0x01, 20}, {0x03, 40}, {0x04, 10}, }; static const DropEntry e02[] = { {0x01, 20}, {0x03, 10}, {0x04, 40}, }; static const DropEntry e03[] = { {0x01, 20}, {0x04, 25}, {0x05, 25}, }; static const DropEntry e04[] = { {0x01, 100}, }; static const DropEntry e05[] = { {0x01, 60}, {0x02, 10}, }; static const DropEntry e06[] = { {0x02, 100}, }; static const DropEntry e07[] = { {0x03, 40}, {0x04, 10}, }; static const DropEntry e08[] = { {0x03, 50}, {0x04, 40}, {0x05, 10}, }; static const DropEntry e09[] = { {0x04, 70}, {0x05, 30}, }; static const DropEntry e10[] = { {0x06, 100}, }; static const DropEntry e11[] = { {0x08, 100}, }; static const DropEntry e12[] = { {0x0A, 100}, }; static const DropEntry e13[] = { {0x0B, 100}, }; static const DropEntry e14[] = { {0x0C, 100}, }; static const DropEntry e15[] = { {0x0D, 100}, }; static const DropEntry e16[] = { {0x03, 7}, {0x04, 3}, }; static const DropEntry e17[] = { {0x01, 7}, {0x03, 3}, }; static const DropEntry e18[] = { {0x03, 100}, }; static const DropEntry e19[] = { {0x04, 100}, }; static const DropEntry e20[] = { {0x05, 100}, }; static const DropEntry e21[] = { {0x0E, 100}, }; static const DropEntry e22[] = { {0x0F, 100}, }; static const DropEntry e23[] = { {0x03, 60}, {0x04, 30}, {0x0F, 10}, }; static const DropEntry e24[] = { {0x03, 30}, {0x04, 30}, {0x05, 20}, {0x0F, 20}, }; static const DropEntry e25[] = { {0x10, 100}, }; static const DropEntry e26[] = { {0x12, 100}, }; static const DropEntry e27[] = { {0x13, 100}, }; static const DropEntry e28[] = { {0x14, 100}, }; static const DropEntry e29[] = { {0x10, 10}, }; static const DropEntry e30[] = { {0x0B, 100}, }; static const DropEntry e31[] = { {0x01, 100}, }; static const DropEntry e32[] = { {0x01, 12}, }; static const DropEntry e33[] = { {0x01, 35}, }; static const DropEntry e34[] = { {0x16, 30}, {0x15, 10}, {0x02, 20}, {0x01, 20}, }; static const DropEntry e35[] = { {0x15, 100}, }; static const DropEntry e36[] = { {0x16, 100}, }; static const DropEntry e37[] = { {0x01, 20}, {0x03, 30}, {0x04, 15}, }; static const DropEntry e38[] = { {0x04, 30}, {0x05, 5}, {0x03, 45}, }; static const DropEntry e39[] = { {0x01, 10}, }; static const DropEntry e40[] = { {0x03, 10}, {0x04, 5}, }; static const DropEntry e41[] = { {0x17, 100}, }; static const DropEntry e42[] = { {0x01, 20}, }; static const DropEntry e44[] = { {0x01, 7}, }; static const DropEntry e45[] = { {0x03, 14}, {0x04, 6}, }; static const DropEntry e46[] = { {0x01, 7}, {0x03, 13}, }; static const DropEntry e47[] = { {0x18, 100}, }; static const DropEntry e48[] = { {0x01, 7}, }; static const DropEntry e49[] = { {0x01, 14}, }; static const DropEntry e50[] = { {0x01, 50}, }; static const DropEntry e51[] = { {0x0B, 20}, {0x03, 20}, {0x04, 10}, }; static const DropEntry e52[] = { {0x0A, 60}, {0x01, 20}, {0x04, 20}, }; static const DropEntry e53[] = { {0x08, 20}, {0x0A, 20}, {0x0B, 20}, {0x03, 10}, {0x04, 5}, }; #define FLAG_EXTRA_HEARTS 1 #define FLAG_EXTRA_RUPEES 2 #define FLAG_EXTRA_ALL (FLAG_EXTRA_HEARTS | FLAG_EXTRA_RUPEES) static const DropList sList[] = { {0, 0, nullptr}, {3, FLAG_EXTRA_ALL, e01}, {3, FLAG_EXTRA_ALL, e02}, {3, FLAG_EXTRA_ALL, e03}, {1, FLAG_EXTRA_ALL, e04}, {2, FLAG_EXTRA_ALL, e05}, {1, FLAG_EXTRA_ALL, e06}, {2, FLAG_EXTRA_ALL, e07}, {3, FLAG_EXTRA_ALL, e08}, {2, FLAG_EXTRA_ALL, e09}, {1, 0, e10}, {1, 0, e11}, {1, 0, e12}, {1, 0, e13}, {1, 0, e14}, {1, 0, e15}, {2, FLAG_EXTRA_ALL, e16}, {2, FLAG_EXTRA_ALL, e17}, {1, 0, e18}, {1, 0, e19}, {1, 0, e20}, {1, 0, e21}, {1, 0, e22}, {3, FLAG_EXTRA_ALL, e23}, {4, FLAG_EXTRA_ALL, e24}, {1, 0, e25}, {1, 0, e26}, {1, 0, e27}, {1, 0, e28}, {1, 0, e29}, {1, 0, e30}, {1, FLAG_EXTRA_ALL, e31}, {1, FLAG_EXTRA_ALL, e32}, {1, FLAG_EXTRA_ALL, e33}, {4, FLAG_EXTRA_ALL, e34}, {1, 0, e35}, {1, 0, e36}, {3, FLAG_EXTRA_ALL, e37}, {3, FLAG_EXTRA_ALL, e38}, {1, 0, e39}, {2, 0, e40}, {1, 0, e41}, {1, FLAG_EXTRA_HEARTS, e42}, {0, 0, nullptr}, {1, FLAG_EXTRA_HEARTS, e44}, {2, FLAG_EXTRA_ALL, e45}, {2, FLAG_EXTRA_ALL, e46}, {1, 0, e47}, {1, FLAG_EXTRA_HEARTS, e48}, {1, FLAG_EXTRA_HEARTS, e49}, {1, 0, e50}, {3, FLAG_EXTRA_ALL, e51}, {3, 0, e52}, {5, FLAG_EXTRA_RUPEES, e53}, }; struct LowHealthReplacement { s32 mOriginalIdx; s32 mReplacementIndex; }; const LowHealthReplacement LOW_HEALTH_REPLACEMENTS[] = { { 0x10, 0x11, }, { 0x1E, 0x1F, }, { 0x2B, 0x2C, }, { 0x2D, 0x2E, }, }; static const u16 SPECIAL_ITEM_ARRAY[] = {ITEM_NONE, ITEM_HEART, ITEM_HEART, ITEM_GREEN_RUPEE, ITEM_BLUE_RUPEE, ITEM_RED_RUPEE, ITEM_FARORE_TEAR, ITEM_5_BOMBS, ITEM_10_BOMBS, ITEM_SINGLE_ARROW, ITEM_BUNDLE_ARROWS, ITEM_5_DEKU_SEEDS, ITEM_NONE, ITEM_NONE, ITEM_KEY_PIECE, ITEM_ELDIN_ORE, ITEM_UNK_58, ITEM_UNK_59, ITEM_UNK_183, ITEM_UNK_184, ITEM_UNK_185, ITEM_FAIRY, ITEM_FAIRY, ITEM_SMALL_KEY, ITEM_RUPOOR}; static const u32 sNumDropEntries = 0x36; static const struct { u16 mNumEntries; const DropList *mList; } sDropTable = { 0x36, sList, }; static const u16 RAND_RUPEE_ARRAY[3] = {ITEM_GREEN_RUPEE, ITEM_BLUE_RUPEE, ITEM_RED_RUPEE}; int SpecialItemDropMgr::fn_800C7BB0(int specialItemId) { const DropList *list = &sDropTable.mList[specialItemId]; const DropEntry *e = list->mpEntries; int random = cM::rndInt(100); int acc = 0; for (int i = 0; i < list->mLen; i++) { int entryIdx = e->mEntryIndex; int weight = e->mDropChance; if (entryIdx == 9 || entryIdx == 10) { if (dAcPy_c::getCurrentBowType() == 0) { // No arrows until you get the Bow weight = 0; } } else if (entryIdx == 11) { if (dAcPy_c::getCurrentSlingshotType() == 0) { // No seeds until you get the Slingshot weight = 0; } } else if (specialItemId != 0x27 && (entryIdx == 1 || entryIdx == 2) && dScGame_c::isHeroMode()) { // Hearts generally don't drop on Hero Mode unless it's a special kind of heart (TODO which?) weight = 0; } else if ((entryIdx == 7 || entryIdx == 8) && !dAcItem_c::checkFlag(ITEM_BOMB_BAG)) { // Bombs won't drop until you get the Bomb Bag weight = 0; } if (weight != 0) { int x = 0x1E / weight; acc += weight; if (x != 0) { int tmp = x <= 3 ? x : 3; if (random % tmp != 0) { return 0; } } if (random < acc) { return entryIdx; } } e++; } return 0; } bool SpecialItemDropMgr::shouldTryExtraHearts(int specialItemId) { return sDropTable.mList[specialItemId].mFlags & FLAG_EXTRA_HEARTS; } bool SpecialItemDropMgr::shouldTryExtraRupees(int specialItemId) { return sDropTable.mList[specialItemId].mFlags & FLAG_EXTRA_RUPEES; } int SpecialItemDropMgr::giveSpecialDropItem( int specialItemId, int roomid, mVec3_c *pos, int subtype, mAng rot, s32 unused ) { if (specialItemId == 0xFF) { return 0; } for (u32 i = 0; i < 4; i++) { const LowHealthReplacement &entry = LOW_HEALTH_REPLACEMENTS[i]; if (specialItemId == entry.mOriginalIdx && dAcPy_c::LINK->hasLessThanQuarterHealth(false)) { specialItemId = entry.mReplacementIndex; break; } } bool ret = spawnSpecialDropItem(specialItemId, roomid, pos, subtype, rot); if (shouldTryExtraHearts(specialItemId)) { for (u32 i = 0; i < adventurePouchCountItem(ITEM_HEART_MEDAL); i++) { bool ok = spawnSpecialDropItem(0x27, roomid, pos, subtype, rot); ret |= ok; } } if (shouldTryExtraRupees(specialItemId)) { for (u32 i = 0; i < adventurePouchCountItem(ITEM_RUPEE_MEDAL) + adventurePouchCountItem(ITEM_CURSED_MEDAL); i++) { bool ok = spawnSpecialDropItem(0x28, roomid, pos, subtype, rot); ret |= ok; } } return ret; } static s32 SOME_ANG = -3641; // Very unmatching. Just here as a starting point bool SpecialItemDropMgr::spawnSpecialDropItem(int specialItemId, int roomid, mVec3_c *pos, int subtype, mAng rot) { s32 unk = fn_800C7BB0(specialItemId); if (unk == 0) { return false; } s8 itemCount = 1; if (unk == 2) { itemCount = 3; } else if (unk == 12) { itemCount = 10; } else if (unk == 13) { itemCount = 5; } else if (unk == 22) { itemCount = 2; } u32 itemid = SPECIAL_ITEM_ARRAY[unk]; mAng3_c itemRot(0, 0, 0); s32 max; s32 min; if (subtype == 2 || subtype == 6) { max = rot; min = SOME_ANG; } else { max = (s16)cLib::targetAngleY(dAcPy_c::LINK->mPosition, *pos) + 0x4000; min = -0x8000; } s16 stepSize = s16(min) / itemCount; mAng range = stepSize / 2; s32 step = s16(max) + range; mAng rndMax = range / 2; mAng rndMin = -rndMax; for (int currentItemIndex = 0; currentItemIndex < itemCount; currentItemIndex++) { itemRot.y = mAng(step) + cM::rndRange(rndMin, rndMax); if (unk >= 12 && unk < 14) { itemid = RAND_RUPEE_ARRAY[cM::rndInt(3)]; spawnItem1(itemid, roomid, pos, &itemRot, 0xFFFFFFFF, 0); } else if (subtype == 2) { spawnItem2(itemid, roomid, pos, &itemRot, 0xFFFFFFFF, 0); } else { if (subtype == 1) { spawnItem3(itemid, roomid, pos, &itemRot, 0xFFFFFFFF, 0); } else if (subtype == 4) { spawnItem4(itemid, 0, 0xFFFFFFFF); } else if (subtype == 3) { spawnItem5(itemid, roomid, pos, &itemRot, 0xFFFFFFFF, 0); } else if (subtype == 5) { spawnItem6(itemid, roomid, pos, &itemRot, 0xFFFFFFFF, 0); } else if (subtype == 6) { spawnItem7(itemid, roomid, pos, &itemRot, 0xFFFFFFFF, 0); } else if (subtype == 7) { spawnItem8(itemid, roomid, pos, &itemRot, nullptr, nullptr, 0xFFFFFFFF, 0); } else { spawnItem9(itemid, roomid, pos, &itemRot); } } step = mAng(step) - stepSize; } return true; }