From 46806415142f32b06decaec038e8f59bf186deb9 Mon Sep 17 00:00:00 2001 From: lilDavid <1337lilDavid@gmail.com> Date: Tue, 23 Aug 2022 19:14:17 -0500 Subject: [PATCH] Add bombchu drops (#1257) * Add bombchu drops * Force 3D bombchu drops * Move bombchu drop conditions for better clarity * Fix grouping on chu drop condition --- libultraship/libultraship/ImGuiImpl.cpp | 4 ++ soh/include/z64actor.h | 3 +- soh/src/code/z_en_item00.c | 67 +++++++++++++++++++++++-- 3 files changed, 69 insertions(+), 5 deletions(-) diff --git a/libultraship/libultraship/ImGuiImpl.cpp b/libultraship/libultraship/ImGuiImpl.cpp index f35ab484d..fdaeb4eee 100644 --- a/libultraship/libultraship/ImGuiImpl.cpp +++ b/libultraship/libultraship/ImGuiImpl.cpp @@ -1359,6 +1359,8 @@ namespace SohImGui { ); PaddedEnhancementCheckbox("No Random Drops", "gNoRandomDrops", true, false); Tooltip("Disables random drops, except from the Goron Pot, Dampe, and bosses"); + PaddedEnhancementCheckbox("Enable Bombchu Drops", "gBombchuDrops", true, false); + Tooltip("Bombchus will sometimes drop in place of bombs"); PaddedEnhancementCheckbox("No Heart Drops", "gNoHeartDrops", true, false); Tooltip("Disables heart drops, but not heart placements, like from a Deku Scrub running off\nThis simulates Hero Mode from other games in the series"); PaddedEnhancementCheckbox("Always Win Goron Pot", "gGoronPot", true, false); @@ -2228,6 +2230,8 @@ namespace SohImGui { CVar_SetS32("gNoRandomDrops", 0); // No Heart Drops CVar_SetS32("gNoHeartDrops", 0); + // Enable Bombchu Drops + CVar_SetS32("gBombchuDrops", 0); // Always Win Goron Pot CVar_SetS32("gGoronPot", 0); diff --git a/soh/include/z64actor.h b/soh/include/z64actor.h index cd58ac20b..01eb28a1d 100644 --- a/soh/include/z64actor.h +++ b/soh/include/z64actor.h @@ -271,7 +271,8 @@ typedef enum { /* 0x16 */ ITEM00_SHIELD_HYLIAN, /* 0x17 */ ITEM00_TUNIC_ZORA, /* 0x18 */ ITEM00_TUNIC_GORON, - /* 0x19 */ ITEM00_BOMBS_SPECIAL + /* 0x19 */ ITEM00_BOMBS_SPECIAL, + /* 0x20 */ ITEM00_BOMBCHU, } Item00Type; struct EnItem00; diff --git a/soh/src/code/z_en_item00.c b/soh/src/code/z_en_item00.c index a50c74e87..54beafdab 100644 --- a/soh/src/code/z_en_item00.c +++ b/soh/src/code/z_en_item00.c @@ -70,6 +70,7 @@ static void* sItemDropTex[] = { gDropRecoveryHeartTex, gDropBombTex, gDropArrows1Tex, gDropArrows2Tex, gDropArrows3Tex, gDropBombTex, gDropDekuNutTex, gDropDekuStickTex, gDropMagicLargeTex, gDropMagicSmallTex, gDropDekuSeedsTex, gDropKeySmallTex, + // OTRTODO: use 2D bombchu texture }; static u8 sItemDropIds[] = { @@ -390,6 +391,7 @@ void EnItem00_Init(Actor* thisx, GlobalContext* globalCtx) { case ITEM00_RUPEE_ORANGE: case ITEM00_RUPEE_PURPLE: case ITEM00_FLEXIBLE: + case ITEM00_BOMBCHU: yOffset = 500.0f; Actor_SetScale(&this->actor, 0.01f); this->scale = 0.01f; @@ -507,6 +509,9 @@ void EnItem00_Init(Actor* thisx, GlobalContext* globalCtx) { case ITEM00_TUNIC_GORON: case ITEM00_BOMBS_SPECIAL: break; + case ITEM00_BOMBCHU: + Item_Give(globalCtx, ITEM_BOMBCHUS_5); + break; } if (!Actor_HasParent(&this->actor, globalCtx)) { @@ -537,7 +542,8 @@ void func_8001DFC8(EnItem00* this, GlobalContext* globalCtx) { (this->actor.params == ITEM00_HEART_PIECE)) { this->actor.shape.rot.y += 960; } else { - if ((this->actor.params >= ITEM00_SHIELD_DEKU) && (this->actor.params != ITEM00_BOMBS_SPECIAL)) { + if ((this->actor.params >= ITEM00_SHIELD_DEKU) && (this->actor.params != ITEM00_BOMBS_SPECIAL) && + (this->actor.params != ITEM00_BOMBCHU)) { if (this->unk_15A == -1) { if (Math_SmoothStepToS(&this->actor.shape.rot.x, this->actor.world.rot.x - 0x4000, 2, 3000, 1500) == 0) { @@ -640,7 +646,8 @@ void func_8001E304(EnItem00* this, GlobalContext* globalCtx) { if (this->actor.params <= ITEM00_RUPEE_RED) { this->actor.shape.rot.y += 960; - } else if ((this->actor.params >= ITEM00_SHIELD_DEKU) && (this->actor.params != ITEM00_BOMBS_SPECIAL)) { + } else if ((this->actor.params >= ITEM00_SHIELD_DEKU) && (this->actor.params != ITEM00_BOMBS_SPECIAL) && + (this->actor.params != ITEM00_BOMBCHU)) { this->actor.world.rot.x -= 700; this->actor.shape.rot.y += 400; this->actor.shape.rot.x = this->actor.world.rot.x - 0x4000; @@ -722,7 +729,8 @@ void EnItem00_Update(Actor* thisx, GlobalContext* globalCtx) { EnItem00* this = (EnItem00*)thisx; s32 pad; - if (CVar_GetS32("gNewDrops", 0)) { //set the rotation system on selected model only :) + // OTRTODO: remove special case for bombchu when its 2D drop is implemented + if (CVar_GetS32("gNewDrops", 0) || this->actor.params == ITEM00_BOMBCHU) { //set the rotation system on selected model only :) if ((this->actor.params == ITEM00_RUPEE_GREEN) || (this->actor.params == ITEM00_RUPEE_BLUE) || (this->actor.params == ITEM00_RUPEE_RED) || (this->actor.params == ITEM00_ARROWS_SINGLE) || (this->actor.params == ITEM00_ARROWS_SMALL) || (this->actor.params == ITEM00_ARROWS_MEDIUM) || @@ -730,7 +738,8 @@ void EnItem00_Update(Actor* thisx, GlobalContext* globalCtx) { (this->actor.params == ITEM00_BOMBS_B) || (this->actor.params == ITEM00_NUTS) || (this->actor.params == ITEM00_MAGIC_SMALL) || (this->actor.params == ITEM00_SEEDS) || (this->actor.params == ITEM00_MAGIC_LARGE) || (this->actor.params == ITEM00_HEART) || - (this->actor.params == ITEM00_BOMBS_SPECIAL) || this->actor.params == ITEM00_HEART_PIECE) { + (this->actor.params == ITEM00_BOMBS_SPECIAL) || this->actor.params == ITEM00_HEART_PIECE || + (this->actor.params == ITEM00_BOMBCHU)) { this->actor.shape.rot.y += 960; } if (this->actor.params == ITEM00_SMALL_KEY && !gSaveContext.n64ddFlag) { @@ -891,6 +900,9 @@ void EnItem00_Update(Actor* thisx, GlobalContext* globalCtx) { break; case ITEM00_BOMBS_SPECIAL: break; + case ITEM00_BOMBCHU: + Item_Give(globalCtx, ITEM_BOMBCHUS_5); + break; } params = &this->actor.params; @@ -1212,6 +1224,15 @@ void EnItem00_Draw(Actor* thisx, GlobalContext* globalCtx) { EnItem00_DrawCollectible(this, globalCtx); break; } + case ITEM00_BOMBCHU: + // OTRTODO: Stop forcing chu drops to be 3D when the texture is added + Actor_SetScale(&this->actor, 0.2f); + this->scale = 0.2f; + this->actor.shape.yOffset = 50.0f; + this->actor.world.rot.x = 0x4000; + this->actor.shape.shadowScale = 0.3f; + GetItem_Draw(globalCtx, GID_BOMBCHU); + break; case ITEM00_SHIELD_DEKU: GetItem_Draw(globalCtx, GID_SHIELD_DEKU); break; @@ -1370,6 +1391,9 @@ void EnItem00_DrawCollectible(EnItem00* this, GlobalContext* globalCtx) { if (this->actor.params == ITEM00_BOMBS_SPECIAL) { texIndex = 1; + // OTRTODO: 2D bombchu drops + //} else if (this->actor.params == ITEM00_BOMBCHU) { + // texIndex = 12; } else if (this->actor.params >= ITEM00_ARROWS_SMALL) { texIndex -= 3; } @@ -1435,6 +1459,36 @@ void EnItem00_DrawHeartPiece(EnItem00* this, GlobalContext* globalCtx) { } } +/** + * Sometimes convert the given drop ID into a bombchu. + * Returns the new drop type ID. + */ +s16 EnItem00_ConvertBombDropToBombchu(s16 dropId) { + if (INV_CONTENT(ITEM_BOMBCHU) == ITEM_NONE) { + return dropId; + } + + if (INV_CONTENT(ITEM_BOMB) == ITEM_NONE) { + return ITEM00_BOMBCHU; + } + + if (AMMO(ITEM_BOMB) <= 15) { + // Player needs bombs and might need chus, so drop whichever has less + if (AMMO(ITEM_BOMB) <= AMMO(ITEM_BOMBCHU)) { + return dropId; + } else { + return ITEM00_BOMBCHU; + } + } else { + // Player has enough bombs, so drop chus if they need some, else it's 50/50 + if (AMMO(ITEM_BOMBCHU) <= 15) { + return ITEM00_BOMBCHU; + } else { + return Rand_Next() % 2 ? dropId : ITEM00_BOMBCHU; + } + } +} + /** * Converts a given drop type ID based on link's current age, health and owned items. * Returns a new drop type ID or -1 to cancel the drop. @@ -1452,6 +1506,11 @@ s16 func_8001F404(s16 dropId) { } } + if (CVar_GetS32("gBombchuDrops", 0) && + (dropId == ITEM00_BOMBS_A || dropId == ITEM00_BOMBS_B || dropId == ITEM00_BOMBS_SPECIAL)) { + dropId = EnItem00_ConvertBombDropToBombchu(dropId); + } + // This is convoluted but it seems like it must be a single condition to match // clang-format off if (((dropId == ITEM00_BOMBS_A || dropId == ITEM00_BOMBS_SPECIAL || dropId == ITEM00_BOMBS_B) && INV_CONTENT(ITEM_BOMB) == ITEM_NONE) ||