From 0ce2d30605f252055215cad42216816a2c03659c Mon Sep 17 00:00:00 2001 From: Adam Bird Date: Sun, 20 Aug 2023 14:00:43 -0400 Subject: [PATCH] add visualization for pause menu item cycling (#3118) --- .../misc/ovl_kaleido_scope/z_kaleido_item.c | 166 ++++++++++++++++-- 1 file changed, 156 insertions(+), 10 deletions(-) diff --git a/soh/src/overlays/misc/ovl_kaleido_scope/z_kaleido_item.c b/soh/src/overlays/misc/ovl_kaleido_scope/z_kaleido_item.c index 0cc51ec98..19bda2fed 100644 --- a/soh/src/overlays/misc/ovl_kaleido_scope/z_kaleido_item.c +++ b/soh/src/overlays/misc/ovl_kaleido_scope/z_kaleido_item.c @@ -1,8 +1,10 @@ #include "z_kaleido_scope.h" #include "textures/parameter_static/parameter_static.h" +#include "textures/icon_item_static/icon_item_static.h" #include "soh/Enhancements/randomizer/adult_trade_shuffle.h" #include "soh/Enhancements/randomizer/randomizerTypes.h" #include "soh/Enhancements/enhancementTypes.h" +#include "soh/Enhancements/cosmetics/cosmeticsTypes.h" u8 gAmmoItems[] = { ITEM_STICK, ITEM_NUT, ITEM_BOMB, ITEM_BOW, ITEM_NONE, ITEM_NONE, ITEM_SLINGSHOT, ITEM_NONE, @@ -85,6 +87,139 @@ void KaleidoScope_SetItemCursorVtx(PauseContext* pauseCtx) { KaleidoScope_SetCursorVtx(pauseCtx, pauseCtx->cursorSlot[PAUSE_ITEM] * 4, pauseCtx->itemVtx); } +// Vertices for the extra items +static Vtx sCycleExtraItemVtx[] = { + // Left Item + VTX(-48, 16, 0, 0 << 5, 0 << 5, 0xFF, 0xFF, 0xFF, 0xFF), + VTX(-16, 16, 0, 32 << 5, 0 << 5, 0xFF, 0xFF, 0xFF, 0xFF), + VTX(-48, -16, 0, 0 << 5, 32 << 5, 0xFF, 0xFF, 0xFF, 0xFF), + VTX(-16, -16, 0, 32 << 5, 32 << 5, 0xFF, 0xFF, 0xFF, 0xFF), + // Right Item + VTX(16, 16, 0, 0 << 5, 0 << 5, 0xFF, 0xFF, 0xFF, 0xFF), + VTX(48, 16, 0, 32 << 5, 0 << 5, 0xFF, 0xFF, 0xFF, 0xFF), + VTX(16, -16, 0, 0 << 5, 32 << 5, 0xFF, 0xFF, 0xFF, 0xFF), + VTX(48, -16, 0, 32 << 5, 32 << 5, 0xFF, 0xFF, 0xFF, 0xFF), +}; + +// Vertices for the circle behind the items +static Vtx sCycleCircleVtx[] = { + // Left Item + VTX(-56, 24, 0, 0 << 5, 0 << 5, 0xFF, 0xFF, 0xFF, 0xFF), + VTX(-8, 24, 0, 48 << 5, 0 << 5, 0xFF, 0xFF, 0xFF, 0xFF), + VTX(-56, -24, 0, 0 << 5, 48 << 5, 0xFF, 0xFF, 0xFF, 0xFF), + VTX(-8, -24, 0, 48 << 5, 48 << 5, 0xFF, 0xFF, 0xFF, 0xFF), + // Right Item + VTX(8, 24, 0, 0 << 5, 0 << 5, 0xFF, 0xFF, 0xFF, 0xFF), + VTX(56, 24, 0, 48 << 5, 0 << 5, 0xFF, 0xFF, 0xFF, 0xFF), + VTX(8, -24, 0, 0 << 5, 48 << 5, 0xFF, 0xFF, 0xFF, 0xFF), + VTX(56, -24, 0, 48 << 5, 48 << 5, 0xFF, 0xFF, 0xFF, 0xFF), +}; + +// Vertices for A button indicator (coordinates 1.5x larger than texture size) +static Vtx sCycleAButtonVtx[] = { + VTX(-18, 12, 0, 0 << 5, 0 << 5, 0xFF, 0xFF, 0xFF, 0xFF), + VTX(18, 12, 0, 24 << 5, 0 << 5, 0xFF, 0xFF, 0xFF, 0xFF), + VTX(-18, -12, 0, 0 << 5, 16 << 5, 0xFF, 0xFF, 0xFF, 0xFF), + VTX(18, -12, 0, 24 << 5, 16 << 5, 0xFF, 0xFF, 0xFF, 0xFF), +}; + +// Track animation timers for each inventory slot +static sSlotCycleActiveAnimTimer[24] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; + +// Renders a left and/or right item for any item slot that can support cycling +void KaleidoScope_DrawItemCycleExtras(PlayState* play, u8 slot, u8 isCycling, u8 canCycle, u8 leftItem, u8 rightItem) { + PauseContext* pauseCtx = &play->pauseCtx; + + OPEN_DISPS(play->state.gfxCtx); + + // Update active cycling animation timer + if (isCycling) { + if (sSlotCycleActiveAnimTimer[slot] < 5) { + sSlotCycleActiveAnimTimer[slot]++; + } + } else { + if (sSlotCycleActiveAnimTimer[slot] > 0) { + sSlotCycleActiveAnimTimer[slot]--; + } + } + + u8 slotItem = gSaveContext.inventory.items[slot]; + u8 showLeftItem = leftItem != ITEM_NONE && slotItem != leftItem; + u8 showRightItem = rightItem != ITEM_NONE && slotItem != rightItem && leftItem != rightItem; + + // Render the extra cycle items if at least the left or right item are valid + if (canCycle && slotItem != ITEM_NONE && (showLeftItem || showRightItem)) { + Matrix_Push(); + + Vtx* itemTopLeft = &pauseCtx->itemVtx[slot * 4]; + Vtx* itemBottomRight = &itemTopLeft[3]; + + s16 halfX = (itemBottomRight->v.ob[0] - itemTopLeft->v.ob[0]) / 2; + s16 halfY = (itemBottomRight->v.ob[1] - itemTopLeft->v.ob[1]) / 2; + + Matrix_Translate(itemTopLeft->v.ob[0] + halfX, itemTopLeft->v.ob[1] + halfY, 0, MTXMODE_APPLY); + + f32 animScale = (f32)(5 - sSlotCycleActiveAnimTimer[slot]) / 5; + + // When not cycling or actively animating, shrink and move the items under the main slot item + if (!isCycling || sSlotCycleActiveAnimTimer[slot] < 5) { + f32 finalScale = 1.0f - (0.675f * animScale); + Matrix_Translate(0, -15.0f * animScale, 0, MTXMODE_APPLY); + Matrix_Scale(finalScale, finalScale, 1.0f, MTXMODE_APPLY); + } + + gSPMatrix(POLY_KAL_DISP++, MATRIX_NEWMTX(play->state.gfxCtx), G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + + // Render A button indicator when hovered and not cycling + if (!isCycling && sSlotCycleActiveAnimTimer[slot] == 0 && pauseCtx->cursorSlot[PAUSE_ITEM] == slot && + pauseCtx->cursorSpecialPos == 0) { + Color_RGB8 aButtonColor = { 0, 100, 255 }; + if (CVarGetInteger("gCosmetics.Hud_AButton.Changed", 0)) { + aButtonColor = CVarGetColor24("gCosmetics.Hud_AButton.Value", aButtonColor); + } else if (CVarGetInteger("gCosmetics.DefaultColorScheme", COLORSCHEME_N64) == COLORSCHEME_GAMECUBE) { + aButtonColor = (Color_RGB8){ 0, 255, 100 }; + } + + gSPVertex(POLY_KAL_DISP++, sCycleAButtonVtx, 4, 0); + gDPSetPrimColor(POLY_KAL_DISP++, 0, 0, aButtonColor.r, aButtonColor.g, aButtonColor.b, pauseCtx->alpha); + gDPLoadTextureBlock(POLY_KAL_DISP++, gABtnSymbolTex, G_IM_FMT_IA, G_IM_SIZ_8b, 24, 16, 0, + G_TX_NOMIRROR | G_TX_CLAMP, G_TX_NOMIRROR | G_TX_CLAMP, 4, 4, G_TX_NOLOD, G_TX_NOLOD); + gSP1Quadrangle(POLY_KAL_DISP++, 0, 2, 3, 1, 0); + } + + // Render a dark circle behind the extra items when cycling + if (isCycling) { + gSPVertex(POLY_KAL_DISP++, sCycleCircleVtx, 8, 0); + gDPSetPrimColor(POLY_KAL_DISP++, 0, 0, 0, 0, 0, pauseCtx->alpha * (1.0f - animScale)); + gDPLoadTextureBlock_4b(POLY_KAL_DISP++, gPausePromptCursorTex, G_IM_FMT_I, 48, 48, 0, + G_TX_NOMIRROR | G_TX_WRAP, G_TX_NOMIRROR | G_TX_WRAP, G_TX_NOMASK, G_TX_NOMASK, + G_TX_NOLOD, G_TX_NOLOD); + + if (showLeftItem) { + gSP1Quadrangle(POLY_KAL_DISP++, 0, 2, 3, 1, 0); + } + if (showRightItem) { + gSP1Quadrangle(POLY_KAL_DISP++, 4, 6, 7, 5, 0); + } + } + + // Render left and right items + gDPSetPrimColor(POLY_KAL_DISP++, 0, 0, 255, 255, 255, pauseCtx->alpha); + gSPVertex(POLY_KAL_DISP++, sCycleExtraItemVtx, 8, 0); + + if (showLeftItem) { + KaleidoScope_DrawQuadTextureRGBA32(play->state.gfxCtx, gItemIcons[leftItem], 32, 32, 0); + } + if (showRightItem) { + KaleidoScope_DrawQuadTextureRGBA32(play->state.gfxCtx, gItemIcons[rightItem], 32, 32, 4); + } + + Matrix_Pop(); + } + + CLOSE_DISPS(play->state.gfxCtx); +} + void KaleidoScope_DrawItemSelect(PlayState* play) { static s16 magicArrowEffectsR[] = { 255, 100, 255 }; static s16 magicArrowEffectsG[] = { 0, 100, 255 }; @@ -105,6 +240,16 @@ void KaleidoScope_DrawItemSelect(PlayState* play) { bool pauseAnyCursor = (CVarGetInteger("gPauseAnyCursor", 0) == PAUSE_ANY_CURSOR_RANDO_ONLY && gSaveContext.n64ddFlag) || (CVarGetInteger("gPauseAnyCursor", 0) == PAUSE_ANY_CURSOR_ALWAYS_ON); + // only allow mask select when: + // the shop is open: + // * zelda's letter check: Flags_GetEventChkInf(EVENTCHKINF_OBTAINED_ZELDAS_LETTER) + // * kak gate check: Flags_GetInfTable(INFTABLE_SHOWED_ZELDAS_LETTER_TO_GATE_GUARD) + // and the mask quest is complete: Flags_GetEventChkInf(EVENTCHKINF_PAID_BACK_BUNNY_HOOD_FEE) + bool canMaskSelect = CVarGetInteger("gMaskSelect", 0) && + Flags_GetEventChkInf(EVENTCHKINF_PAID_BACK_BUNNY_HOOD_FEE) && + Flags_GetEventChkInf(EVENTCHKINF_OBTAINED_ZELDAS_LETTER) && + Flags_GetInfTable(INFTABLE_SHOWED_ZELDAS_LETTER_TO_GATE_GUARD); + OPEN_DISPS(play->state.gfxCtx); Gfx_SetupDL_42Opa(play->state.gfxCtx); @@ -354,16 +499,7 @@ void KaleidoScope_DrawItemSelect(PlayState* play) { KaleidoScope_SetCursorVtx(pauseCtx, index, pauseCtx->itemVtx); if ((pauseCtx->debugState == 0) && (pauseCtx->state == 6) && (pauseCtx->unk_1E4 == 0)) { - // only allow mask select when: - // the shop is open: - // * zelda's letter check: Flags_GetEventChkInf(EVENTCHKINF_OBTAINED_ZELDAS_LETTER) - // * kak gate check: Flags_GetInfTable(INFTABLE_SHOWED_ZELDAS_LETTER_TO_GATE_GUARD) - // and the mask quest is complete: Flags_GetEventChkInf(EVENTCHKINF_PAID_BACK_BUNNY_HOOD_FEE) - if (CVarGetInteger("gMaskSelect", 0) && - (Flags_GetEventChkInf(EVENTCHKINF_PAID_BACK_BUNNY_HOOD_FEE)) && - cursorSlot == SLOT_TRADE_CHILD && CHECK_BTN_ALL(input->press.button, BTN_A) && - (Flags_GetEventChkInf(EVENTCHKINF_OBTAINED_ZELDAS_LETTER)) && - (Flags_GetInfTable(INFTABLE_SHOWED_ZELDAS_LETTER_TO_GATE_GUARD))) { + if (canMaskSelect && cursorSlot == SLOT_TRADE_CHILD && CHECK_BTN_ALL(input->press.button, BTN_A)) { Audio_PlaySoundGeneral(NA_SE_SY_DECIDE, &D_801333D4, 4, &D_801333E0, &D_801333E0, &D_801333E8); gSelectingMask = !gSelectingMask; } @@ -542,6 +678,16 @@ void KaleidoScope_DrawItemSelect(PlayState* play) { } } + // Adult trade item cycle + KaleidoScope_DrawItemCycleExtras(play, SLOT_TRADE_ADULT, gSelectingAdultTrade, + gSaveContext.n64ddFlag && Randomizer_GetSettingValue(RSK_SHUFFLE_ADULT_TRADE), + Randomizer_GetPrevAdultTradeItem(), Randomizer_GetNextAdultTradeItem()); + // Child mask item cycle (mimics the left/right item behavior from the cycling logic above) + u8 childTradeItem = INV_CONTENT(ITEM_TRADE_CHILD); + KaleidoScope_DrawItemCycleExtras(play, SLOT_TRADE_CHILD, gSelectingMask, canMaskSelect, + childTradeItem <= ITEM_MASK_KEATON ? ITEM_MASK_TRUTH : childTradeItem - 1, + childTradeItem >= ITEM_MASK_TRUTH ? ITEM_MASK_KEATON : childTradeItem + 1); + CLOSE_DISPS(play->state.gfxCtx); }