mirror of
https://github.com/HarbourMasters/Shipwright
synced 2026-09-11 12:55:12 -04:00
610 lines
30 KiB
C++
610 lines
30 KiB
C++
#include <sstream>
|
|
|
|
#include "ship/utils/StringHelper.h"
|
|
#include <libultraship/bridge/consolevariablebridge.h>
|
|
|
|
#include "kaleido.h"
|
|
#include "soh/Enhancements/randomizer/SeedContext.h"
|
|
#include "soh/ShipInit.hpp"
|
|
#include "soh/ShipUtils.h"
|
|
#include "soh/OTRGlobals.h"
|
|
#include "soh/Enhancements/game-interactor/GameInteractor.h"
|
|
#include "soh/Enhancements/cosmetics/cosmeticsTypes.h"
|
|
|
|
extern "C" {
|
|
#include "objects/gameplay_keep/gameplay_keep.h"
|
|
#include "z64.h"
|
|
#include "functions.h"
|
|
#include "macros.h"
|
|
#include "variables.h"
|
|
#include <textures/parameter_static/parameter_static.h>
|
|
#include "soh_assets.h"
|
|
#include "textures/icon_item_static/icon_item_static.h"
|
|
extern PlayState* gPlayState;
|
|
void KaleidoScope_MoveCursorToSpecialPos(PlayState* play, u16 specialPos);
|
|
}
|
|
|
|
namespace Rando {
|
|
|
|
void KaleidoEntryIcon::LoadIconTex(std::vector<Gfx>* mEntryDl) {
|
|
if (mIconFormat == G_IM_FMT_IA) {
|
|
if (mIconSize == G_IM_SIZ_8b) {
|
|
Gfx iconTexture[] = { gsDPLoadTextureBlock(
|
|
mIconResourceName, G_IM_FMT_IA, G_IM_SIZ_8b, mIconWidth, mIconHeight, 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) };
|
|
mEntryDl->insert(mEntryDl->end(), std::begin(iconTexture), std::end(iconTexture));
|
|
}
|
|
} else if (mIconFormat == G_IM_FMT_RGBA) {
|
|
if (mIconSize == G_IM_SIZ_32b) {
|
|
Gfx iconTexture[] = { gsDPLoadTextureBlock(
|
|
mIconResourceName, G_IM_FMT_RGBA, G_IM_SIZ_32b, mIconWidth, mIconHeight, 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) };
|
|
mEntryDl->insert(mEntryDl->end(), std::begin(iconTexture), std::end(iconTexture));
|
|
} else if (mIconSize == G_IM_SIZ_16b) {
|
|
Gfx iconTexture[] = { gsDPLoadTextureBlock(
|
|
mIconResourceName, G_IM_FMT_RGBA, G_IM_SIZ_16b, mIconWidth, mIconHeight, 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) };
|
|
mEntryDl->insert(mEntryDl->end(), std::begin(iconTexture), std::end(iconTexture));
|
|
}
|
|
}
|
|
}
|
|
|
|
KaleidoEntry::KaleidoEntry(std::string text) : mText(std::move(text)) {
|
|
mHeight = 0;
|
|
mWidth = 0;
|
|
vtx = nullptr;
|
|
}
|
|
|
|
void KaleidoEntry::SetYOffset(int yOffset) {
|
|
mY = yOffset;
|
|
}
|
|
|
|
void KaleidoEntry::SetSelected(bool val) {
|
|
mSelected = val;
|
|
}
|
|
|
|
void KaleidoEntryIcon::Draw(PlayState* play, std::vector<Gfx>* mEntryDl) {
|
|
PauseContext* pauseCtx = &play->pauseCtx;
|
|
if (vtx == nullptr) {
|
|
return;
|
|
}
|
|
size_t numChar = mText.length();
|
|
if (numChar == 0) {
|
|
return;
|
|
}
|
|
|
|
Color_RGBA8 textColor = { 255, 255, 255, 255 };
|
|
if (mAchieved) {
|
|
textColor = { 0x98, 0xFF, 0x44, 255 };
|
|
}
|
|
|
|
Matrix_Translate(mX, mY, 0.0f, MTXMODE_APPLY);
|
|
|
|
mEntryDl->push_back(gsSPMatrix(Matrix_NewMtx(play->state.gfxCtx, (char*)__FILE__, __LINE__),
|
|
G_MTX_PUSH | G_MTX_LOAD | G_MTX_MODELVIEW));
|
|
|
|
// cursor (if selected)
|
|
if (mSelected) {
|
|
mEntryDl->push_back(gsDPSetPrimColor(0, 0, 255, 255, 255, 255));
|
|
mEntryDl->push_back(gsSPVertex(vtx, 4, 0));
|
|
Gfx cursorIconTex[] = { gsDPLoadTextureBlock(gArrowCursorTex, G_IM_FMT_IA, G_IM_SIZ_8b, 16, 24, 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) };
|
|
mEntryDl->insert(mEntryDl->end(), std::begin(cursorIconTex), std::end(cursorIconTex));
|
|
mEntryDl->push_back(gsSP1Quadrangle(0, 2, 3, 1, 0));
|
|
}
|
|
|
|
// icon
|
|
if (!mAchieved) {
|
|
mEntryDl->push_back(gsDPSetGrayscaleColor(109, 109, 109, 255));
|
|
mEntryDl->push_back(gsSPGrayscale(true));
|
|
}
|
|
mEntryDl->push_back(gsDPSetPrimColor(0, 0, mIconColor.r, mIconColor.g, mIconColor.b, mIconColor.a));
|
|
mEntryDl->push_back(gsSPVertex(&vtx[4], 4, 0));
|
|
LoadIconTex(mEntryDl);
|
|
mEntryDl->push_back(gsSP1Quadrangle(0, 2, 3, 1, 0));
|
|
mEntryDl->push_back(gsSPGrayscale(false));
|
|
|
|
// text
|
|
mEntryDl->push_back(gsDPSetPrimColor(0, 0, textColor.r, textColor.g, textColor.b, textColor.a));
|
|
for (size_t i = 0, vtxGroup = 0; i < numChar; i++) {
|
|
// A maximum of 64 Vtx can be loaded at once by gSPVertex, or basically 16 characters
|
|
// handle loading groups of 16 chars at a time until there are no more left to load.
|
|
// By this point 8 vertices have already been loaded for the preceding icon and cursor.
|
|
if (i % 16 == 0) {
|
|
size_t numVtxToLoad = std::min<size_t>(numChar - i, 16) * 4;
|
|
mEntryDl->push_back(gsSPVertex(&vtx[8 + (vtxGroup * 16 * 4)], numVtxToLoad, 0));
|
|
vtxGroup++;
|
|
}
|
|
|
|
auto texture = reinterpret_cast<uintptr_t>(Ship_GetCharFontTexture(mText[i]));
|
|
auto vertexStart = static_cast<int16_t>(4 * (i % 16));
|
|
|
|
Gfx charTexture[] = { gsDPLoadTextureBlock_4b(texture, G_IM_FMT_I, FONT_CHAR_TEX_WIDTH, FONT_CHAR_TEX_HEIGHT, 0,
|
|
G_TX_NOMIRROR | G_TX_CLAMP, G_TX_NOMIRROR | G_TX_CLAMP,
|
|
G_TX_NOMASK, G_TX_NOMASK, G_TX_NOLOD, G_TX_NOLOD) };
|
|
mEntryDl->insert(mEntryDl->end(), std::begin(charTexture), std::end(charTexture));
|
|
mEntryDl->push_back(gsSP1Quadrangle(vertexStart, vertexStart + 2, vertexStart + 3, vertexStart + 1, 0));
|
|
}
|
|
mEntryDl->push_back(gsSPPopMatrix(G_MTX_MODELVIEW));
|
|
}
|
|
|
|
Kaleido::Kaleido() {
|
|
const auto ctx = Rando::Context::GetInstance();
|
|
int yOffset = 0;
|
|
mEntries.push_back(std::make_shared<KaleidoEntryIconFlag>(
|
|
gRupeeCounterIconTex, G_IM_FMT_IA, G_IM_SIZ_8b, 16, 16, Color_RGBA8{ 0xC8, 0xFF, 0x64, 255 },
|
|
FlagType::FLAG_RANDOMIZER_INF, static_cast<int>(RAND_INF_GREG_FOUND), "Greg"));
|
|
if (ctx->GetOption(RSK_SHUFFLE_FISHING_POLE)) {
|
|
mEntries.push_back(std::make_shared<KaleidoEntryIconFlag>(
|
|
gItemIconFishingPoleTex, G_IM_FMT_RGBA, G_IM_SIZ_32b, 32, 32, Color_RGBA8{ 255, 255, 255, 255 },
|
|
FlagType::FLAG_RANDOMIZER_INF, static_cast<int>(RAND_INF_FISHING_POLE_FOUND), "Fishing Pole"));
|
|
}
|
|
if (ctx->GetOption(RSK_TRIFORCE_HUNT_PIECES_TOTAL).Get() > 0) {
|
|
mEntries.push_back(std::make_shared<KaleidoEntryIconCountRequired>(
|
|
gTriforcePieceTex, G_IM_FMT_RGBA, G_IM_SIZ_32b, 32, 32, Color_RGBA8{ 255, 255, 255, 255 },
|
|
reinterpret_cast<int*>(&gSaveContext.ship.quest.data.randomizer.triforcePiecesCollected),
|
|
ctx->GetOption(RSK_WINCON_TRIFORCE_COUNT).Get(), ctx->GetOption(RSK_TRIFORCE_HUNT_PIECES_TOTAL).Get()));
|
|
}
|
|
if (ctx->GetOption(RSK_SKELETON_KEY)) {
|
|
mEntries.push_back(std::make_shared<KaleidoEntryIconFlag>(
|
|
gSmallKeyCounterIconTex, G_IM_FMT_IA, G_IM_SIZ_8b, 16, 16, Color_RGBA8{ 255, 255, 255, 255 },
|
|
FlagType::FLAG_RANDOMIZER_INF, static_cast<int>(RAND_INF_HAS_SKELETON_KEY), "Skeleton Key"));
|
|
}
|
|
if (ctx->GetOption(RSK_SHUFFLE_OCARINA_BUTTONS)) {
|
|
mEntries.push_back(std::make_shared<KaleidoEntryOcarinaButtons>());
|
|
}
|
|
if (ctx->GetOption(RSK_SHUFFLE_BOSS_SOULS).IsNot(RO_BOSS_SOULS_OFF)) {
|
|
static const char* bossSoulNames[] = {
|
|
"Gohma's Soul", "King Dodongo's Soul", "Barinade's Soul", "Phantom Ganon's Soul",
|
|
"Volvagia's Soul", "Morpha's Soul", "Bongo Bongo's Soul", "Twinrova's Soul",
|
|
};
|
|
for (int i = RAND_INF_GOHMA_SOUL; i < RAND_INF_GANON_SOUL; i++) {
|
|
mEntries.push_back(std::make_shared<KaleidoEntryIconFlag>(
|
|
gBossSoulTex, G_IM_FMT_RGBA, G_IM_SIZ_32b, 32, 32, Color_RGBA8{ 255, 255, 255, 255 },
|
|
FlagType::FLAG_RANDOMIZER_INF, i, bossSoulNames[i - RAND_INF_GOHMA_SOUL]));
|
|
}
|
|
}
|
|
if (ctx->GetOption(RSK_GANONS_SOUL).IsNot(RO_GANONS_SOUL_STARTWITH)) {
|
|
mEntries.push_back(std::make_shared<KaleidoEntryIconFlag>(
|
|
gBossSoulTex, G_IM_FMT_RGBA, G_IM_SIZ_32b, 32, 32, Color_RGBA8{ 255, 255, 255, 255 },
|
|
FlagType::FLAG_RANDOMIZER_INF, RAND_INF_GANON_SOUL, "Ganon's Soul"));
|
|
}
|
|
if (ctx->GetOption(RSK_LOCK_OVERWORLD_DOORS)) {
|
|
int rg = RG_GUARD_HOUSE_KEY;
|
|
for (int i = RAND_INF_GUARD_HOUSE_KEY_OBTAINED; i <= RAND_INF_FISHING_HOLE_KEY_OBTAINED; i += 2, rg++) {
|
|
mEntries.push_back(std::make_shared<KaleidoEntryIconFlag>(
|
|
gSmallKeyCounterIconTex, G_IM_FMT_IA, G_IM_SIZ_8b, 16, 16, Color_RGBA8{ 255, 255, 255, 255 },
|
|
FlagType::FLAG_RANDOMIZER_INF, i,
|
|
Rando::StaticData::RetrieveItem(static_cast<RandomizerGet>(rg)).GetName().english));
|
|
}
|
|
}
|
|
// Default A button color stored for action shuffle (crawl, climb, etc.)
|
|
// TODO: find a way to update this so we can match Cosmetics Editor color, or just replace these icons
|
|
Color_RGBA8 aButtonColor = { 90, 90, 255, 255 };
|
|
if (ctx->GetOption(RSK_SHUFFLE_CRAWL)) {
|
|
mEntries.push_back(std::make_shared<KaleidoEntryIconFlag>(gButtonBackgroundTex, G_IM_FMT_IA, G_IM_SIZ_8b, 32,
|
|
32, aButtonColor, FlagType::FLAG_RANDOMIZER_INF,
|
|
RAND_INF_CAN_CRAWL, "Crawl"));
|
|
}
|
|
if (ctx->GetOption(RSK_SHUFFLE_CLIMB)) {
|
|
mEntries.push_back(std::make_shared<KaleidoEntryIconFlag>(gButtonBackgroundTex, G_IM_FMT_IA, G_IM_SIZ_8b, 32,
|
|
32, aButtonColor, FlagType::FLAG_RANDOMIZER_INF,
|
|
RAND_INF_CAN_CLIMB, "Climb"));
|
|
}
|
|
if (ctx->GetOption(RSK_SHUFFLE_GRAB)) {
|
|
mEntries.push_back(std::make_shared<KaleidoEntryIconFlag>(gButtonBackgroundTex, G_IM_FMT_IA, G_IM_SIZ_8b, 32,
|
|
32, aButtonColor, FlagType::FLAG_RANDOMIZER_INF,
|
|
RAND_INF_CAN_GRAB, "Grab"));
|
|
}
|
|
if (ctx->GetOption(RSK_SHUFFLE_SPEAK)) {
|
|
int rg = RG_SPEAK_DEKU;
|
|
for (int i = RAND_INF_CAN_SPEAK_DEKU; i <= RAND_INF_CAN_SPEAK_ZORA; i++, rg++) {
|
|
std::string speakName = Rando::StaticData::RetrieveItem(static_cast<RandomizerGet>(rg)).GetName().english;
|
|
StringHelper::ReplaceOriginal(speakName, " Jabber Nut", "");
|
|
mEntries.push_back(std::make_shared<KaleidoEntryIconFlag>(gItemIconDekuNutTex, G_IM_FMT_RGBA, G_IM_SIZ_32b,
|
|
32, 32, Color_RGBA8{ 255, 255, 255, 255 },
|
|
FlagType::FLAG_RANDOMIZER_INF, i, speakName));
|
|
}
|
|
}
|
|
if (ctx->GetOption(RSK_SHUFFLE_OPEN_CHEST)) {
|
|
mEntries.push_back(std::make_shared<KaleidoEntryIconFlag>(
|
|
gMapChestIconTex, G_IM_FMT_RGBA, G_IM_SIZ_16b, 8, 8, Color_RGBA8{ 255, 255, 255, 255 },
|
|
FlagType::FLAG_RANDOMIZER_INF, RAND_INF_CAN_OPEN_CHEST, "Open Chests"));
|
|
if (ctx->GetOption(RSK_SHUFFLE_OPEN_CHEST).Is(RO_OPEN_CHEST_PROGRESSIVE)) {
|
|
mEntries.push_back(std::make_shared<KaleidoEntryIconFlag>(
|
|
gMapChestIconTex, G_IM_FMT_RGBA, G_IM_SIZ_16b, 8, 8, Color_RGBA8{ 255, 255, 255, 255 },
|
|
FlagType::FLAG_RANDOMIZER_INF, RAND_INF_CAN_OPEN_LARGE_CHEST, "Open Large Chests"));
|
|
}
|
|
}
|
|
if (ctx->GetOption(RSK_SHUFFLE_SWIM)) {
|
|
mEntries.push_back(std::make_shared<KaleidoEntryIconFlag>(
|
|
gItemIconScaleSilverTex, G_IM_FMT_RGBA, G_IM_SIZ_32b, 32, 32, Color_RGBA8{ 255, 255, 255, 255 },
|
|
FlagType::FLAG_RANDOMIZER_INF, RAND_INF_CAN_SWIM, "Swim"));
|
|
}
|
|
if (ctx->GetOption(RSK_SHUFFLE_BEAN_SOULS)) {
|
|
int rg = RG_DEATH_MOUNTAIN_CRATER_BEAN_SOUL;
|
|
for (int i = RAND_INF_DEATH_MOUNTAIN_CRATER_BEAN_SOUL; i <= RAND_INF_ZORAS_RIVER_BEAN_SOUL; i++, rg++) {
|
|
std::string beanSoulName =
|
|
Rando::StaticData::RetrieveItem(static_cast<RandomizerGet>(rg)).GetName().english;
|
|
StringHelper::ReplaceOriginal(beanSoulName, " Bean Soul", "");
|
|
mEntries.push_back(std::make_shared<KaleidoEntryIconFlag>(
|
|
gItemIconMagicBeanTex, G_IM_FMT_RGBA, G_IM_SIZ_32b, 32, 32, Color_RGBA8{ 255, 255, 255, 255 },
|
|
FlagType::FLAG_RANDOMIZER_INF, i, std::move(beanSoulName)));
|
|
}
|
|
}
|
|
if (ctx->GetOption(RSK_ROCS_FEATHER)) {
|
|
mEntries.push_back(std::make_shared<KaleidoEntryIconFlag>(
|
|
gRocsFeatherTex, G_IM_FMT_RGBA, G_IM_SIZ_32b, 32, 32, Color_RGBA8{ 255, 255, 255, 255 },
|
|
FlagType::FLAG_RANDOMIZER_INF, RAND_INF_OBTAINED_ROCS_FEATHER, "Roc's Feather"));
|
|
}
|
|
}
|
|
|
|
extern "C" {
|
|
void FrameInterpolation_RecordCloseChild(void);
|
|
void FrameInterpolation_RecordOpenChild(const void* a, int b);
|
|
}
|
|
|
|
void Kaleido::Draw(PlayState* play) {
|
|
if (play == nullptr || mEntries.empty()) {
|
|
return;
|
|
}
|
|
PauseContext* pauseCtx = &play->pauseCtx;
|
|
Input* input = &play->state.input[0];
|
|
mEntryDl.clear();
|
|
OPEN_DISPS(play->state.gfxCtx);
|
|
mEntryDl.push_back(gsDPPipeSync());
|
|
Gfx_SetupDL_39Opa(play->state.gfxCtx);
|
|
Gfx_SetupDL_42Opa(play->state.gfxCtx);
|
|
mEntryDl.push_back(gsDPSetCombineMode(G_CC_MODULATEIA_PRIM, G_CC_MODULATEIA_PRIM));
|
|
|
|
// Move the matrix origin to the top-left corner of the kaleido page
|
|
Matrix_Translate(-108.f, 58.f, 0.0f, MTXMODE_APPLY);
|
|
// Invert the matrix to render vertices with positive going down
|
|
Matrix_Scale(1.0f, -1.0f, 1.0f, MTXMODE_APPLY);
|
|
if (((pauseCtx->unk_1E4 == 0) || (pauseCtx->unk_1E4 == 5) || (pauseCtx->unk_1E4 == 8)) &&
|
|
(pauseCtx->pageIndex == PAUSE_QUEST)) {
|
|
u16 buttonsToCheck = BTN_CLEFT | BTN_CRIGHT;
|
|
if (CVarGetInteger(CVAR_SETTING("DPadOnPause"), 0)) {
|
|
buttonsToCheck |= BTN_DUP | BTN_DDOWN | BTN_DLEFT | BTN_DRIGHT;
|
|
}
|
|
if (!((pauseCtx->state != 6) || ((pauseCtx->stickRelX == 0) && (pauseCtx->stickRelY == 0) &&
|
|
!CHECK_BTN_ANY(input->press.button, buttonsToCheck)))) {
|
|
if (pauseCtx->cursorSpecialPos == 0) {
|
|
if ((pauseCtx->stickRelY > 30) || CHECK_BTN_ALL(input->press.button, BTN_DUP)) {
|
|
if (mCursorPos > 0) {
|
|
mCursorPos--;
|
|
Audio_PlaySfxGeneral(NA_SE_SY_CURSOR, &gSfxDefaultPos, 4, &gSfxDefaultFreqAndVolScale,
|
|
&gSfxDefaultFreqAndVolScale, &gSfxDefaultReverb);
|
|
}
|
|
if (mCursorPos < mTopIndex) {
|
|
mTopIndex = mCursorPos;
|
|
}
|
|
} else if ((pauseCtx->stickRelY < -30) || CHECK_BTN_ALL(input->press.button, BTN_DDOWN)) {
|
|
if (mCursorPos < static_cast<int>(mEntries.size() - 1)) {
|
|
mCursorPos++;
|
|
Audio_PlaySfxGeneral(NA_SE_SY_CURSOR, &gSfxDefaultPos, 4, &gSfxDefaultFreqAndVolScale,
|
|
&gSfxDefaultFreqAndVolScale, &gSfxDefaultReverb);
|
|
}
|
|
if (mCursorPos >= mTopIndex + mNumVisible &&
|
|
mTopIndex + mNumVisible < static_cast<int>(mEntries.size())) {
|
|
mTopIndex = mCursorPos - mNumVisible + 1;
|
|
}
|
|
} else if (CHECK_BTN_ALL(input->press.button, BTN_CLEFT)) {
|
|
if (mCursorPos > 0) {
|
|
mCursorPos -= mNumVisible;
|
|
if (mCursorPos < 0) {
|
|
mCursorPos = 0;
|
|
}
|
|
Audio_PlaySfxGeneral(NA_SE_SY_CURSOR, &gSfxDefaultPos, 4, &gSfxDefaultFreqAndVolScale,
|
|
&gSfxDefaultFreqAndVolScale, &gSfxDefaultReverb);
|
|
}
|
|
if (mCursorPos < mTopIndex) {
|
|
mTopIndex = mCursorPos;
|
|
}
|
|
} else if (CHECK_BTN_ALL(input->press.button, BTN_CRIGHT)) {
|
|
if (mCursorPos < static_cast<int>(mEntries.size() - 1)) {
|
|
mCursorPos += mNumVisible;
|
|
if (mCursorPos > static_cast<int>(mEntries.size() - 1)) {
|
|
mCursorPos = static_cast<int>(mEntries.size() - 1);
|
|
}
|
|
Audio_PlaySfxGeneral(NA_SE_SY_CURSOR, &gSfxDefaultPos, 4, &gSfxDefaultFreqAndVolScale,
|
|
&gSfxDefaultFreqAndVolScale, &gSfxDefaultReverb);
|
|
}
|
|
if (mCursorPos >= mTopIndex + mNumVisible &&
|
|
mTopIndex + mNumVisible < static_cast<int>(mEntries.size())) {
|
|
mTopIndex = mCursorPos - mNumVisible + 1;
|
|
}
|
|
} else if ((pauseCtx->stickRelX < -30) || CHECK_BTN_ALL(input->press.button, BTN_DLEFT)) {
|
|
KaleidoScope_MoveCursorToSpecialPos(play, PAUSE_CURSOR_PAGE_LEFT);
|
|
pauseCtx->unk_1E4 = 0;
|
|
} else if ((pauseCtx->stickRelX > 30) || CHECK_BTN_ALL(input->press.button, BTN_DRIGHT)) {
|
|
KaleidoScope_MoveCursorToSpecialPos(play, PAUSE_CURSOR_PAGE_RIGHT);
|
|
pauseCtx->unk_1E4 = 0;
|
|
}
|
|
} else if (pauseCtx->cursorSpecialPos == PAUSE_CURSOR_PAGE_LEFT) {
|
|
if ((pauseCtx->stickRelX > 30) || CHECK_BTN_ALL(input->press.button, BTN_DRIGHT)) {
|
|
pauseCtx->cursorSpecialPos = 0;
|
|
Audio_PlaySfxGeneral(NA_SE_SY_CURSOR, &gSfxDefaultPos, 4, &gSfxDefaultFreqAndVolScale,
|
|
&gSfxDefaultFreqAndVolScale, &gSfxDefaultReverb);
|
|
}
|
|
} else if (pauseCtx->cursorSpecialPos == PAUSE_CURSOR_PAGE_RIGHT) {
|
|
if ((pauseCtx->stickRelX < -30) || CHECK_BTN_ALL(input->press.button, BTN_DLEFT)) {
|
|
pauseCtx->cursorSpecialPos = 0;
|
|
Audio_PlaySfxGeneral(NA_SE_SY_CURSOR, &gSfxDefaultPos, 4, &gSfxDefaultFreqAndVolScale,
|
|
&gSfxDefaultFreqAndVolScale, &gSfxDefaultReverb);
|
|
}
|
|
}
|
|
} else if (pauseCtx->cursorSpecialPos != 0 && pauseCtx->state == 7) {
|
|
pauseCtx->cursorSpecialPos = 0;
|
|
}
|
|
}
|
|
int yOffset = 1;
|
|
for (int i = mTopIndex; i < (mTopIndex + mNumVisible) && i < static_cast<int>(mEntries.size()); i++) {
|
|
auto& entry = mEntries[i];
|
|
entry->SetYOffset(yOffset);
|
|
yOffset += 9;
|
|
Matrix_Push();
|
|
entry->SetSelected((i == mCursorPos) && !(pauseCtx->cursorSpecialPos == PAUSE_CURSOR_PAGE_RIGHT ||
|
|
pauseCtx->cursorSpecialPos == PAUSE_CURSOR_PAGE_LEFT));
|
|
entry->Draw(play, &mEntryDl);
|
|
Matrix_Pop();
|
|
}
|
|
|
|
mEntryDl.push_back(gsSPEndDisplayList());
|
|
gSPDisplayList(POLY_OPA_DISP++, mEntryDl.data());
|
|
CLOSE_DISPS(play->state.gfxCtx);
|
|
}
|
|
|
|
void Kaleido::Update(PlayState* play) {
|
|
for (int i = mTopIndex; i < (mTopIndex + mNumVisible) && i < static_cast<int>(mEntries.size()); i++) {
|
|
const auto& entry = mEntries[i];
|
|
entry->Update(play);
|
|
}
|
|
}
|
|
|
|
extern "C" void RandoKaleido_DrawMiscCollectibles(PlayState* play) {
|
|
OTRGlobals::Instance->gRandoContext->GetKaleido()->Draw(play);
|
|
}
|
|
|
|
extern "C" void RandoKaleido_UpdateMiscCollectibles(int16_t inDungeonScene) {
|
|
PauseContext* pauseCtx = &gPlayState->pauseCtx;
|
|
if (pauseCtx->randoQuestMode && pauseCtx->pageIndex == PAUSE_QUEST) {
|
|
OTRGlobals::Instance->gRandoContext->GetKaleido()->Update(gPlayState);
|
|
}
|
|
}
|
|
|
|
KaleidoEntryIconFlag::KaleidoEntryIconFlag(const char* iconResourceName, int iconFormat, int iconSize, int iconWidth,
|
|
int iconHeight, Color_RGBA8 iconColor, FlagType flagType, int flag,
|
|
std::string name)
|
|
: KaleidoEntryIcon(iconResourceName, iconFormat, iconSize, iconWidth, iconHeight, iconColor, std::move(name)),
|
|
mFlagType(flagType), mFlag(flag) {
|
|
BuildVertices();
|
|
}
|
|
|
|
void KaleidoEntryIconFlag::Update(PlayState* play) {
|
|
mAchieved = GameInteractor::RawAction::CheckFlag(mFlagType, static_cast<int16_t>(mFlag));
|
|
}
|
|
|
|
KaleidoEntryIconCountRequired::KaleidoEntryIconCountRequired(const char* iconResourceName, int iconFormat, int iconSize,
|
|
int iconWidth, int iconHeight, Color_RGBA8 iconColor,
|
|
int* watch, int required, int total)
|
|
: KaleidoEntryIcon(iconResourceName, iconFormat, iconSize, iconWidth, iconHeight, iconColor), mWatch(watch),
|
|
mRequired(required), mTotal(total) {
|
|
mCount = *mWatch;
|
|
BuildText();
|
|
BuildVertices();
|
|
}
|
|
|
|
void KaleidoEntryIconCountRequired::BuildText() {
|
|
std::ostringstream totals;
|
|
totals << mCount;
|
|
if (mRequired != 0 && mCount < mRequired) {
|
|
totals << '/' << mRequired;
|
|
}
|
|
if (mTotal >= mRequired && mCount >= mRequired) {
|
|
totals << '/' << mTotal;
|
|
}
|
|
mText = totals.str();
|
|
}
|
|
|
|
void KaleidoEntryIcon::BuildVertices() {
|
|
int offsetY = 0;
|
|
int offsetX = 0;
|
|
// 4 vertices per character, plus one for the preceding icon, plus one for the cursor.
|
|
Vtx* vertices = (Vtx*)calloc(sizeof(Vtx[4]), mText.length() + 2);
|
|
// Vertex for the cursor.
|
|
Ship_CreateQuadVertexGroup(vertices, offsetX, offsetY, 16, 24, 0);
|
|
offsetX += 18;
|
|
// Vertex for the preceding icon.
|
|
Ship_CreateQuadVertexGroup(&vertices[4], offsetX, offsetY, mIconWidth, mIconHeight, 0);
|
|
offsetX += 18;
|
|
for (size_t i = 0; i < mText.length(); i++) {
|
|
int charWidth = static_cast<int>(Ship_GetCharFontWidth(mText[i]));
|
|
Ship_CreateQuadVertexGroup(&(vertices)[((i + 1) * 4) + 4], offsetX, offsetY, charWidth, 16, 0);
|
|
offsetX += charWidth;
|
|
}
|
|
offsetY += FONT_CHAR_TEX_HEIGHT;
|
|
// mWidth = static_cast<int16_t>(offsetX);
|
|
// mHeight = static_cast<int16_t>(offsetY);
|
|
|
|
vertices[1].v.ob[0] = 15; // top-right x
|
|
vertices[2].v.ob[1] = 15; // bottom-left y
|
|
vertices[3].v.ob[0] = 15; // bottom-right x
|
|
vertices[3].v.ob[1] = 15; // bottom-right y
|
|
vertices[5].v.ob[0] = 32; // top-right x
|
|
vertices[6].v.ob[1] = 16; // bottom-left-y
|
|
vertices[7].v.ob[0] = 32; // bottom-right x
|
|
vertices[7].v.ob[1] = 16; // bottom-right y
|
|
|
|
for (size_t i = 0; i < mText.length() + 2; i++) {
|
|
size_t j = i * 4;
|
|
vertices[j].v.ob[0] = vertices[j].v.ob[0] / 2;
|
|
vertices[j].v.ob[1] = vertices[j].v.ob[1] / 2;
|
|
vertices[j + 1].v.ob[0] = vertices[j + 1].v.ob[0] / 2;
|
|
vertices[j + 1].v.ob[1] = vertices[j + 1].v.ob[1] / 2;
|
|
vertices[j + 2].v.ob[0] = vertices[j + 2].v.ob[0] / 2;
|
|
vertices[j + 2].v.ob[1] = vertices[j + 2].v.ob[1] / 2;
|
|
vertices[j + 3].v.ob[0] = vertices[j + 3].v.ob[0] / 2;
|
|
vertices[j + 3].v.ob[1] = vertices[j + 3].v.ob[1] / 2;
|
|
}
|
|
|
|
mWidth = static_cast<int16_t>(offsetX / 2);
|
|
mHeight = static_cast<int16_t>(8);
|
|
|
|
vtx = vertices;
|
|
}
|
|
|
|
KaleidoEntryIcon::KaleidoEntryIcon(const char* iconResourceName, int iconFormat, int iconSize, int iconWidth,
|
|
int iconHeight, Color_RGBA8 iconColor, std::string text)
|
|
: KaleidoEntry(std::move(text)), mIconResourceName(iconResourceName), mIconFormat(iconFormat), mIconSize(iconSize),
|
|
mIconWidth(iconWidth), mIconHeight(iconHeight), mIconColor(iconColor) {
|
|
}
|
|
|
|
void KaleidoEntryIcon::RebuildVertices() {
|
|
free(vtx);
|
|
vtx = nullptr;
|
|
BuildVertices();
|
|
}
|
|
|
|
void KaleidoEntryIconCountRequired::Update(PlayState* play) {
|
|
if (mCount != *mWatch) {
|
|
mCount = *mWatch;
|
|
BuildText();
|
|
RebuildVertices();
|
|
mAchieved = mCount >= mRequired;
|
|
}
|
|
}
|
|
|
|
KaleidoEntryOcarinaButtons::KaleidoEntryOcarinaButtons()
|
|
: KaleidoEntryIcon(gItemIconOcarinaOfTimeTex, G_IM_FMT_RGBA, G_IM_SIZ_32b, 32, 32,
|
|
Color_RGBA8{ 255, 255, 255, 255 }, "\x9F\xA5\xA6\xA7\xA8") {
|
|
CalculateColors();
|
|
BuildVertices();
|
|
}
|
|
|
|
void KaleidoEntryOcarinaButtons::CalculateColors() {
|
|
Color_RGB8 aButtonColor = { 80, 150, 255 };
|
|
if (CVarGetInteger(CVAR_COSMETIC("HUD.AButton.Changed"), 0)) {
|
|
aButtonColor = CVarGetColor24(CVAR_COSMETIC("HUD.AButton.Value"), aButtonColor);
|
|
} else if (CVarGetInteger(CVAR_COSMETIC("DefaultColorScheme"), COLORSCHEME_N64) == COLORSCHEME_GAMECUBE) {
|
|
aButtonColor = { 80, 255, 150 };
|
|
}
|
|
mButtonColors[0] = { aButtonColor.r, aButtonColor.g, aButtonColor.b, 255 };
|
|
Color_RGB8 cButtonsColor = { 255, 255, 50 };
|
|
Color_RGB8 cUpButtonColor = cButtonsColor;
|
|
Color_RGB8 cDownButtonColor = cButtonsColor;
|
|
Color_RGB8 cLeftButtonColor = cButtonsColor;
|
|
Color_RGB8 cRightButtonColor = cButtonsColor;
|
|
if (CVarGetInteger(CVAR_COSMETIC("HUD.CButtons.Changed"), 0)) {
|
|
cUpButtonColor = CVarGetColor24(CVAR_COSMETIC("HUD.CButtons.Value"), cButtonsColor);
|
|
cDownButtonColor = CVarGetColor24(CVAR_COSMETIC("HUD.CButtons.Value"), cButtonsColor);
|
|
cLeftButtonColor = CVarGetColor24(CVAR_COSMETIC("HUD.CButtons.Value"), cButtonsColor);
|
|
cRightButtonColor = CVarGetColor24(CVAR_COSMETIC("HUD.CButtons.Value"), cButtonsColor);
|
|
}
|
|
if (CVarGetInteger(CVAR_COSMETIC("HUD.CUpButton.Changed"), 0)) {
|
|
cUpButtonColor = CVarGetColor24(CVAR_COSMETIC("HUD.CUpButton.Value"), cUpButtonColor);
|
|
}
|
|
if (CVarGetInteger(CVAR_COSMETIC("HUD.CDownButton.Changed"), 0)) {
|
|
cDownButtonColor = CVarGetColor24(CVAR_COSMETIC("HUD.CDownButton.Value"), cDownButtonColor);
|
|
}
|
|
if (CVarGetInteger(CVAR_COSMETIC("HUD.CLeftButton.Changed"), 0)) {
|
|
cLeftButtonColor = CVarGetColor24(CVAR_COSMETIC("HUD.CLeftButton.Value"), cLeftButtonColor);
|
|
}
|
|
if (CVarGetInteger(CVAR_COSMETIC("HUD.CRightButton.Changed"), 0)) {
|
|
cRightButtonColor = CVarGetColor24(CVAR_COSMETIC("HUD.CRightButton.Value"), cRightButtonColor);
|
|
}
|
|
mButtonColors[1] = { cUpButtonColor.r, cUpButtonColor.g, cUpButtonColor.b, 255 };
|
|
mButtonColors[2] = { cDownButtonColor.r, cDownButtonColor.g, cDownButtonColor.b, 255 };
|
|
mButtonColors[3] = { cLeftButtonColor.r, cLeftButtonColor.g, cLeftButtonColor.b, 255 };
|
|
mButtonColors[4] = { cRightButtonColor.r, cRightButtonColor.g, cRightButtonColor.b, 255 };
|
|
}
|
|
|
|
void KaleidoEntryOcarinaButtons::Update(PlayState* play) {
|
|
mButtonCollected[0] = GameInteractor::RawAction::CheckFlag(FLAG_RANDOMIZER_INF, RAND_INF_HAS_OCARINA_A);
|
|
mButtonCollected[1] = GameInteractor::RawAction::CheckFlag(FLAG_RANDOMIZER_INF, RAND_INF_HAS_OCARINA_C_UP);
|
|
mButtonCollected[2] = GameInteractor::RawAction::CheckFlag(FLAG_RANDOMIZER_INF, RAND_INF_HAS_OCARINA_C_DOWN);
|
|
mButtonCollected[3] = GameInteractor::RawAction::CheckFlag(FLAG_RANDOMIZER_INF, RAND_INF_HAS_OCARINA_C_LEFT);
|
|
mButtonCollected[4] = GameInteractor::RawAction::CheckFlag(FLAG_RANDOMIZER_INF, RAND_INF_HAS_OCARINA_C_RIGHT);
|
|
CalculateColors();
|
|
mAchieved = false;
|
|
for (size_t i = 0; i < mButtonCollected.size(); i++) {
|
|
if (!mButtonCollected[i]) {
|
|
mButtonColors[i] = Color_RGBA8{ 109, 109, 109, 255 };
|
|
} else {
|
|
mAchieved = true;
|
|
}
|
|
}
|
|
}
|
|
|
|
void KaleidoEntryOcarinaButtons::Draw(PlayState* play, std::vector<Gfx>* mEntryDl) {
|
|
if (vtx == nullptr) {
|
|
return;
|
|
}
|
|
size_t numChar = mText.length();
|
|
if (numChar == 0) {
|
|
return;
|
|
}
|
|
|
|
Matrix_Translate(mX, mY, 0.0f, MTXMODE_APPLY);
|
|
// Matrix_Scale(0.75f, 0.75f, 0.75f, MTXMODE_APPLY);
|
|
|
|
mEntryDl->push_back(gsSPMatrix(Matrix_NewMtx(play->state.gfxCtx, (char*)__FILE__, __LINE__),
|
|
G_MTX_PUSH | G_MTX_LOAD | G_MTX_MODELVIEW));
|
|
|
|
// cursor (if selected)
|
|
if (mSelected) {
|
|
mEntryDl->push_back(gsDPSetPrimColor(0, 0, 255, 255, 255, 255));
|
|
mEntryDl->push_back(gsSPVertex(vtx, 4, 0));
|
|
Gfx cursorIconTex[] = { gsDPLoadTextureBlock(gArrowCursorTex, G_IM_FMT_IA, G_IM_SIZ_8b, 16, 24, 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) };
|
|
mEntryDl->insert(mEntryDl->end(), std::begin(cursorIconTex), std::end(cursorIconTex));
|
|
mEntryDl->push_back(gsSP1Quadrangle(0, 2, 3, 1, 0));
|
|
}
|
|
|
|
// icon
|
|
if (!mAchieved) {
|
|
mEntryDl->push_back(gsDPSetGrayscaleColor(109, 109, 109, 255));
|
|
mEntryDl->push_back(gsSPGrayscale(true));
|
|
}
|
|
mEntryDl->push_back(gsDPSetPrimColor(0, 0, mIconColor.r, mIconColor.g, mIconColor.b, mIconColor.a));
|
|
mEntryDl->push_back(gsSPVertex(&vtx[4], 4, 0));
|
|
LoadIconTex(mEntryDl);
|
|
mEntryDl->push_back(gsSP1Quadrangle(0, 2, 3, 1, 0));
|
|
mEntryDl->push_back(gsSPGrayscale(false));
|
|
|
|
// text
|
|
for (size_t i = 0, vtxGroup = 0; i < numChar; i++) {
|
|
mEntryDl->push_back(
|
|
gsDPSetPrimColor(0, 0, mButtonColors[i].r, mButtonColors[i].g, mButtonColors[i].b, mButtonColors[i].a));
|
|
|
|
// A maximum of 64 Vtx can be loaded at once by gSPVertex, or basically 16 characters
|
|
// handle loading groups of 16 chars at a time until there are no more left to load.
|
|
// By this point 4 vertices have already been loaded for the preceding icon.
|
|
if (i % 16 == 0) {
|
|
size_t numVtxToLoad = std::min<size_t>(numChar - i, 16) * 4;
|
|
mEntryDl->push_back(gsSPVertex(&vtx[8 + (vtxGroup * 16 * 4)], numVtxToLoad, 0));
|
|
vtxGroup++;
|
|
}
|
|
|
|
auto texture = reinterpret_cast<uintptr_t>(Ship_GetCharFontTexture(mText[i]));
|
|
auto vertexStart = static_cast<int16_t>(4 * (i % 16));
|
|
|
|
Gfx charTexture[] = { gsDPLoadTextureBlock_4b(texture, G_IM_FMT_I, FONT_CHAR_TEX_WIDTH, FONT_CHAR_TEX_HEIGHT, 0,
|
|
G_TX_NOMIRROR | G_TX_CLAMP, G_TX_NOMIRROR | G_TX_CLAMP,
|
|
G_TX_NOMASK, G_TX_NOMASK, G_TX_NOLOD, G_TX_NOLOD) };
|
|
mEntryDl->insert(mEntryDl->end(), std::begin(charTexture), std::end(charTexture));
|
|
mEntryDl->push_back(gsSP1Quadrangle(vertexStart, vertexStart + 2, vertexStart + 3, vertexStart + 1, 0));
|
|
}
|
|
mEntryDl->push_back(gsSPPopMatrix(G_MTX_MODELVIEW));
|
|
}
|
|
} // namespace Rando
|
|
|
|
static void RandoKaleido_RegisterHooks() {
|
|
GameInteractor::Instance->RegisterGameHook<GameInteractor::OnKaleidoscopeUpdate>(
|
|
RandoKaleido_UpdateMiscCollectibles);
|
|
}
|
|
|
|
static RegisterShipInitFunc initFunc(RandoKaleido_RegisterHooks);
|