#include "SaveEditor.h" #include "2s2h/BenGui/UIWidgets.hpp" #include "2s2h/GameInteractor/GameInteractor.h" #include "2s2h/Rando/Rando.h" #include "2s2h/Rando/MiscBehavior/ClockShuffle.h" #include "2s2h/CustomMessage/CustomMessage.h" #include "2s2h/CustomItem/CustomItem.h" #include "2s2h/BenGui/Notification.h" #include "2s2h/Rando/Spoiler/Spoiler.h" #include "2s2h/ShipUtils.h" #include "interface/icon_item_dungeon_static/icon_item_dungeon_static.h" #include "archives/icon_item_24_static/icon_item_24_static_yar.h" extern "C" { #include #include #include #include #include #include "overlays/actors/ovl_En_Test4/z_en_test4.h" #include "overlays/actors/ovl_Obj_Tokei_Step/z_obj_tokei_step.h" extern PlayState* gPlayState; extern SaveContext gSaveContext; extern TexturePtr gItemIcons[131]; extern s16 D_801CFF94[250]; extern u8 gItemSlots[77]; void Interface_LoadItemIconImpl(PlayState* play, u8 btn); void Interface_NewDay(PlayState* play, s32 day); extern RegEditor* gRegEditor; extern ActorOverlay gActorOverlayTable[ACTOR_ID_MAX]; extern s16 gPlayerFormObjectIds[PLAYER_FORM_MAX]; void Player_PlaySfx(Player* player, u16 sfxId); void PlayerCall_Init(Actor* thisx, PlayState* play); void PlayerCall_Update(Actor* thisx, PlayState* play); void PlayerCall_Draw(Actor* thisx, PlayState* play); void TransitionFade_SetColor(void* thisx, u32 color); void ObjTokeiStep_SetupOpen(ObjTokeiStep* objTokeiStep); void ObjTokeiStep_DrawOpen(Actor* actor, PlayState* play); void ObjTokeiStep_DoNothing(ObjTokeiStep* objTokeiStep, PlayState* play); void EnTest4_GetBellTimeOnDay3(EnTest4* thisx); void EnTest4_GetBellTimeAndShrinkScreenBeforeDay3(EnTest4* thisx, PlayState* play); } bool safeMode = true; constexpr float INV_GRID_WIDTH = 46.0f; constexpr float INV_GRID_HEIGHT = 58.0f; constexpr float INV_GRID_ICON_SIZE = 40.0f; constexpr float INV_GRID_PADDING = 10.0f; constexpr float INV_GRID_TOP_MARGIN = 20.0f; constexpr uint16_t HEART_COUNT_MIN = 3; constexpr uint16_t HEART_COUNT_MAX = 20; constexpr int16_t S16_ZERO = 0; constexpr int8_t S8_ZERO = 0; constexpr u8 U8_ZERO = 0; constexpr u8 REG_PAGES_MAX = REG_PAGES; constexpr u8 REG_GROUPS_MAX = REG_GROUPS - 1; const char* MAGIC_LEVEL_NAMES[3] = { "No Magic", "Single Magic", "Double Magic" }; constexpr int8_t MAGIC_LEVEL_MAX = 2; const char* WALLET_LEVEL_NAMES[3] = { "Child Wallet", "Adult Wallet", "Giant Wallet" }; constexpr u8 WALLET_LEVEL_MAX = 2; ImVec4 colorTint; const char* songTooltip; const char* curForm; UIWidgets::Colors formColor; uint32_t formObject; static std::unordered_map randoItemIdComboboxMap; InventorySlot selectedInventorySlot = SLOT_NONE; std::vector safeItemsForInventorySlot[SLOT_MASK_FIERCE_DEITY + 1] = {}; using namespace UIWidgets; void initSafeItemsForInventorySlot() { for (int i = 0; i < sizeof(gItemSlots); i++) { InventorySlot slot = static_cast(gItemSlots[i]); switch (slot) { case SLOT_BOTTLE_1: if (i != ITEM_LONGSHOT) { // No longshot in bottles safeItemsForInventorySlot[SLOT_BOTTLE_1].push_back(static_cast(i)); safeItemsForInventorySlot[SLOT_BOTTLE_2].push_back(static_cast(i)); safeItemsForInventorySlot[SLOT_BOTTLE_3].push_back(static_cast(i)); safeItemsForInventorySlot[SLOT_BOTTLE_4].push_back(static_cast(i)); safeItemsForInventorySlot[SLOT_BOTTLE_5].push_back(static_cast(i)); safeItemsForInventorySlot[SLOT_BOTTLE_6].push_back(static_cast(i)); } break; case SLOT_BOW: if (i == ITEM_BOW) { // No elemental bows here safeItemsForInventorySlot[slot].push_back(static_cast(i)); } break; case SLOT_TRADE_KEY_MAMA: if (i != ITEM_SLINGSHOT) { // No slingshot in trade items safeItemsForInventorySlot[slot].push_back(static_cast(i)); } break; case SLOT_TRADE_DEED: if (i != ITEM_OCARINA_FAIRY) { // No fairy ocarina in trade items safeItemsForInventorySlot[slot].push_back(static_cast(i)); } break; default: safeItemsForInventorySlot[slot].push_back(static_cast(i)); break; } } } char z2ASCII(int code) { int ret; if (code < 10) { // Digits ret = code + 0x30; } else if (code >= 10 && code < 36) { // Uppercase letters ret = code + 0x37; } else if (code >= 36 && code < 62) { // Lowercase letters ret = code + 0x3D; } else if (code == 62) { // Space ret = code - 0x1E; } else if (code == 63 || code == 64) { // _ and . ret = code - 0x12; } else { ret = code; } return char(ret); } char ASCII2z(int code) { int ret; if (code >= 0x30 && code < 0x3A) { // Digits ret = code - 0x30; } else if (code >= 0x41 && code < 0x5B) { // Uppercase letters ret = code - 0x37; } else if (code >= 0x61 && code < 0x7B) { // Lowercase letters ret = code - 0x3D; } else if (code == 0x20 || code == 0x0) { // Space ret = 62; } else if (code == 0x5F || code == 0x2E) { // _ and . ret = code + 0x12; } else { ret = code; } return char(ret); } char* getPlayerName(char* playerNameBuf) { for (int i = 0; i < 8; i++) { playerNameBuf[i] = z2ASCII(gSaveContext.save.saveInfo.playerData.playerName[i]); } playerNameBuf[8] = '\0'; return playerNameBuf; } int setPlayerName(ImGuiInputTextCallbackData* data) { for (int i = 0; i < 8; i++) { gSaveContext.save.saveInfo.playerData.playerName[i] = ASCII2z(data->Buf[i]); } return 0; }; EnTest4* FindEnTest4Actor() { if (gPlayState == NULL) { return NULL; } Actor* enTest4Search = gPlayState->actorCtx.actorLists[ACTORCAT_SWITCH].first; while (enTest4Search != NULL) { if (enTest4Search->id == ACTOR_EN_TEST4) { return (EnTest4*)enTest4Search; } enTest4Search = enTest4Search->next; } return NULL; } void UpdateGameTime(u16 gameTime) { bool newTimeIsNight = (gameTime > CLOCK_TIME(18, 0)) || (gameTime < CLOCK_TIME(6, 0)); bool prevTimeIsNight = (CURRENT_TIME > CLOCK_TIME(18, 0)) || (CURRENT_TIME < CLOCK_TIME(6, 0)); gSaveContext.save.time = gameTime; if (gPlayState == NULL) { return; } // Clear weather from day 2 gWeatherMode = WEATHER_MODE_CLEAR; gPlayState->envCtx.lightningState = LIGHTNING_OFF; // When transitioning over night boundaries, stop the sequences and ask to replay, then respawn actors if (newTimeIsNight != prevTimeIsNight) { // AMBIENCE_ID_13 is used to persist a scenes sequence through night, so we shouldn't // change anything if thats active if (gPlayState->sceneSequences.ambienceId != AMBIENCE_ID_13) { SEQCMD_STOP_SEQUENCE(SEQ_PLAYER_AMBIENCE, 0); SEQCMD_STOP_SEQUENCE(SEQ_PLAYER_BGM_MAIN, 240); gSaveContext.seqId = NA_BGM_DISABLED; gSaveContext.ambienceId = AMBIENCE_ID_DISABLED; Environment_PlaySceneSequence(gPlayState); } // Kills/Spawns half-day actors gPlayState->numSetupActors = -gPlayState->numSetupActors; } EnTest4* enTest4 = FindEnTest4Actor(); // Update EnTest4 actor to be in sync with the new time // This ensures that day transitions are not triggered with the change if (enTest4 != NULL) { enTest4->prevTime = gameTime; enTest4->prevBellTime = gameTime; enTest4->daytimeIndex = newTimeIsNight ? 0 : 1; // Sets the nextBellTime based on the new current time if (CURRENT_DAY == 3) { EnTest4_GetBellTimeOnDay3(enTest4); } else { EnTest4_GetBellTimeAndShrinkScreenBeforeDay3(enTest4, gPlayState); } // Unset any screen scaling from the above funcs gSaveContext.screenScale = 1000.0f; gSaveContext.screenScaleFlag = false; } // Open the Clock Tower rooftop if (((CURRENT_DAY == 3) && (CURRENT_TIME < CLOCK_TIME(6, 0)))) { ObjTokeiStep* objTokeiStep = (ObjTokeiStep*)Actor_FindNearby(gPlayState, &GET_PLAYER(gPlayState)->actor, ACTOR_OBJ_TOKEI_STEP, ACTORCAT_BG, 99999.9f); if (objTokeiStep != NULL && objTokeiStep->actionFunc == ObjTokeiStep_DoNothing) { objTokeiStep->dyna.actor.draw = ObjTokeiStep_DrawOpen; ObjTokeiStep_SetupOpen(objTokeiStep); } } } void DrawTempleClears() { bool cleared; bool open; bool inverted = false; bool inStoneTower = false; if (gPlayState != NULL) { inStoneTower = Play_GetOriginalSceneId(gPlayState->sceneId) == SCENE_F40; inverted = Flags_GetSwitch(gPlayState, 20); } ImGui::Columns(2, nullptr, false); // Left column: Cleared checkboxes // Woodfall cleared = CHECK_WEEKEVENTREG(WEEKEVENTREG_CLEARED_WOODFALL_TEMPLE); if (UIWidgets::Checkbox("Woodfall cleared", &cleared)) { if (cleared) { SET_WEEKEVENTREG(WEEKEVENTREG_CLEARED_WOODFALL_TEMPLE); } else { CLEAR_WEEKEVENTREG(WEEKEVENTREG_CLEARED_WOODFALL_TEMPLE); } } // Snowhead cleared = CHECK_WEEKEVENTREG(WEEKEVENTREG_CLEARED_SNOWHEAD_TEMPLE); if (UIWidgets::Checkbox("Snowhead cleared", &cleared)) { if (cleared) { SET_WEEKEVENTREG(WEEKEVENTREG_CLEARED_SNOWHEAD_TEMPLE); } else { CLEAR_WEEKEVENTREG(WEEKEVENTREG_CLEARED_SNOWHEAD_TEMPLE); } } // Great Bay cleared = CHECK_WEEKEVENTREG(WEEKEVENTREG_CLEARED_GREAT_BAY_TEMPLE); if (UIWidgets::Checkbox("Great Bay cleared", &cleared)) { if (cleared) { SET_WEEKEVENTREG(WEEKEVENTREG_CLEARED_GREAT_BAY_TEMPLE); } else { CLEAR_WEEKEVENTREG(WEEKEVENTREG_CLEARED_GREAT_BAY_TEMPLE); } } // Stone Tower cleared = CHECK_WEEKEVENTREG(WEEKEVENTREG_CLEARED_STONE_TOWER_TEMPLE); if (UIWidgets::Checkbox("Stone Tower cleared", &cleared)) { if (cleared) { SET_WEEKEVENTREG(WEEKEVENTREG_CLEARED_STONE_TOWER_TEMPLE); } else { CLEAR_WEEKEVENTREG(WEEKEVENTREG_CLEARED_STONE_TOWER_TEMPLE); } } ImGui::NextColumn(); // Right column: Open/Inverted checkboxes // Woodfall cleared = CHECK_WEEKEVENTREG(WEEKEVENTREG_CLEARED_WOODFALL_TEMPLE); if (cleared) { open = true; SET_WEEKEVENTREG(WEEKEVENTREG_20_01); } else { open = CHECK_WEEKEVENTREG(WEEKEVENTREG_20_01); } if (UIWidgets::Checkbox("Woodfall Open", &open, { { .disabled = cleared } })) { if (open) { SET_WEEKEVENTREG(WEEKEVENTREG_20_01); } else { CLEAR_WEEKEVENTREG(WEEKEVENTREG_20_01); } } // Snowhead cleared = CHECK_WEEKEVENTREG(WEEKEVENTREG_CLEARED_SNOWHEAD_TEMPLE); if (cleared) { open = true; SET_WEEKEVENTREG(WEEKEVENTREG_30_01); } else { open = CHECK_WEEKEVENTREG(WEEKEVENTREG_30_01); } if (UIWidgets::Checkbox("Snowhead Open", &open, { { .disabled = cleared } })) { if (open) { SET_WEEKEVENTREG(WEEKEVENTREG_30_01); } else { CLEAR_WEEKEVENTREG(WEEKEVENTREG_30_01); } } // Great Bay cleared = CHECK_WEEKEVENTREG(WEEKEVENTREG_CLEARED_GREAT_BAY_TEMPLE); if (cleared) { open = true; // 53_20 is if the turtle is raised // 93_08 determines if a long or short cutscene should play SET_WEEKEVENTREG(WEEKEVENTREG_53_20); SET_WEEKEVENTREG(WEEKEVENTREG_93_08); } else { open = CHECK_WEEKEVENTREG(WEEKEVENTREG_53_20); } if (UIWidgets::Checkbox("Great Bay Open", &open, { { .disabled = cleared } })) { if (open) { SET_WEEKEVENTREG(WEEKEVENTREG_53_20); SET_WEEKEVENTREG(WEEKEVENTREG_93_08); } else { CLEAR_WEEKEVENTREG(WEEKEVENTREG_53_20); CLEAR_WEEKEVENTREG(WEEKEVENTREG_93_08); } } // Stone Tower Inverted if (UIWidgets::Checkbox( "Stone Tower Inverted", &inverted, { { .disabled = !inStoneTower, .disabledTooltip = "Can only invert while in Stone Tower" } })) { if (inverted) { Flags_SetSwitch(gPlayState, 20); } else { Flags_UnsetSwitch(gPlayState, 20); } } ImGui::Columns(1); } void DrawGeneralTab() { ImGui::PushStyleVar(ImGuiStyleVar_ChildRounding, 3.0f); ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(8.0f, 8.0f)); ImGui::BeginChild("generalTab", ImVec2(0, 0), true); UIWidgets::BeginCardLayout({ .columnsPerRow = 2, .minColumnWidth = 420.0f }); // Card 1: Player Identity UIWidgets::BeginCard("identityCard"); ImGui::Text("Player Name"); ImGui::PushStyleColor(ImGuiCol_FrameBg, UIWidgets::ColorValues.at(UIWidgets::Colors::Gray)); ImGui::PushStyleVar(ImGuiStyleVar_FrameRounding, 3.0f); ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(10.0f, 8.0f)); static char playerNameBuf[9]; ImGui::InputText("##playerNameInput", getPlayerName(playerNameBuf), 9, ImGuiInputTextFlags_CallbackAlways | ImGuiInputTextFlags_AutoSelectAll, setPlayerName); ImGui::PopStyleVar(2); ImGui::PopStyleColor(1); ImGui::Spacing(); ImGui::Text("Bombers Code: %d %d %d %d %d", gSaveContext.save.saveInfo.bomberCode[0], gSaveContext.save.saveInfo.bomberCode[1], gSaveContext.save.saveInfo.bomberCode[2], gSaveContext.save.saveInfo.bomberCode[3], gSaveContext.save.saveInfo.bomberCode[4]); ImGui::Spacing(); UIWidgets::Checkbox("Has Tatl", (bool*)&gSaveContext.save.hasTatl, { .color = UIWidgets::Colors::Gray }); UIWidgets::Checkbox("Is Owl Save", (bool*)&gSaveContext.save.isOwlSave, { .color = UIWidgets::Colors::Gray }); UIWidgets::Checkbox("Finished Intro Sequence", (bool*)&gSaveContext.save.isFirstCycle, { .color = UIWidgets::Colors::Gray }); UIWidgets::EndCard(); // Card 2: Time & Day UIWidgets::BeginCard("timeCard"); ImGui::Text("Time of Day"); UIWidgets::PushStyleSlider(); static u16 minTime = 0; static u16 maxTime = 0xFFFF; // Get current time and format as digital string u16 curMinutes = (s32)TIME_TO_MINUTES_F(CURRENT_TIME) % 60; u16 curHours = (s32)TIME_TO_MINUTES_F(CURRENT_TIME) / 60; std::string minutes = (curMinutes < 10 ? "0" : "") + std::to_string(curMinutes); std::string hours = ""; std::string ampm = ""; // Handle 24 or 12 hour time if (CVarGetInteger("gEnhancements.Graphics.24HoursClock", 0)) { if (curHours < 10) { hours += "0"; } } else { ampm = curHours >= 12 ? " pm" : " am"; curHours = curHours % 12 ? curHours % 12 : 12; } hours += std::to_string(curHours); std::string timeString = hours + ":" + minutes + ampm + " (0x%x)"; u16 gameTime = CURRENT_TIME; if (ImGui::SliderScalar("##timeInput", ImGuiDataType_U16, &gameTime, &minTime, &maxTime, timeString.c_str())) { UpdateGameTime(gameTime); } UIWidgets::PopStyleSlider(); ImGui::Spacing(); static const std::array, 4> timeSkipAmounts = { { { -60, "-1hr" }, { -15, "-15m" }, { 15, "+15m" }, { 60, "+1hr" } } }; float availWidth = ImGui::GetContentRegionAvail().x; float spacing = ImGui::GetStyle().ItemSpacing.x; float buttonWidth = (availWidth - (3 * spacing)) / 4; for (size_t i = 0; i < timeSkipAmounts.size(); i++) { const auto& skip = timeSkipAmounts.at(i); if (UIWidgets::Button(skip.second, { .size = ImVec2(buttonWidth, 0), .color = UIWidgets::Colors::LightBlue })) { UpdateGameTime(CURRENT_TIME + CLOCK_TIME(0, skip.first)); } if (i < timeSkipAmounts.size() - 1) { ImGui::SameLine(); } } UIWidgets::PushStyleSlider(); static s32 minDay = 0; static s32 maxDay = 4; if (ImGui::SliderScalar("##dayInput", ImGuiDataType_S32, &gSaveContext.save.day, &minDay, &maxDay, "Day: %d")) { gSaveContext.save.eventDayCount = CURRENT_DAY; if (gPlayState != nullptr) { Interface_NewDay(gPlayState, CURRENT_DAY); // Inverting setup actors forces half-day actors to kill/respawn for new day gPlayState->numSetupActors = -gPlayState->numSetupActors; // Load environment values for new day Environment_NewDay(&gPlayState->envCtx); // Clear weather from day 2 gWeatherMode = WEATHER_MODE_CLEAR; gPlayState->envCtx.lightningState = LIGHTNING_OFF; } } // Time speed slider // Values are added to R_TIME_SPEED which is generally 3 in normal play state static s32 minSpeed = -3; // Time frozen static s32 maxSpeed = 7; std::string extraText = ""; if (gSaveContext.save.timeSpeedOffset == 0) { extraText = " (default)"; } else if (gSaveContext.save.timeSpeedOffset == -2) { extraText = " (inverted)"; } std::string timeSpeedString = "Time Speed: " + std::to_string(gSaveContext.save.timeSpeedOffset + 3) + extraText; ImGui::SliderScalar("##timeSpeedInput", ImGuiDataType_S32, &gSaveContext.save.timeSpeedOffset, &minSpeed, &maxSpeed, timeSpeedString.c_str()); UIWidgets::PopStyleSlider(); UIWidgets::EndCard(); // Card 3: Currency UIWidgets::BeginCard("currencyCard"); if (UIWidgets::Button("Max Rupees", { .size = UIWidgets::Sizes::Inline, .color = UIWidgets::Colors::Green })) { Inventory_ChangeUpgrade(UPG_WALLET, 2); gSaveContext.save.saveInfo.playerData.rupees = CUR_CAPACITY(UPG_WALLET); } ImGui::SameLine(); if (UIWidgets::Button("Reset##resetRupeesButton", { .size = UIWidgets::Sizes::Inline, .color = UIWidgets::Colors::Gray })) { gSaveContext.save.saveInfo.playerData.rupees = 0; Inventory_ChangeUpgrade(UPG_WALLET, 0); } UIWidgets::PushStyleSlider(UIWidgets::Colors::Green); u8 currentWalletLevel = CUR_UPG_VALUE(UPG_WALLET); if (ImGui::SliderScalar("##walletLevelSlider", ImGuiDataType_U8, ¤tWalletLevel, &U8_ZERO, &WALLET_LEVEL_MAX, WALLET_LEVEL_NAMES[currentWalletLevel])) { Inventory_ChangeUpgrade(UPG_WALLET, currentWalletLevel); gSaveContext.save.saveInfo.playerData.rupees = MIN(gSaveContext.save.saveInfo.playerData.rupees, CUR_CAPACITY(UPG_WALLET)); } s16 walletCapacity = CUR_CAPACITY(UPG_WALLET); ImGui::SliderScalar("##rupeesSlider", ImGuiDataType_S16, &gSaveContext.save.saveInfo.playerData.rupees, &S16_ZERO, &walletCapacity, "Rupees: %d"); int bankedRupees = HS_GET_BANK_RUPEES(); if (ImGui::SliderInt("##setBank", &bankedRupees, 0, 5000, "Banked Rupees: %d")) { HS_SET_BANK_RUPEES(bankedRupees); } UIWidgets::Tooltip("To receive the rewards, set the bank to 199, 999, or 4,999 then deposit a single rupee"); UIWidgets::PopStyleSlider(); UIWidgets::EndCard(); // Card 4: Health UIWidgets::BeginCard("healthCard"); if (UIWidgets::Button("Max Health", { .size = UIWidgets::Sizes::Inline, .color = UIWidgets::Colors::Red })) { gSaveContext.save.saveInfo.playerData.doubleDefense = 1; gSaveContext.save.saveInfo.inventory.defenseHearts = 20; gSaveContext.save.saveInfo.playerData.healthCapacity = gSaveContext.save.saveInfo.playerData.health = 20 * 16; gSaveContext.save.saveInfo.inventory.questItems &= ~0xF0000000; } ImGui::SameLine(); if (UIWidgets::Button("Reset##resetHealthButton", { .size = UIWidgets::Sizes::Inline, .color = UIWidgets::Colors::Gray })) { gSaveContext.save.saveInfo.playerData.doubleDefense = 0; gSaveContext.save.saveInfo.inventory.defenseHearts = 0; gSaveContext.save.saveInfo.playerData.healthCapacity = gSaveContext.save.saveInfo.playerData.health = 3 * 16; gSaveContext.save.saveInfo.inventory.questItems &= ~0xF0000000; } ImGui::SameLine(); if (UIWidgets::Checkbox("DD", (bool*)&gSaveContext.save.saveInfo.playerData.doubleDefense, { .color = UIWidgets::Colors::Red })) { if (gSaveContext.save.saveInfo.playerData.doubleDefense) { gSaveContext.save.saveInfo.inventory.defenseHearts = 20; } else { gSaveContext.save.saveInfo.inventory.defenseHearts = 0; } } UIWidgets::PushStyleSlider(UIWidgets::Colors::DarkRed); int16_t heartCount = (int16_t)gSaveContext.save.saveInfo.playerData.healthCapacity / 16; if (ImGui::SliderScalar("##heartCountSlider", ImGuiDataType_U16, &heartCount, &HEART_COUNT_MIN, &HEART_COUNT_MAX, "Max Hearts: %d")) { gSaveContext.save.saveInfo.playerData.healthCapacity = heartCount * 16; gSaveContext.save.saveInfo.playerData.health = MIN(gSaveContext.save.saveInfo.playerData.health, gSaveContext.save.saveInfo.playerData.healthCapacity); } ImGui::SliderScalar("##healthSlider", ImGuiDataType_S16, &gSaveContext.save.saveInfo.playerData.health, &S16_ZERO, &gSaveContext.save.saveInfo.playerData.healthCapacity, "Health: %d"); UIWidgets::PopStyleSlider(); UIWidgets::EndCard(); // Card 5: Magic UIWidgets::BeginCard("magicCard"); if (UIWidgets::Button("Max Magic", { .size = UIWidgets::Sizes::Inline, .color = UIWidgets::Colors::DarkGreen })) { gSaveContext.magicCapacity = gSaveContext.save.saveInfo.playerData.magic = MAGIC_DOUBLE_METER; gSaveContext.save.saveInfo.playerData.magicLevel = 2; gSaveContext.save.saveInfo.playerData.isMagicAcquired = true; gSaveContext.save.saveInfo.playerData.isDoubleMagicAcquired = true; BUTTON_ITEM_EQUIP(PLAYER_FORM_DEKU, EQUIP_SLOT_B) = ITEM_DEKU_NUT; } ImGui::SameLine(); if (UIWidgets::Button("Reset##resetMagicButton", { .size = UIWidgets::Sizes::Inline, .color = UIWidgets::Colors::Gray })) { gSaveContext.magicCapacity = gSaveContext.save.saveInfo.playerData.magic = 0; gSaveContext.save.saveInfo.playerData.magicLevel = 0; gSaveContext.save.saveInfo.playerData.isMagicAcquired = false; gSaveContext.save.saveInfo.playerData.isDoubleMagicAcquired = false; } bool spinAttack = CHECK_WEEKEVENTREG(WEEKEVENTREG_RECEIVED_GREAT_SPIN_ATTACK); if (UIWidgets::Checkbox("Spin Lv2", &spinAttack, { .color = UIWidgets::Colors::DarkGreen })) { if (spinAttack) { SET_WEEKEVENTREG(WEEKEVENTREG_RECEIVED_GREAT_SPIN_ATTACK); } else { CLEAR_WEEKEVENTREG(WEEKEVENTREG_RECEIVED_GREAT_SPIN_ATTACK); } } ImGui::SameLine(); bool drankChateau = CHECK_WEEKEVENTREG(WEEKEVENTREG_DRANK_CHATEAU_ROMANI); if (UIWidgets::Checkbox("Drank Chateau", &drankChateau, { .color = UIWidgets::Colors::DarkGreen })) { if (drankChateau) { SET_WEEKEVENTREG(WEEKEVENTREG_DRANK_CHATEAU_ROMANI); } else { CLEAR_WEEKEVENTREG(WEEKEVENTREG_DRANK_CHATEAU_ROMANI); } } UIWidgets::PushStyleSlider(UIWidgets::Colors::DarkGreen); if (ImGui::SliderScalar("##magicLevelSlider", ImGuiDataType_S8, &gSaveContext.save.saveInfo.playerData.magicLevel, &S8_ZERO, &MAGIC_LEVEL_MAX, MAGIC_LEVEL_NAMES[gSaveContext.save.saveInfo.playerData.magicLevel])) { gSaveContext.magicCapacity = gSaveContext.save.saveInfo.playerData.magicLevel * MAGIC_NORMAL_METER; gSaveContext.save.saveInfo.playerData.magic = MIN(gSaveContext.save.saveInfo.playerData.magic, gSaveContext.magicCapacity); switch (gSaveContext.save.saveInfo.playerData.magicLevel) { case 0: gSaveContext.save.saveInfo.playerData.isMagicAcquired = false; gSaveContext.save.saveInfo.playerData.isDoubleMagicAcquired = false; break; case 1: gSaveContext.save.saveInfo.playerData.isMagicAcquired = true; gSaveContext.save.saveInfo.playerData.isDoubleMagicAcquired = false; BUTTON_ITEM_EQUIP(PLAYER_FORM_DEKU, EQUIP_SLOT_B) = ITEM_DEKU_NUT; break; case 2: gSaveContext.save.saveInfo.playerData.isMagicAcquired = true; gSaveContext.save.saveInfo.playerData.isDoubleMagicAcquired = true; BUTTON_ITEM_EQUIP(PLAYER_FORM_DEKU, EQUIP_SLOT_B) = ITEM_DEKU_NUT; break; } } ImGui::SliderScalar("##magicSlider", ImGuiDataType_S8, &gSaveContext.save.saveInfo.playerData.magic, &S8_ZERO, &gSaveContext.magicCapacity, "Magic: %d"); UIWidgets::PopStyleSlider(); UIWidgets::EndCard(); // Card 6: Temple Progress UIWidgets::BeginCard("progressCard"); DrawTempleClears(); UIWidgets::EndCard(); UIWidgets::EndCardLayout(); ImGui::EndChild(); ImGui::PopStyleVar(2); } template bool contains(C&& c, T e) { return std::find(std::begin(c), std::end(c), e) != std::end(c); }; void DrawAmmoInput(InventorySlot slot) { int x = slot % 6; int y = ((int)floor(slot / 6) % 4); ItemId currentItemId = static_cast(gSaveContext.save.saveInfo.inventory.items[slot]); ImGui::SetCursorPos( ImVec2(x * INV_GRID_WIDTH + INV_GRID_PADDING + 7.0f, y * INV_GRID_HEIGHT + INV_GRID_TOP_MARGIN + INV_GRID_PADDING + (INV_GRID_ICON_SIZE - 2.0f))); ImGui::PushItemWidth(24.0f); ImGui::PushStyleColor(ImGuiCol_FrameBg, UIWidgets::ColorValues.at(UIWidgets::Colors::Gray)); ImGui::PushStyleVar(ImGuiStyleVar_FrameRounding, 3.0f); ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(4.0f, 4.0f)); if (ImGui::InputScalar("##ammoInput", ImGuiDataType_S8, &AMMO(currentItemId))) { // Crashes when < 0 and graphical issues when > 99 AMMO(currentItemId) = MAX(0, MIN(AMMO(currentItemId), 99)); } ImGui::PopStyleVar(2); ImGui::PopStyleColor(1); ImGui::PopItemWidth(); } void DrawEquipItemMenu(InventorySlot slot) { ItemId currentItemId = static_cast(gSaveContext.save.saveInfo.inventory.items[slot]); if (ImGui::BeginMenu("Equip")) { if (ImGui::MenuItem("C-Left")) { SET_CUR_FORM_BTN_ITEM(EQUIP_SLOT_C_LEFT, currentItemId); SET_CUR_FORM_BTN_SLOT(EQUIP_SLOT_C_LEFT, slot); Interface_LoadItemIconImpl(gPlayState, EQUIP_SLOT_C_LEFT); } if (ImGui::MenuItem("C-Down")) { SET_CUR_FORM_BTN_ITEM(EQUIP_SLOT_C_DOWN, currentItemId); SET_CUR_FORM_BTN_SLOT(EQUIP_SLOT_C_DOWN, slot); Interface_LoadItemIconImpl(gPlayState, EQUIP_SLOT_C_DOWN); } if (ImGui::MenuItem("C-Right")) { SET_CUR_FORM_BTN_ITEM(EQUIP_SLOT_C_RIGHT, currentItemId); SET_CUR_FORM_BTN_SLOT(EQUIP_SLOT_C_RIGHT, slot); Interface_LoadItemIconImpl(gPlayState, EQUIP_SLOT_C_RIGHT); } if (ImGui::MenuItem("D-Right")) { DPAD_SET_CUR_FORM_BTN_ITEM(EQUIP_SLOT_D_RIGHT, currentItemId); DPAD_SET_CUR_FORM_BTN_SLOT(EQUIP_SLOT_D_RIGHT, slot); Interface_Dpad_LoadItemIconImpl(gPlayState, EQUIP_SLOT_D_RIGHT); } if (ImGui::MenuItem("D-Left")) { DPAD_SET_CUR_FORM_BTN_ITEM(EQUIP_SLOT_D_LEFT, currentItemId); DPAD_SET_CUR_FORM_BTN_SLOT(EQUIP_SLOT_D_LEFT, slot); Interface_Dpad_LoadItemIconImpl(gPlayState, EQUIP_SLOT_D_LEFT); } if (ImGui::MenuItem("D-Down")) { DPAD_SET_CUR_FORM_BTN_ITEM(EQUIP_SLOT_D_DOWN, currentItemId); DPAD_SET_CUR_FORM_BTN_SLOT(EQUIP_SLOT_D_DOWN, slot); Interface_Dpad_LoadItemIconImpl(gPlayState, EQUIP_SLOT_D_DOWN); } if (ImGui::MenuItem("D-Up")) { DPAD_SET_CUR_FORM_BTN_ITEM(EQUIP_SLOT_D_UP, currentItemId); DPAD_SET_CUR_FORM_BTN_SLOT(EQUIP_SLOT_D_UP, slot); Interface_Dpad_LoadItemIconImpl(gPlayState, EQUIP_SLOT_D_UP); } ImGui::EndMenu(); } } void NextItemInSlot(InventorySlot slot) { ItemId currentItemId = static_cast(gSaveContext.save.saveInfo.inventory.items[slot]); size_t currentItemIndex = find(safeItemsForInventorySlot[slot].begin(), safeItemsForInventorySlot[slot].end(), currentItemId) - safeItemsForInventorySlot[slot].begin(); if (currentItemId == ITEM_NONE) { gSaveContext.save.saveInfo.inventory.items[slot] = safeItemsForInventorySlot[slot][0]; if (gSaveContext.save.saveInfo.inventory.items[slot] == ITEM_BOW) { Inventory_ChangeUpgrade(UPG_QUIVER, 1); AMMO(ITEM_BOW) = CUR_CAPACITY(UPG_QUIVER); } else if (gSaveContext.save.saveInfo.inventory.items[slot] == ITEM_BOMB || gSaveContext.save.saveInfo.inventory.items[slot] == ITEM_BOMBCHU) { if (CUR_UPG_VALUE(UPG_BOMB_BAG) == 0) { Inventory_ChangeUpgrade(UPG_BOMB_BAG, 1); } AMMO(gSaveContext.save.saveInfo.inventory.items[slot]) = CUR_CAPACITY(UPG_BOMB_BAG); } } else if (currentItemId == ITEM_BOW) { if (CUR_UPG_VALUE(UPG_QUIVER) < 3) { Inventory_ChangeUpgrade(UPG_QUIVER, CUR_UPG_VALUE(UPG_QUIVER) + 1); AMMO(ITEM_BOW) = CUR_CAPACITY(UPG_QUIVER); } else { Inventory_DeleteItem(gSaveContext.save.saveInfo.inventory.items[slot], slot); Inventory_ChangeUpgrade(UPG_QUIVER, 0); AMMO(ITEM_BOW) = 0; } } else if (currentItemId == ITEM_BOMB || currentItemId == ITEM_BOMBCHU) { if (CUR_UPG_VALUE(UPG_BOMB_BAG) < 3) { Inventory_ChangeUpgrade(UPG_BOMB_BAG, CUR_UPG_VALUE(UPG_BOMB_BAG) + 1); AMMO(ITEM_BOMB) = AMMO(ITEM_BOMBCHU) = CUR_CAPACITY(UPG_BOMB_BAG); } else { Inventory_DeleteItem(ITEM_BOMB, SLOT_BOMB); Inventory_DeleteItem(ITEM_BOMBCHU, SLOT_BOMBCHU); Inventory_ChangeUpgrade(UPG_BOMB_BAG, 0); AMMO(ITEM_BOMB) = AMMO(ITEM_BOMBCHU) = 0; } } else if (currentItemIndex < safeItemsForInventorySlot[slot].size() - 1) { Inventory_ReplaceItem(gPlayState, currentItemId, safeItemsForInventorySlot[slot][currentItemIndex + 1]); } else { Inventory_DeleteItem(gSaveContext.save.saveInfo.inventory.items[slot], slot); } } void DrawSlot(InventorySlot slot) { int x = slot % 6; int y = ((int)floor(slot / 6) % 4); ItemId currentItemId = static_cast(gSaveContext.save.saveInfo.inventory.items[slot]); ImGui::PushID(slot); if (currentItemId != ITEM_NONE && currentItemId <= ITEM_BOW_LIGHT && // gItemSlots only has entries till 77 (ITEM_BOW_LIGHT) gItemSlots[currentItemId] <= SLOT_BOTTLE_6 && // There is only ammo data for the first page ((safeMode && gAmmoItems[gItemSlots[currentItemId]] != ITEM_NONE) || !safeMode)) { DrawAmmoInput(slot); } ImGui::SetCursorPos( ImVec2(x * INV_GRID_WIDTH + INV_GRID_PADDING, y * INV_GRID_HEIGHT + INV_GRID_TOP_MARGIN + INV_GRID_PADDING)); // isEquipped border ImGui::PushStyleColor(ImGuiCol_Button, ImVec4(1.0f, 1.0f, 1.0f, 0.0f)); ImGui::PushStyleColor(ImGuiCol_ButtonHovered, ImVec4(1.0f, 1.0f, 1.0f, 0.2f)); ImGui::PushStyleColor(ImGuiCol_ButtonActive, ImVec4(1.0f, 1.0f, 1.0f, 0.1f)); if (GET_CUR_FORM_BTN_SLOT(EQUIP_SLOT_C_LEFT) == slot || GET_CUR_FORM_BTN_SLOT(EQUIP_SLOT_C_DOWN) == slot || GET_CUR_FORM_BTN_SLOT(EQUIP_SLOT_C_RIGHT) == slot || (CVarGetInteger("gEnhancements.Dpad.DpadEquips", 0) && (DPAD_GET_CUR_FORM_BTN_SLOT(EQUIP_SLOT_D_RIGHT) == slot || DPAD_GET_CUR_FORM_BTN_SLOT(EQUIP_SLOT_D_LEFT) == slot || DPAD_GET_CUR_FORM_BTN_SLOT(EQUIP_SLOT_D_DOWN) == slot || DPAD_GET_CUR_FORM_BTN_SLOT(EQUIP_SLOT_D_UP) == slot))) { ImGui::PushStyleColor(ImGuiCol_Border, UIWidgets::ColorValues.at(UIWidgets::Colors::White)); } else { ImGui::PushStyleColor(ImGuiCol_Border, ImVec4(1.0f, 1.0f, 1.0f, 0.0f)); } ImGui::PushStyleVar(ImGuiStyleVar_FrameBorderSize, 2.0f); ImTextureID textureId = Ship::Context::GetInstance()->GetWindow()->GetGui()->GetTextureByName( (const char*)gItemIcons[safeItemsForInventorySlot[slot][0]]); if (currentItemId != ITEM_NONE) { textureId = Ship::Context::GetInstance()->GetWindow()->GetGui()->GetTextureByName( (const char*)gItemIcons[currentItemId]); } ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(0, 0)); bool buttonPressed = ImGui::ImageButton((const char*)gItemIcons[safeItemsForInventorySlot[slot][0]], textureId, ImVec2(INV_GRID_ICON_SIZE, INV_GRID_ICON_SIZE), ImVec2(0, 0), ImVec2(1, 1), ImVec4(0, 0, 0, 0), ImVec4(1, 1, 1, currentItemId == ITEM_NONE ? 0.4f : 1.0f)); ImGui::PopStyleVar(); if (buttonPressed) { if (safeMode && safeItemsForInventorySlot[slot].size() < 2) { NextItemInSlot(slot); } else { selectedInventorySlot = slot; ImGui::OpenPopup("InventorySlotPopup"); } } ImGui::PopStyleVar(); ImGui::PopStyleColor(4); if (ImGui::BeginPopup("InventorySlotPopup")) { ImGui::PushStyleColor(ImGuiCol_Button, ImVec4(1.0f, 1.0f, 1.0f, 0.0f)); ImGui::PushStyleColor(ImGuiCol_ButtonHovered, ImVec4(1.0f, 1.0f, 1.0f, 0.2f)); ImGui::PushStyleColor(ImGuiCol_ButtonActive, ImVec4(1.0f, 1.0f, 1.0f, 0.1f)); if (ImGui::Button("##invNonePicker", ImVec2(INV_GRID_ICON_SIZE, INV_GRID_ICON_SIZE))) { Inventory_DeleteItem(gSaveContext.save.saveInfo.inventory.items[selectedInventorySlot], selectedInventorySlot); ImGui::CloseCurrentPopup(); } UIWidgets::Tooltip("None"); size_t availableItems = safeMode ? safeItemsForInventorySlot[selectedInventorySlot].size() : ITEM_FISHING_ROD; for (int32_t pickerIndex = 0; pickerIndex < availableItems; pickerIndex++) { if (((pickerIndex + 1) % 8) != 0) { ImGui::SameLine(); } ItemId id = safeMode ? safeItemsForInventorySlot[selectedInventorySlot][pickerIndex] : static_cast(pickerIndex); ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(0, 0)); bool buttonPressed = ImGui::ImageButton( (const char*)gItemIcons[id], Ship::Context::GetInstance()->GetWindow()->GetGui()->GetTextureByName((const char*)gItemIcons[id]), ImVec2(INV_GRID_ICON_SIZE, INV_GRID_ICON_SIZE)); ImGui::PopStyleVar(); if (buttonPressed) { gSaveContext.save.saveInfo.inventory.items[selectedInventorySlot] = id; ImGui::CloseCurrentPopup(); } // TODO: Add names for items // UIWidgets::SetLastItemHoverText(SohUtils::GetItemName(id)); } ImGui::PopStyleColor(3); ImGui::EndPopup(); } ImGui::PopID(); if (currentItemId != ITEM_NONE && ImGui::BeginPopupContextItem()) { DrawEquipItemMenu(slot); ImGui::EndPopup(); } } void DrawItemsAndMasksTab() { ImGui::PushStyleVar(ImGuiStyleVar_ChildRounding, 3.0f); ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(8.0f, 8.0f)); // Group left side in a child for a scroll bar ImGui::BeginChild("leftSide##items", ImVec2(0, 0), ImGuiChildFlags_AutoResizeX); ImGui::BeginGroup(); ImGui::BeginChild("itemsBox", ImVec2(INV_GRID_WIDTH * 6 + INV_GRID_PADDING * 2, INV_GRID_HEIGHT * 4 + INV_GRID_PADDING * 2 + INV_GRID_TOP_MARGIN), ImGuiChildFlags_Border); ImGui::Text("Items"); for (uint32_t i = SLOT_OCARINA; i <= SLOT_BOTTLE_6; i++) { InventorySlot slot = static_cast(i); DrawSlot(slot); } ImGui::EndChild(); ImGui::BeginChild("masksBox", ImVec2(INV_GRID_WIDTH * 6 + INV_GRID_PADDING * 2, INV_GRID_HEIGHT * 4 + INV_GRID_PADDING * 2 + INV_GRID_TOP_MARGIN), ImGuiChildFlags_Border); ImGui::Text("Masks"); for (uint32_t i = SLOT_MASK_POSTMAN; i <= SLOT_MASK_FIERCE_DEITY; i++) { InventorySlot slot = static_cast(i); DrawSlot(slot); } ImGui::EndChild(); ImGui::EndGroup(); ImGui::EndChild(); ImGui::SameLine(); ImGui::BeginChild("equipsBox", ImVec2(0, 0), true); if (UIWidgets::Button("Give All##items")) { for (int32_t i = 0; i <= SLOT_MASK_FIERCE_DEITY; i++) { if (i >= SLOT_BOTTLE_1 && i <= SLOT_BOTTLE_6) { gSaveContext.save.saveInfo.inventory.items[i] = ITEM_BOTTLE; } else { gSaveContext.save.saveInfo.inventory.items[i] = safeItemsForInventorySlot[i].back(); } } Inventory_ChangeUpgrade(UPG_BOMB_BAG, 3); Inventory_ChangeUpgrade(UPG_QUIVER, 3); AMMO(ITEM_BOW) = CUR_CAPACITY(UPG_QUIVER); AMMO(ITEM_BOMB) = AMMO(ITEM_BOMBCHU) = CUR_CAPACITY(UPG_BOMB_BAG); AMMO(ITEM_DEKU_STICK) = CUR_CAPACITY(UPG_DEKU_STICKS); AMMO(ITEM_DEKU_NUT) = CUR_CAPACITY(UPG_DEKU_NUTS); AMMO(ITEM_MAGIC_BEANS) = 20; AMMO(ITEM_POWDER_KEG) = 1; } if (UIWidgets::Button("Reset##items")) { for (int32_t i = 0; i <= SLOT_MASK_FIERCE_DEITY; i++) { Inventory_DeleteItem(gSaveContext.save.saveInfo.inventory.items[i], i); } AMMO(ITEM_BOW) = AMMO(ITEM_BOMB) = AMMO(ITEM_BOMBCHU) = AMMO(ITEM_DEKU_STICK) = AMMO(ITEM_DEKU_NUT) = AMMO(ITEM_MAGIC_BEANS) = AMMO(ITEM_POWDER_KEG) = 0; Inventory_ChangeUpgrade(UPG_BOMB_BAG, 0); Inventory_ChangeUpgrade(UPG_QUIVER, 0); } UIWidgets::Checkbox("Safe Mode", &safeMode); if (gSaveContext.save.shipSaveInfo.saveType == SAVETYPE_RANDO) { if (RANDO_SAVE_OPTIONS[RO_CLOCK_SHUFFLE]) { // Time Items Management Section ImGui::SeparatorText("Time Items"); // Individual time items in 3x2 grid with static positioning RandoItemId clockItems[] = { RI_TIME_DAY_1, RI_TIME_DAY_2, RI_TIME_DAY_3, RI_TIME_NIGHT_1, RI_TIME_NIGHT_2, RI_TIME_NIGHT_3 }; const char* clockNames[] = { "Day 1", "Day 2", "Day 3", "Night 1", "Night 2", "Night 3" }; // Use table for static positioning - 3 columns, 2 rows if (ImGui::BeginTable("ClockItemsTable", 3, ImGuiTableFlags_None)) { ImGui::TableSetupColumn("Day 1", ImGuiTableColumnFlags_WidthStretch); ImGui::TableSetupColumn("Day 2", ImGuiTableColumnFlags_WidthStretch); ImGui::TableSetupColumn("Day 3", ImGuiTableColumnFlags_WidthStretch); // First row - Day items ImGui::TableNextRow(); for (int i = 0; i < 3; i++) { ImGui::TableNextColumn(); RandoItemId clockItem = clockItems[i]; int halfIndex = Rando::ClockItems::GetHalfDayIndexFromClockItem(clockItem); bool isOwned = Flags_GetRandoInf(static_cast(RANDO_INF_OBTAINED_CLOCK_DAY_1 + halfIndex)); std::string buttonText = isOwned ? ("Remove " + std::string(clockNames[i])) : ("No Item##" + std::to_string(i)); std::string tooltipText = ""; if (!isOwned) { tooltipText = "You don't own " + std::string(clockNames[i]); } UIWidgets::ButtonOptions buttonOpts; buttonOpts.disabled = !isOwned; buttonOpts.disabledTooltip = !isOwned ? tooltipText.c_str() : ""; if (UIWidgets::Button(buttonText.c_str(), buttonOpts)) { Rando::RemoveItem(clockItem); } } // Second row - Night items ImGui::TableNextRow(); for (int i = 3; i < 6; i++) { ImGui::TableNextColumn(); RandoItemId clockItem = clockItems[i]; int halfIndex = Rando::ClockItems::GetHalfDayIndexFromClockItem(clockItem); bool isOwned = Flags_GetRandoInf(static_cast(RANDO_INF_OBTAINED_CLOCK_DAY_1 + halfIndex)); std::string buttonText = isOwned ? ("Remove " + std::string(clockNames[i])) : ("No Item##" + std::to_string(i)); std::string tooltipText = ""; if (!isOwned) { tooltipText = "You don't own " + std::string(clockNames[i]); } UIWidgets::ButtonOptions buttonOpts; buttonOpts.disabled = !isOwned; buttonOpts.disabledTooltip = !isOwned ? tooltipText.c_str() : ""; if (UIWidgets::Button(buttonText.c_str(), buttonOpts)) { Rando::RemoveItem(clockItem); } } ImGui::EndTable(); } } // Queue Randomizer Item Gives section ImGui::Spacing(); ImGui::SeparatorText("Queue Randomizer Item Gives"); static ImGuiTextFilter riFilter; UIWidgets::PushStyleCombobox(); riFilter.Draw("##filter", ImGui::GetContentRegionAvail().x); UIWidgets::PopStyleCombobox(); if (!riFilter.IsActive()) { ImGui::SameLine(18.0f); ImGui::Text("Search"); } std::string riFilterString(riFilter.InputBuf); for (auto& [randoItemId, randoStaticItem] : Rando::StaticData::Items) { if (!riFilter.PassFilter(randoStaticItem.name)) { continue; } if (randoItemId == RI_TRIFORCE_PIECE_PREVIOUS) { continue; } std::string buttonLabel = "Give "; buttonLabel += randoStaticItem.name; if (UIWidgets::Button(buttonLabel.c_str())) { GameInteractor::Instance->events.emplace_back(GIEventGiveItem{ .showGetItemCutscene = Rando::StaticData::ShouldShowGetItemCutscene(Rando::ConvertItem(randoItemId)), .param = (int16_t)randoItemId, .giveItem = [](Actor* actor, PlayState* play) { RandoItemId randoItemId = Rando::ConvertItem((RandoItemId)CUSTOM_ITEM_PARAM); std::string prefix = "You found"; std::string message = Rando::StaticData::GetItemName(randoItemId); CustomMessage::Entry entry = { .textboxType = 2, .icon = Rando::StaticData::GetIconForZMessage(randoItemId), .msg = prefix + " " + message + "!", }; if (CUSTOM_ITEM_FLAGS & CustomItem::GIVE_ITEM_CUTSCENE) { CustomMessage::SetActiveCustomMessage(entry.msg, entry); } else if (Rando::StaticData::ShouldShowGetItemCutscene( Rando::ConvertItem((RandoItemId)CUSTOM_ITEM_PARAM))) { CustomMessage::StartTextbox(entry.msg + "\x1C\x02\x10", entry); } else { Notification::Emit({ .itemIcon = Rando::StaticData::GetIconTexturePath(randoItemId), .message = prefix, .suffix = message, }); } Rando::GiveItem(randoItemId); CUSTOM_ITEM_PARAM = randoItemId; }, .drawItem = [](Actor* actor, PlayState* play) { RandoItemId randoItemId; // If the item has been given, the CUSTOM_ITEM_PARAM is set to the RI, prior to that it's // the RC if (CUSTOM_ITEM_FLAGS & CustomItem::CALLED_ACTION) { randoItemId = (RandoItemId)CUSTOM_ITEM_PARAM; } else { randoItemId = Rando::ConvertItem((RandoItemId)CUSTOM_ITEM_PARAM); } Matrix_Scale(30.0f, 30.0f, 30.0f, MTXMODE_APPLY); Rando::DrawItem(randoItemId, RC_UNKNOWN, actor); } }); } } } // Expose inputs to edit raw number values of equips // ImGui::Text("Equips"); // ImGui::Text("C-Buttons"); // ImGui::Text("D-Pad"); ImGui::EndChild(); ImGui::PopStyleVar(2); } void SongInfo(int32_t itemID) { switch (itemID) { case QUEST_SONG_SONATA: colorTint = ImVec4(0.588f, 1.0f, 0.392f, CHECK_QUEST_ITEM(itemID) ? 1.0f : 0.4f); songTooltip = "Sonata of Awakening"; break; case QUEST_SONG_LULLABY: colorTint = ImVec4(1.0f, 0.313f, 0.156f, (CHECK_QUEST_ITEM(itemID) || CHECK_QUEST_ITEM(QUEST_SONG_LULLABY_INTRO)) ? 1.0f : 0.4f); songTooltip = (!CHECK_QUEST_ITEM(itemID) && CHECK_QUEST_ITEM(QUEST_SONG_LULLABY_INTRO)) ? "Goron Lullaby Intro" : "Goron Lullaby"; break; case QUEST_SONG_BOSSA_NOVA: colorTint = ImVec4(0.392f, 0.588f, 1.0f, CHECK_QUEST_ITEM(itemID) ? 1.0f : 0.4f); songTooltip = "New Wave Bossa Nova"; break; case QUEST_SONG_ELEGY: colorTint = ImVec4(1.0f, 0.627f, 0.0f, CHECK_QUEST_ITEM(itemID) ? 1.0f : 0.4f); songTooltip = "Elegy of Emptiness"; break; case QUEST_SONG_OATH: colorTint = ImVec4(1.0f, 0.392f, 1.0f, CHECK_QUEST_ITEM(itemID) ? 1.0f : 0.4f); songTooltip = "Oath to Order"; break; case QUEST_SONG_TIME: colorTint = ImVec4(1, 1, 1, CHECK_QUEST_ITEM(itemID) ? 1.0f : 0.4f); songTooltip = "Song of Time"; break; case QUEST_SONG_HEALING: colorTint = ImVec4(1, 1, 1, CHECK_QUEST_ITEM(itemID) ? 1.0f : 0.4f); songTooltip = "Song of Healing"; break; case QUEST_SONG_EPONA: colorTint = ImVec4(1, 1, 1, CHECK_QUEST_ITEM(itemID) ? 1.0f : 0.4f); songTooltip = "Eponas Song"; break; case QUEST_SONG_SOARING: colorTint = ImVec4(1, 1, 1, CHECK_QUEST_ITEM(itemID) ? 1.0f : 0.4f); songTooltip = "Song of Soaring"; break; case QUEST_SONG_STORMS: colorTint = ImVec4(1, 1, 1, CHECK_QUEST_ITEM(itemID) ? 1.0f : 0.4f); songTooltip = "Song of Storms"; break; case QUEST_SONG_SARIA: colorTint = ImVec4(1, 1, 1, CHECK_QUEST_ITEM(itemID) ? 1.0f : 0.4f); songTooltip = "Saria's Song (Unused?)"; break; case QUEST_SONG_SUN: colorTint = ImVec4(1, 1, 1, CHECK_QUEST_ITEM(itemID) ? 1.0f : 0.4f); songTooltip = "Sun's Song (Not Obtainable)"; break; default: colorTint = ImVec4(1, 1, 1, CHECK_QUEST_ITEM(itemID) ? 1.0f : 0.4f); songTooltip = " "; break; } } std::map questToItemMap = { { QUEST_REMAINS_ODOLWA, ITEM_REMAINS_ODOLWA }, { QUEST_REMAINS_GOHT, ITEM_REMAINS_GOHT }, { QUEST_REMAINS_GYORG, ITEM_REMAINS_GYORG }, { QUEST_REMAINS_TWINMOLD, ITEM_REMAINS_TWINMOLD }, { QUEST_SONG_SONATA, ITEM_SONG_SONATA }, { QUEST_SONG_LULLABY, ITEM_SONG_LULLABY }, { QUEST_SONG_BOSSA_NOVA, ITEM_SONG_NOVA }, { QUEST_SONG_ELEGY, ITEM_SONG_ELEGY }, { QUEST_SONG_OATH, ITEM_SONG_OATH }, { QUEST_SONG_SARIA, ITEM_SONG_SARIA }, { QUEST_SONG_TIME, ITEM_SONG_TIME }, { QUEST_SONG_HEALING, ITEM_SONG_HEALING }, { QUEST_SONG_EPONA, ITEM_SONG_EPONA }, { QUEST_SONG_SOARING, ITEM_SONG_SOARING }, { QUEST_SONG_STORMS, ITEM_SONG_STORMS }, { QUEST_SONG_SUN, ITEM_SONG_SUN } }; void NextQuestInSlot(QuestItem slot) { if (!gPlayState) { return; } Player* player = GET_PLAYER(gPlayState); if (slot == QUEST_SONG_LULLABY || slot == QUEST_SONG_LULLABY_INTRO) { if (CHECK_QUEST_ITEM(QUEST_SONG_LULLABY)) { REMOVE_QUEST_ITEM(QUEST_SONG_LULLABY); REMOVE_QUEST_ITEM(QUEST_SONG_LULLABY_INTRO); } else if (CHECK_QUEST_ITEM(QUEST_SONG_LULLABY_INTRO)) { SET_QUEST_ITEM(QUEST_SONG_LULLABY); } else { SET_QUEST_ITEM(QUEST_SONG_LULLABY_INTRO); } } else if (slot == QUEST_SWORD) { uint32_t currentSword = GET_CUR_EQUIP_VALUE(EQUIP_TYPE_SWORD); if (GET_CUR_EQUIP_VALUE(EQUIP_TYPE_SWORD) >= EQUIP_VALUE_SWORD_GILDED) { SET_EQUIP_VALUE(EQUIP_TYPE_SWORD, EQUIP_VALUE_SWORD_NONE); } else { SET_EQUIP_VALUE(EQUIP_TYPE_SWORD, currentSword + 1); } if (GET_CUR_EQUIP_VALUE(EQUIP_TYPE_SWORD) == EQUIP_VALUE_SWORD_NONE) { BUTTON_ITEM_EQUIP(0, EQUIP_SLOT_B) = ITEM_NONE; } else { BUTTON_ITEM_EQUIP(0, EQUIP_SLOT_B) = ITEM_SWORD_KOKIRI + GET_CUR_EQUIP_VALUE(EQUIP_TYPE_SWORD) - EQUIP_VALUE_SWORD_KOKIRI; } if (GET_CUR_EQUIP_VALUE(EQUIP_TYPE_SWORD) == EQUIP_VALUE_SWORD_RAZOR) { gSaveContext.save.saveInfo.playerData.swordHealth = 100; } Interface_LoadItemIconImpl(gPlayState, EQUIP_SLOT_B); } else if (slot == QUEST_SHIELD) { uint32_t currentShield = GET_CUR_EQUIP_VALUE(EQUIP_TYPE_SHIELD); if (GET_CUR_EQUIP_VALUE(EQUIP_TYPE_SHIELD) == EQUIP_VALUE_SHIELD_NONE) { SET_EQUIP_VALUE(EQUIP_TYPE_SHIELD, EQUIP_VALUE_SHIELD_HERO); } else if (GET_CUR_EQUIP_VALUE(EQUIP_TYPE_SHIELD) == EQUIP_VALUE_SHIELD_HERO) { SET_EQUIP_VALUE(EQUIP_TYPE_SHIELD, EQUIP_VALUE_SHIELD_MIRROR); } else { SET_EQUIP_VALUE(EQUIP_TYPE_SHIELD, EQUIP_VALUE_SHIELD_NONE); } Player_SetEquipmentData(gPlayState, player); } else { if (CHECK_QUEST_ITEM(slot)) { REMOVE_QUEST_ITEM(slot); } else { SET_QUEST_ITEM(slot); } } } void DrawQuestSlot(QuestItem slot) { int x = slot % 6; int y = ((int)floor(slot / 6) % 4); ImGui::PushID(slot); ImGui::SetCursorPos( ImVec2(x * INV_GRID_WIDTH + INV_GRID_PADDING, y * INV_GRID_HEIGHT + INV_GRID_TOP_MARGIN + INV_GRID_PADDING)); ImTextureID textureId = Ship::Context::GetInstance()->GetWindow()->GetGui()->GetTextureByName( (const char*)gItemIcons[questToItemMap[slot]]); if (ImGui::ImageButton(std::to_string(slot).c_str(), textureId, ImVec2(INV_GRID_ICON_SIZE, INV_GRID_ICON_SIZE), ImVec2(0, 0), ImVec2(1, 1), ImVec4(0, 0, 0, 0), ImVec4(1, 1, 1, CHECK_QUEST_ITEM(slot) ? 1.0f : 0.4f))) { NextQuestInSlot(slot); } ImGui::PopID(); } ImVec2 DrawSong(QuestItem slot) { SongInfo(slot); if (ImGui::ImageButton(std::to_string(slot).c_str(), Ship::Context::GetInstance()->GetWindow()->GetGui()->GetTextureByName( (const char*)gItemIcons[questToItemMap[(QuestItem)slot]]), ImVec2(INV_GRID_ICON_SIZE / 1.5f, INV_GRID_ICON_SIZE), ImVec2(0, 0), ImVec2(1, 1), ImVec4(0, 0, 0, 0), colorTint)) { NextQuestInSlot(slot); } ImVec2 itemSize = ImGui::GetItemRectSize(); if (ImGui::IsItemHovered()) { ImGui::BeginTooltip(); ImGui::Text("%s", songTooltip); ImGui::EndTooltip(); } return itemSize; } void DrawQuestStatusTab() { ImGui::PushStyleColor(ImGuiCol_Button, ImVec4(1.0f, 1.0f, 1.0f, 0.0f)); ImGui::PushStyleVar(ImGuiStyleVar_ChildRounding, 3.0f); ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(8.0f, 8.0f)); ImGui::BeginChild("questTab", ImVec2(0, 0), true); if (UIWidgets::Button("Give All##items", { .size = UIWidgets::Sizes::Inline, .color = UIWidgets::Colors::Green })) { for (int32_t i = QUEST_REMAINS_ODOLWA; i <= QUEST_BOMBERS_NOTEBOOK; i++) { if (i != QUEST_SHIELD && i != QUEST_SWORD) { SET_QUEST_ITEM(i); } } SET_EQUIP_VALUE(EQUIP_TYPE_SHIELD, EQUIP_VALUE_SHIELD_MIRROR); if (GET_PLAYER_FORM != PLAYER_FORM_FIERCE_DEITY) { SET_EQUIP_VALUE(EQUIP_TYPE_SWORD, EQUIP_VALUE_SWORD_GILDED); if (gPlayState) { Player_SetEquipmentData(gPlayState, GET_PLAYER(gPlayState)); BUTTON_ITEM_EQUIP(0, EQUIP_SLOT_B) = ITEM_SWORD_KOKIRI + GET_CUR_EQUIP_VALUE(EQUIP_TYPE_SWORD) - EQUIP_VALUE_SWORD_KOKIRI; Interface_LoadItemIconImpl(gPlayState, EQUIP_SLOT_B); } } } ImGui::SameLine(); if (UIWidgets::Button("Reset##items", { .size = UIWidgets::Sizes::Inline, .color = UIWidgets::Colors::Red })) { for (int32_t i = QUEST_REMAINS_ODOLWA; i <= QUEST_BOMBERS_NOTEBOOK; i++) { if (i != QUEST_SHIELD && i != QUEST_SWORD) { REMOVE_QUEST_ITEM(i); } } REMOVE_QUEST_ITEM(QUEST_SONG_LULLABY_INTRO); SET_EQUIP_VALUE(EQUIP_TYPE_SHIELD, EQUIP_VALUE_SHIELD_HERO); if (GET_PLAYER_FORM != PLAYER_FORM_FIERCE_DEITY) { SET_EQUIP_VALUE(EQUIP_TYPE_SWORD, EQUIP_VALUE_SWORD_KOKIRI); if (gPlayState) { Player_SetEquipmentData(gPlayState, GET_PLAYER(gPlayState)); BUTTON_ITEM_EQUIP(0, EQUIP_SLOT_B) = ITEM_SWORD_KOKIRI + GET_CUR_EQUIP_VALUE(EQUIP_TYPE_SWORD) - EQUIP_VALUE_SWORD_KOKIRI; Interface_LoadItemIconImpl(gPlayState, EQUIP_SLOT_B); } } } ImGui::BeginChild("leftColumn", ImVec2(ImGui::GetWindowWidth() / 2, 0)); ImGui::SeparatorText("Boss Remains"); for (int32_t i = QUEST_REMAINS_ODOLWA; i <= QUEST_REMAINS_TWINMOLD; i++) { QuestItem slot = static_cast(i); DrawQuestSlot(slot); } ImGui::SeparatorText("Songs"); auto drawSongRange = [](int32_t start, int32_t end) { for (int32_t i = start; i <= end; i++) { ImVec2 itemSize = DrawSong(static_cast(i)); if (i != end) { float currentCursorX = ImGui::GetCursorPosX(); float nextItemMaxX = currentCursorX + ImGui::GetStyle().ItemSpacing.x + itemSize.x; float regionMaxX = ImGui::GetWindowContentRegionMax().x; if (nextItemMaxX <= regionMaxX) { ImGui::SameLine(); } } } }; drawSongRange(QUEST_SONG_TIME, QUEST_SONG_SUN); drawSongRange(QUEST_SONG_SONATA, QUEST_SONG_SARIA); ImGui::SeparatorText("Equipment"); if (GET_PLAYER_FORM == PLAYER_FORM_FIERCE_DEITY) { ImTextureID swordTextureId = Ship::Context::GetInstance()->GetWindow()->GetGui()->GetTextureByName( (const char*)gItemIcons[ITEM_SWORD_DEITY]); ImGui::ImageButton(std::to_string(ITEM_SWORD_DEITY).c_str(), swordTextureId, ImVec2(INV_GRID_ICON_SIZE, INV_GRID_ICON_SIZE), ImVec2(0, 0), ImVec2(1, 1), ImVec4(0, 0, 0, 0), ImVec4(1, 1, 1, 1)); } else { int swordValue = GET_CUR_EQUIP_VALUE(EQUIP_TYPE_SWORD); if (swordValue == EQUIP_VALUE_SWORD_NONE) { swordValue = EQUIP_VALUE_SWORD_KOKIRI; } ImTextureID swordTextureId = Ship::Context::GetInstance()->GetWindow()->GetGui()->GetTextureByName( (const char*)gItemIcons[ITEM_SWORD_KOKIRI + swordValue - EQUIP_VALUE_SWORD_KOKIRI]); if (ImGui::ImageButton(std::to_string(ITEM_SWORD_KOKIRI).c_str(), swordTextureId, ImVec2(INV_GRID_ICON_SIZE, INV_GRID_ICON_SIZE), ImVec2(0, 0), ImVec2(1, 1), ImVec4(0, 0, 0, 0), ImVec4(1, 1, 1, GET_CUR_EQUIP_VALUE(EQUIP_TYPE_SWORD) ? 1 : 0.4f))) { NextQuestInSlot(QUEST_SWORD); } } ImGui::SameLine(); int shieldValue = GET_CUR_EQUIP_VALUE(EQUIP_TYPE_SHIELD); if (shieldValue == EQUIP_VALUE_SHIELD_NONE) { shieldValue = EQUIP_VALUE_SHIELD_HERO; } ImTextureID shieldTextureId = Ship::Context::GetInstance()->GetWindow()->GetGui()->GetTextureByName( (const char*)gItemIcons[ITEM_SHIELD_HERO + shieldValue - EQUIP_VALUE_SHIELD_HERO]); if (ImGui::ImageButton(std::to_string(ITEM_SHIELD_HERO).c_str(), shieldTextureId, ImVec2(INV_GRID_ICON_SIZE, INV_GRID_ICON_SIZE), ImVec2(0, 0), ImVec2(1, 1), ImVec4(0, 0, 0, 0), ImVec4(1, 1, 1, GET_CUR_EQUIP_VALUE(EQUIP_TYPE_SHIELD) ? 1 : 0.4f))) { NextQuestInSlot(QUEST_SHIELD); } ImGui::SameLine(); ImTextureID textureId = Ship::Context::GetInstance()->GetWindow()->GetGui()->GetTextureByName( (const char*)gItemIcons[ITEM_BOMBERS_NOTEBOOK]); if (ImGui::ImageButton(std::to_string(ITEM_BOMBERS_NOTEBOOK).c_str(), textureId, ImVec2(INV_GRID_ICON_SIZE, INV_GRID_ICON_SIZE), ImVec2(0, 0), ImVec2(1, 1), ImVec4(0, 0, 0, 0), ImVec4(1, 1, 1, CHECK_QUEST_ITEM(QUEST_BOMBERS_NOTEBOOK) ? 1.0f : 0.4f))) { NextQuestInSlot(QUEST_BOMBERS_NOTEBOOK); } ImGui::SeparatorText("Heart Pieces"); int32_t pohCount = (gSaveContext.save.saveInfo.inventory.questItems & 0xF0000000) >> 28; UIWidgets::PushStyleCombobox(UIWidgets::Colors::Red); if (ImGui::BeginCombo("##PoHcount", std::to_string(pohCount).c_str())) { for (int32_t i = 0; i < 4; i++) { if (ImGui::Selectable(std::to_string(i).c_str(), pohCount == i)) { gSaveContext.save.saveInfo.inventory.questItems &= ~0xF0000000; gSaveContext.save.saveInfo.inventory.questItems |= (i << 28); } } ImGui::EndCombo(); } UIWidgets::PopStyleCombobox(); ImGui::EndChild(); ImGui::SameLine(); ImGui::BeginChild("rightColumn", ImVec2(0, 0)); ImGui::SeparatorText("Gold Skulltula Tokens"); int OceanSkullTokens = Inventory_GetSkullTokenCount(SCENE_KINDAN2); int SwampSkullTokens = Inventory_GetSkullTokenCount(SCENE_KINSTA1); UIWidgets::PushStyleSlider(); ImGui::PushItemWidth(ImGui::GetWindowWidth()); if (ImGui::SliderInt("##swampSkulltulas", &SwampSkullTokens, 0, 30, "Swamp Tokens: %d")) { gSaveContext.save.saveInfo.skullTokenCount = ((int)(SwampSkullTokens & 0xFFFF) << 0x10) | (gSaveContext.save.saveInfo.skullTokenCount & 0xFFFF); } if (ImGui::SliderInt("##oceanSkulltulas", &OceanSkullTokens, 0, 30, "Ocean Tokens: %d")) { gSaveContext.save.saveInfo.skullTokenCount = (gSaveContext.save.saveInfo.skullTokenCount & 0xFFFF0000) | (OceanSkullTokens & 0xFFFF); } ImGui::PopItemWidth(); UIWidgets::PopStyleSlider(); ImGui::EndChild(); ImGui::EndChild(); ImGui::PopStyleVar(2); ImGui::PopStyleColor(1); } void SetDungeonItems(uint32_t dungeonItem, uint32_t dungeonId) { if (CHECK_DUNGEON_ITEM(dungeonItem, dungeonId)) { REMOVE_DUNGEON_ITEM(dungeonItem, dungeonId); } else { SET_DUNGEON_ITEM(dungeonItem, dungeonId); } } const char* dungeonNames[4] = { "Woodfall Temple", "Snowhead Temple", "Great Bay Temple", "Stone Tower Temple" }; uint32_t smallKeyCounts[4] = { 1, 3, 1, 4 }; const char* fairyIcons[] = { gDungeonStrayFairyWoodfallIconTex, gDungeonStrayFairySnowheadIconTex, gDungeonStrayFairyGreatBayIconTex, gDungeonStrayFairyStoneTowerIconTex }; void DrawDungeonItemTab() { ImGui::PushStyleColor(ImGuiCol_Button, ImVec4(1.0f, 1.0f, 1.0f, 0.0f)); ImGui::PushStyleVar(ImGuiStyleVar_ChildRounding, 3.0f); ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(8.0f, 8.0f)); ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(0, 0)); ImGui::BeginChild("dungeonTab", ImVec2(0, 0), true); for (int i = DUNGEON_SCENE_INDEX_WOODFALL_TEMPLE; i <= DUNGEON_SCENE_INDEX_STONE_TOWER_TEMPLE; i++) { std::string stray_id = "Stray" + std::to_string(i); std::string map_id = "Map" + std::to_string(i); std::string comp_id = "Compass" + std::to_string(i); std::string sKey_id = "Small Key" + std::to_string(i); std::string bKey_id = "Boss Key" + std::to_string(i); int dungeonId = i; ImGui::BeginChild(std::to_string(i).c_str(), ImVec2(INV_GRID_WIDTH * 6 + INV_GRID_PADDING * 2, INV_GRID_HEIGHT * 1.5 + INV_GRID_PADDING), ImGuiChildFlags_Border); ImGui::Text("%s", dungeonNames[i]); if (ImGui::ImageButton( stray_id.c_str(), Ship::Context::GetInstance()->GetWindow()->GetGui()->GetTextureByName(fairyIcons[dungeonId]), ImVec2(INV_GRID_ICON_SIZE, INV_GRID_ICON_SIZE), ImVec2(0, 0), ImVec2(1, 1), ImVec4(0, 0, 0, 0), ImVec4(1, 1, 1, gSaveContext.save.saveInfo.inventory.strayFairies[dungeonId] ? 1.0f : 0.4f))) { ImGui::OpenPopup("strayFairies"); } ImGui::SameLine(); if (ImGui::ImageButton( map_id.c_str(), Ship::Context::GetInstance()->GetWindow()->GetGui()->GetTextureByName(gQuestIconDungeonMapTex), ImVec2(INV_GRID_ICON_SIZE, INV_GRID_ICON_SIZE), ImVec2(0, 0), ImVec2(1, 1), ImVec4(0, 0, 0, 0), ImVec4(1, 1, 1, CHECK_DUNGEON_ITEM(DUNGEON_MAP, i) ? 1.0f : 0.4f))) { SetDungeonItems(DUNGEON_MAP, i); } ImGui::SameLine(); if (ImGui::ImageButton( comp_id.c_str(), Ship::Context::GetInstance()->GetWindow()->GetGui()->GetTextureByName(gQuestIconCompassTex), ImVec2(INV_GRID_ICON_SIZE, INV_GRID_ICON_SIZE), ImVec2(0, 0), ImVec2(1, 1), ImVec4(0, 0, 0, 0), ImVec4(1, 1, 1, CHECK_DUNGEON_ITEM(DUNGEON_COMPASS, i) ? 1.0f : 0.4f))) { SetDungeonItems(DUNGEON_COMPASS, i); } ImGui::SameLine(); if (ImGui::ImageButton( sKey_id.c_str(), Ship::Context::GetInstance()->GetWindow()->GetGui()->GetTextureByName(gQuestIconSmallKeyTex), ImVec2(INV_GRID_ICON_SIZE, INV_GRID_ICON_SIZE), ImVec2(0, 0), ImVec2(1, 1), ImVec4(0, 0, 0, 0), ImVec4(1, 1, 1, DUNGEON_KEY_COUNT(i) + 1 ? 1.0f : 0.4f))) { ImGui::OpenPopup("smallKeys"); } ImGui::SameLine(); if (ImGui::ImageButton( bKey_id.c_str(), Ship::Context::GetInstance()->GetWindow()->GetGui()->GetTextureByName(gQuestIconBossKeyTex), ImVec2(INV_GRID_ICON_SIZE, INV_GRID_ICON_SIZE), ImVec2(0, 0), ImVec2(1, 1), ImVec4(0, 0, 0, 0), ImVec4(1, 1, 1, CHECK_DUNGEON_ITEM(DUNGEON_BOSS_KEY, i) ? 1.0f : 0.4f))) { SetDungeonItems(DUNGEON_BOSS_KEY, i); } if (ImGui::BeginPopup("strayFairies")) { UIWidgets::PushStyleSlider(UIWidgets::Colors(CVarGetInteger("gSettings.Menu.Theme", 5))); s32 minStray = 0; s32 maxStray = 15; int currentStrays = gSaveContext.save.saveInfo.inventory.strayFairies[dungeonId]; ImGui::PushItemWidth(ImGui::GetFontSize() * 12.0f); if (ImGui::SliderScalar("##strayCount", ImGuiDataType_S32, ¤tStrays, &minStray, &maxStray, "Strays: %d")) { gSaveContext.save.saveInfo.inventory.strayFairies[dungeonId] = currentStrays; } ImGui::PopItemWidth(); UIWidgets::PopStyleSlider(); ImGui::EndPopup(); } if (ImGui::BeginPopup("smallKeys")) { UIWidgets::PushStyleSlider(UIWidgets::Colors(CVarGetInteger("gSettings.Menu.Theme", 5))); s32 minKey = -1; s32 maxKey = smallKeyCounts[dungeonId]; int currentKeys = gSaveContext.save.saveInfo.inventory.dungeonKeys[dungeonId]; ImGui::PushItemWidth(ImGui::GetFontSize() * 12.0f); if (ImGui::SliderScalar("##sKeyCount", ImGuiDataType_S32, ¤tKeys, &minKey, &maxKey, "Small Keys: %d")) { gSaveContext.save.saveInfo.inventory.dungeonKeys[dungeonId] = currentKeys; } ImGui::PopItemWidth(); UIWidgets::PopStyleSlider(); ImGui::EndPopup(); } ImGui::EndChild(); } ImGui::EndChild(); ImGui::PopStyleVar(3); ImGui::PopStyleColor(1); } void GetPlayerForm(uint32_t form) { switch (form) { case PLAYER_FORM_FIERCE_DEITY: curForm = "Fierce Deity"; formColor = UIWidgets::Colors::Red; formObject = OBJECT_LINK_BOY; break; case PLAYER_FORM_GORON: curForm = "Goron"; formColor = UIWidgets::Colors::Brown; formObject = OBJECT_LINK_GORON; break; case PLAYER_FORM_ZORA: curForm = "Zora"; formColor = UIWidgets::Colors::LightBlue; formObject = OBJECT_LINK_ZORA; break; case PLAYER_FORM_DEKU: curForm = "Deku"; formColor = UIWidgets::Colors::DarkGreen; formObject = OBJECT_LINK_NUTS; break; case PLAYER_FORM_HUMAN: curForm = "Human"; formColor = UIWidgets::Colors::Green; formObject = OBJECT_LINK_CHILD; break; default: break; } } void ClearAllEquippedItems() { auto& buttonItems = gSaveContext.save.saveInfo.equips.buttonItems; auto& cButtonSlots = gSaveContext.save.saveInfo.equips.cButtonSlots; for (size_t form = 0; form < ARRAY_COUNT(buttonItems); form++) { for (size_t slot = 0; slot < ARRAY_COUNT(buttonItems[form]); slot++) { if (slot == EQUIP_SLOT_B) { // Goron/Deku/Zora/FD expect their innate B actions; leave untouched. // Form 0 is shared by FD and Human via CUR_FORM if (form != 0 || GET_PLAYER_FORM == PLAYER_FORM_FIERCE_DEITY) { continue; } int swordValue = GET_CUR_EQUIP_VALUE(EQUIP_TYPE_SWORD); if (swordValue == EQUIP_VALUE_SWORD_NONE) { buttonItems[form][slot] = ITEM_NONE; } else { buttonItems[form][slot] = ITEM_SWORD_KOKIRI + swordValue - EQUIP_VALUE_SWORD_KOKIRI; } continue; } // B slot stores item directly (sword); cButtonSlots only maps C-buttons to inventory slots buttonItems[form][slot] = ITEM_NONE; cButtonSlots[form][slot] = SLOT_NONE; } } auto& dpadItems = gSaveContext.save.shipSaveInfo.dpadEquips.dpadItems; auto& dpadSlots = gSaveContext.save.shipSaveInfo.dpadEquips.dpadSlots; for (size_t form = 0; form < ARRAY_COUNT(dpadItems); form++) { for (size_t slot = 0; slot < ARRAY_COUNT(dpadItems[form]); slot++) { dpadItems[form][slot] = ITEM_NONE; dpadSlots[form][slot] = SLOT_NONE; } } } void DrawPlayerTab() { ImGui::PushStyleColor(ImGuiCol_Button, ImVec4(1.0f, 1.0f, 1.0f, 0.0f)); ImGui::PushStyleVar(ImGuiStyleVar_ChildRounding, 3.0f); ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(8.0f, 8.0f)); ImGui::BeginChild("playerTab", ImVec2(0, 0), true); if (gPlayState) { Player* player = GET_PLAYER(gPlayState); UIWidgets::BeginCardLayout({ .columnsPerRow = 2, .minColumnWidth = 420.0f }); UIWidgets::BeginCard("playerSpeed"); ImGui::Text("Link's Speed"); UIWidgets::PushStyleCombobox(formColor); ImGui::PushItemWidth(ImGui::GetFontSize() * 6); ImGui::InputScalar("XZ Speed", ImGuiDataType_Float, &player->speedXZ); ImGui::InputScalar("Y Velocity", ImGuiDataType_Float, &player->actor.velocity.y); ImGui::InputScalar("Ledge Height", ImGuiDataType_Float, &player->yDistToLedge); ImGui::InputScalar("Invincibility Timer", ImGuiDataType_S16, &player->invincibilityTimer); ImGui::InputScalar("Gravity", ImGuiDataType_Float, &player->actor.gravity); ImGui::PopItemWidth(); UIWidgets::PopStyleCombobox(); UIWidgets::EndCard(); UIWidgets::BeginCard("playerLocation"); GetPlayerForm(GET_PLAYER_FORM); ImGui::Text("%s Link", curForm); ImGui::Text("Position:"); UIWidgets::PushStyleCombobox(formColor); ImGui::PushItemWidth(ImGui::GetFontSize() * 6); ImGui::InputScalar("X Pos", ImGuiDataType_Float, &player->actor.world.pos.x); ImGui::SameLine(); ImGui::InputScalar("Y Pos", ImGuiDataType_Float, &player->actor.world.pos.y); ImGui::SameLine(); ImGui::InputScalar("Z Pos", ImGuiDataType_Float, &player->actor.world.pos.z); ImGui::Text("Rotation:"); ImGui::InputScalar("X Rot", ImGuiDataType_S16, &player->actor.world.rot.x); ImGui::SameLine(); ImGui::InputScalar("Y Rot", ImGuiDataType_S16, &player->actor.world.rot.y); ImGui::SameLine(); ImGui::InputScalar("Z Rot", ImGuiDataType_S16, &player->actor.world.rot.z); ImGui::PopItemWidth(); UIWidgets::PopStyleCombobox(); UIWidgets::EndCard(); UIWidgets::BeginCard("playerForm"); ImGui::Text("Change Link's Current Form"); for (int i = PLAYER_FORM_FIERCE_DEITY; i <= PLAYER_FORM_HUMAN; i++) { GetPlayerForm(i); UIWidgets::PushStyleButton(formColor); if (ImGui::Button(curForm)) { s16 objectId = formObject; if (i != PLAYER_FORM_HUMAN) { gSaveContext.save.equippedMask = PLAYER_MASK_FIERCE_DEITY + i; } gActorOverlayTable[ACTOR_PLAYER].profile->objectId = objectId; func_8012F73C(&gPlayState->objectCtx, player->actor.objectSlot, objectId); player->actor.objectSlot = Object_GetSlot(&gPlayState->objectCtx, GAMEPLAY_KEEP); gSaveContext.save.playerForm = i; s32 objectSlot = Object_GetSlot(&gPlayState->objectCtx, gActorOverlayTable[ACTOR_PLAYER].profile->objectId); player->actor.objectSlot = objectSlot; player->actor.shape.rot.z = GET_PLAYER_FORM + 1; player->actor.init = PlayerCall_Init; player->actor.update = PlayerCall_Update; player->actor.draw = PlayerCall_Draw; gSaveContext.save.equippedMask = PLAYER_MASK_NONE; TransitionFade_SetColor(&gPlayState->unk_18E48, 0x000000); R_TRANS_FADE_FLASH_ALPHA_STEP = -1; Player_PlaySfx(GET_PLAYER(gPlayState), NA_SE_SY_TRANSFORM_MASK_FLASH); } UIWidgets::PopStyleButton(); if (i != PLAYER_FORM_HUMAN) { ImGui::SameLine(); } } ImGui::Separator(); ImGui::Text("Link's Current Equipment"); if (GET_PLAYER_FORM == PLAYER_FORM_HUMAN) { static const char* weaponCombo[] = { "Kokiri Sword", "Razor Sword", "Gilded Sword" }; static const char* shieldCombo[] = { "Hero's Shield", "Mirror Shield" }; static int currentWeapon = 0; static int currentShield = 0; UIWidgets::PushStyleCombobox(UIWidgets::Colors::Green); ImGui::PushItemWidth(ImGui::GetFontSize() * 15); if (ImGui::BeginCombo("Equipped Sword", weaponCombo[currentWeapon])) { for (int i = EQUIP_VALUE_SWORD_KOKIRI; i <= EQUIP_VALUE_SWORD_GILDED; i++) { const bool isSelected = (currentWeapon == i); if (ImGui::Selectable(weaponCombo[i - 1], isSelected)) { currentWeapon = i - 1; SET_EQUIP_VALUE(EQUIP_TYPE_SWORD, i); if (GET_CUR_EQUIP_VALUE(EQUIP_TYPE_SWORD) == EQUIP_VALUE_SWORD_RAZOR) { gSaveContext.save.saveInfo.playerData.swordHealth = 100; } BUTTON_ITEM_EQUIP(0, EQUIP_SLOT_B) = ITEM_SWORD_KOKIRI + GET_CUR_EQUIP_VALUE(EQUIP_TYPE_SWORD) - EQUIP_VALUE_SWORD_KOKIRI; Interface_LoadItemIconImpl(gPlayState, EQUIP_SLOT_B); } if (isSelected) { ImGui::SetItemDefaultFocus(); } } ImGui::EndCombo(); } if (ImGui::BeginCombo("Equipped Shield", shieldCombo[currentShield])) { for (int i = EQUIP_VALUE_SHIELD_HERO; i <= EQUIP_VALUE_SHIELD_MIRROR; i++) { const bool isSelected = (currentShield == i); if (ImGui::Selectable(shieldCombo[i - 1], isSelected)) { currentShield = i - 1; SET_EQUIP_VALUE(EQUIP_TYPE_SHIELD, i); Player_SetEquipmentData(gPlayState, player); } if (isSelected) { ImGui::SetItemDefaultFocus(); } } ImGui::EndCombo(); } ImGui::PopItemWidth(); UIWidgets::PopStyleCombobox(); } else { ImGui::Separator(); ImGui::Text(" No Equipment in this Form"); ImGui::Separator(); } UIWidgets::EndCard(); UIWidgets::BeginCard("currentItems"); ImGui::Text("Current Items"); // Two-column layout ImGui::Columns(2, nullptr, false); // Left column: C-buttons ImGui::PushItemWidth(ImGui::GetFontSize() * 6); UIWidgets::PushStyleInput(); static u8 one = 1; ImGui::InputScalar("B Button", ImGuiDataType_U8, &CUR_FORM_EQUIP(0), &one, NULL); ImGui::InputScalar("C Left", ImGuiDataType_U8, &CUR_FORM_EQUIP(1), &one, NULL); ImGui::InputScalar("C Down", ImGuiDataType_U8, &CUR_FORM_EQUIP(2), &one, NULL); ImGui::InputScalar("C Right", ImGuiDataType_U8, &CUR_FORM_EQUIP(3), &one, NULL); UIWidgets::PopStyleInput(); ImGui::PopItemWidth(); // Right column: D-pad ImGui::NextColumn(); ImGui::PushItemWidth(ImGui::GetFontSize() * 6); UIWidgets::PushStyleInput(); ImGui::InputScalar("D-Pad Right", ImGuiDataType_U8, &DPAD_GET_CUR_FORM_BTN_ITEM(EQUIP_SLOT_D_RIGHT), &one, NULL); ImGui::InputScalar("D-Pad Left", ImGuiDataType_U8, &DPAD_GET_CUR_FORM_BTN_ITEM(EQUIP_SLOT_D_LEFT), &one, NULL); ImGui::InputScalar("D-Pad Down", ImGuiDataType_U8, &DPAD_GET_CUR_FORM_BTN_ITEM(EQUIP_SLOT_D_DOWN), &one, NULL); ImGui::InputScalar("D-Pad Up", ImGuiDataType_U8, &DPAD_GET_CUR_FORM_BTN_ITEM(EQUIP_SLOT_D_UP), &one, NULL); UIWidgets::PopStyleInput(); ImGui::PopItemWidth(); ImGui::Columns(1); // Clear All button ImGui::Spacing(); if (UIWidgets::Button("Clear All", { .size = UIWidgets::Sizes::Inline, .color = UIWidgets::Colors::Red })) { ClearAllEquippedItems(); } UIWidgets::EndCard(); UIWidgets::BeginCard("playerStates"); ImGui::Text("Player States"); uint32_t states[4] = { player->stateFlags1, player->stateFlags2, player->stateFlags3, player->meleeWeaponState }; if (ImGui::BeginTable("stateTable", 4)) { GetPlayerForm(GET_PLAYER_FORM); ImGui::PushStyleVar(ImGuiTableColumnFlags_WidthFixed, 15.0f); ImGui::PushStyleColor(ImGuiCol_TableHeaderBg, formColor); ImGui::TableSetupColumn("State 1"); ImGui::TableSetupColumn("State 2"); ImGui::TableSetupColumn("State 3"); ImGui::TableSetupColumn("Sword State"); ImGui::TableHeadersRow(); ImGui::TableNextColumn(); for (int i = 0; i <= 3; i++) { ImGui::Text("%s", std::to_string(states[i]).c_str()); ImGui::TableNextColumn(); } ImGui::PopStyleColor(1); ImGui::PopStyleVar(1); ImGui::EndTable(); } UIWidgets::EndCard(); UIWidgets::EndCardLayout(); } ImGui::EndChild(); ImGui::PopStyleVar(2); ImGui::PopStyleColor(1); } const char* regGroupNames[] = { "REG (0)", "SREG (1)", "OREG (2)", "PREG (3)", "QREG (4)", "MREG (5)", "YREG (6)", "DREG (7)", "UREG (8)", "IREG (9)", "ZREG (10)", "CREG (11)", "NREG (12)", "KREG (13)", "XREG (14)", "cREG (15)", "sREG (16)", "iREG (17)", "WREG (18)", "AREG (19)", "VREG (20)", "HREG (21)", "GREG (22)", "mREG (23)", "nREG (24)", "BREG (25)", "dREG (26)", "kREG (27)", "bREG (28)", }; // 2S2H Added columns to scene table: entranceSceneId, betterMapSelectIndex, humanName #define DEFINE_SCENE(_name, enumValue, _textId, _drawConfig, _restrictionFlags, _persistentCycleFlags, \ _entranceSceneId, _betterMapSelectIndex, humanName) \ { enumValue, humanName }, #define DEFINE_SCENE_UNSET(_enumValue) std::unordered_map sceneList = { #include "tables/scene_table.h" }; #undef DEFINE_SCENE #undef DEFINE_SCENE_UNSET void DrawRegEditorTab() { UIWidgets::PushStyleSlider(); ImGui::SliderScalar("Reg Group", ImGuiDataType_U8, &gRegEditor->regGroup, &S8_ZERO, ®_GROUPS_MAX, regGroupNames[gRegEditor->regGroup]); ImGui::SliderScalar("Reg Page", ImGuiDataType_U8, &gRegEditor->regPage, &S8_ZERO, ®_PAGES_MAX, "%d"); UIWidgets::PopStyleSlider(); ImGui::BeginChild("regSliders", ImVec2(0, 0), ImGuiChildFlags_Border); ImGui::Columns(2, nullptr, false); ImGui::SetColumnWidth(0, 100.0f); for (int i = 0; i < REG_PER_PAGE; i++) { ImGui::PushID(i); ImGui::PushItemWidth(ImGui::GetContentRegionAvail().x - 4.0f); ImGui::Text("%02X (%d)", i + gRegEditor->regPage * REG_PER_PAGE, i + gRegEditor->regPage * REG_PER_PAGE); ImGui::NextColumn(); ImGui::PushStyleColor(ImGuiCol_FrameBg, UIWidgets::ColorValues.at(UIWidgets::Colors::Gray)); ImGui::PushStyleVar(ImGuiStyleVar_FrameRounding, 3.0f); ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(10.0f, 8.0f)); ImGui::InputScalar( "##regInput", ImGuiDataType_S16, &gRegEditor->data[i + gRegEditor->regPage * REG_PER_PAGE + gRegEditor->regGroup * REG_PER_PAGE * REG_PAGES], NULL, NULL, "%d"); ImGui::PopStyleVar(2); ImGui::PopStyleColor(1); ImGui::NextColumn(); ImGui::PopItemWidth(); ImGui::PopID(); } ImGui::Columns(1); ImGui::EndChild(); } const char* flagEditorSections[] = { "currentSceneFlags", "weekEventReg", "eventInf", "scenesVisible", "owlActivation", "permanentSceneFlags", "cycleSceneFlags", }; void DrawFlagsTab() { ImGui::PushStyleVar(ImGuiStyleVar_ChildRounding, 3.0f); ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(8.0f, 8.0f)); ImGui::BeginChild("flagsBox", ImVec2(0, 0), true); static int selectedFlagSection = 0; UIWidgets::Combobox("Flag Type", &selectedFlagSection, flagEditorSections, { .alignment = UIWidgets::ComponentAlignment::Left, .labelPosition = UIWidgets::LabelPosition::None, }); if (selectedFlagSection == WEEK_EVENT_REG) { // Draw color legend ImGui::SameLine(); ImGui::TextColored(UIWidgets::ColorValues.at(UIWidgets::Colors::Gray), ICON_FA_SQUARE "Persistent"); ImGui::SameLine(); ImGui::TextColored(UIWidgets::ColorValues.at(UIWidgets::Colors::LightBlue), ICON_FA_SQUARE "Cycle"); ImGui::SameLine(); ImGui::TextColored(UIWidgets::ColorValues.at(UIWidgets::Colors::Orange), ICON_FA_SQUARE "Scene/Other"); } static int16_t selectedScene = 0; if (selectedFlagSection == PERMANENT_SCENE_FLAGS || selectedFlagSection == CYCLE_SCENE_FLAGS) { ImGui::SameLine(); UIWidgets::ComboboxWithSearch("Scene", &selectedScene, &sceneList, { .alignment = UIWidgets::ComponentAlignment::Left, .labelPosition = UIWidgets::LabelPosition::None, }); if (gPlayState != NULL) { ImGui::SameLine(); if (UIWidgets::Button("Current", UIWidgets::ButtonOptions{ { .color = UIWidgets::Colors::Gray } }.Size( UIWidgets::Sizes::Inline))) { selectedScene = gPlayState->sceneId; } } } ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(1.0f, 1.0f)); switch (selectedFlagSection) { case CURRENT_SCENE_FLAGS: if (gPlayState == NULL) { ImGui::Text("Play state is NULL, cannot display scene flags"); break; } ImGui::BeginGroup(); ImGui::AlignTextToFramePadding(); ImGui::Text("switches[0]"); ImGui::SameLine(110); if (UIWidgets::Button("All##switches0", { .size = UIWidgets::Sizes::Inline, .color = UIWidgets::Colors::Gray })) { gPlayState->actorCtx.sceneFlags.switches[0] = UINT32_MAX; } ImGui::SameLine(); if (UIWidgets::Button("Clear##switches0", { .size = UIWidgets::Sizes::Inline, .color = UIWidgets::Colors::Red })) { gPlayState->actorCtx.sceneFlags.switches[0] = 0; } UIWidgets::DrawFlagArray32("##switches0", gPlayState->actorCtx.sceneFlags.switches[0]); ImGui::EndGroup(); ImGui::SameLine(0, 10.0f); ImGui::BeginGroup(); ImGui::AlignTextToFramePadding(); ImGui::Text("switches[1]"); ImGui::SameLine(110); if (UIWidgets::Button("All##switches1", { .size = UIWidgets::Sizes::Inline, .color = UIWidgets::Colors::Gray })) { gPlayState->actorCtx.sceneFlags.switches[1] = UINT32_MAX; } ImGui::SameLine(); if (UIWidgets::Button("Clear##switches1", { .size = UIWidgets::Sizes::Inline, .color = UIWidgets::Colors::Red })) { gPlayState->actorCtx.sceneFlags.switches[1] = 0; } UIWidgets::DrawFlagArray32("##switches1", gPlayState->actorCtx.sceneFlags.switches[1]); ImGui::EndGroup(); ImGui::BeginGroup(); ImGui::AlignTextToFramePadding(); ImGui::Text("switches[2]"); ImGui::SameLine(110); if (UIWidgets::Button("All##switches2", { .size = UIWidgets::Sizes::Inline, .color = UIWidgets::Colors::Gray })) { gPlayState->actorCtx.sceneFlags.switches[2] = UINT32_MAX; } ImGui::SameLine(); if (UIWidgets::Button("Clear##switches2", { .size = UIWidgets::Sizes::Inline, .color = UIWidgets::Colors::Red })) { gPlayState->actorCtx.sceneFlags.switches[2] = 0; } UIWidgets::DrawFlagArray32("##switches2", gPlayState->actorCtx.sceneFlags.switches[2]); ImGui::EndGroup(); ImGui::SameLine(0, 10.0f); ImGui::BeginGroup(); ImGui::AlignTextToFramePadding(); ImGui::Text("switches[3]"); ImGui::SameLine(110); if (UIWidgets::Button("All##switches3", { .size = UIWidgets::Sizes::Inline, .color = UIWidgets::Colors::Gray })) { gPlayState->actorCtx.sceneFlags.switches[3] = UINT32_MAX; } ImGui::SameLine(); if (UIWidgets::Button("Clear##switches3", { .size = UIWidgets::Sizes::Inline, .color = UIWidgets::Colors::Red })) { gPlayState->actorCtx.sceneFlags.switches[3] = 0; } UIWidgets::DrawFlagArray32("##switches3", gPlayState->actorCtx.sceneFlags.switches[3]); ImGui::EndGroup(); ImGui::BeginGroup(); ImGui::AlignTextToFramePadding(); ImGui::Text("chest"); ImGui::SameLine(110); if (UIWidgets::Button("All##chest", { .size = UIWidgets::Sizes::Inline, .color = UIWidgets::Colors::Gray })) { gPlayState->actorCtx.sceneFlags.chest = UINT32_MAX; } ImGui::SameLine(); if (UIWidgets::Button("Clear##chest", { .size = UIWidgets::Sizes::Inline, .color = UIWidgets::Colors::Red })) { gPlayState->actorCtx.sceneFlags.chest = 0; } UIWidgets::DrawFlagArray32("##chest", gPlayState->actorCtx.sceneFlags.chest); ImGui::EndGroup(); ImGui::SameLine(0, 10.0f); ImGui::BeginGroup(); ImGui::AlignTextToFramePadding(); ImGui::Text("clearedRoom"); ImGui::SameLine(110); if (UIWidgets::Button("All##clearedRoom", { .size = UIWidgets::Sizes::Inline, .color = UIWidgets::Colors::Gray })) { gPlayState->actorCtx.sceneFlags.clearedRoom = UINT32_MAX; } ImGui::SameLine(); if (UIWidgets::Button("Clear##clearedRoom", { .size = UIWidgets::Sizes::Inline, .color = UIWidgets::Colors::Red })) { gPlayState->actorCtx.sceneFlags.clearedRoom = 0; } UIWidgets::DrawFlagArray32("##clearedRoom", gPlayState->actorCtx.sceneFlags.clearedRoom); ImGui::EndGroup(); ImGui::BeginGroup(); ImGui::AlignTextToFramePadding(); ImGui::Text("clearedRoomTemp"); ImGui::SameLine(110); if (UIWidgets::Button("All##clearedRoomTemp", { .size = UIWidgets::Sizes::Inline, .color = UIWidgets::Colors::Gray })) { gPlayState->actorCtx.sceneFlags.clearedRoomTemp = UINT32_MAX; } ImGui::SameLine(); if (UIWidgets::Button("Clear##clearedRoomTemp", { .size = UIWidgets::Sizes::Inline, .color = UIWidgets::Colors::Red })) { gPlayState->actorCtx.sceneFlags.clearedRoomTemp = 0; } UIWidgets::DrawFlagArray32("##clearedRoomTemp", gPlayState->actorCtx.sceneFlags.clearedRoomTemp); ImGui::EndGroup(); ImGui::SameLine(0, 10.0f); ImGui::BeginGroup(); ImGui::AlignTextToFramePadding(); ImGui::Text("collectible[0]"); ImGui::SameLine(110); if (UIWidgets::Button("All##collectible0", { .size = UIWidgets::Sizes::Inline, .color = UIWidgets::Colors::Gray })) { gPlayState->actorCtx.sceneFlags.collectible[0] = UINT32_MAX; } ImGui::SameLine(); if (UIWidgets::Button("Clear##collectible0", { .size = UIWidgets::Sizes::Inline, .color = UIWidgets::Colors::Red })) { gPlayState->actorCtx.sceneFlags.collectible[0] = 0; } UIWidgets::DrawFlagArray32("##collectible0", gPlayState->actorCtx.sceneFlags.collectible[0]); ImGui::EndGroup(); ImGui::BeginGroup(); ImGui::AlignTextToFramePadding(); ImGui::Text("collectible[1]"); ImGui::SameLine(110); if (UIWidgets::Button("All##collectible1", { .size = UIWidgets::Sizes::Inline, .color = UIWidgets::Colors::Gray })) { gPlayState->actorCtx.sceneFlags.collectible[1] = UINT32_MAX; } ImGui::SameLine(); if (UIWidgets::Button("Clear##collectible1", { .size = UIWidgets::Sizes::Inline, .color = UIWidgets::Colors::Red })) { gPlayState->actorCtx.sceneFlags.collectible[1] = 0; } UIWidgets::DrawFlagArray32("##collectible1", gPlayState->actorCtx.sceneFlags.collectible[1]); ImGui::EndGroup(); ImGui::SameLine(0, 10.0f); ImGui::BeginGroup(); ImGui::AlignTextToFramePadding(); ImGui::Text("collectible[2]"); ImGui::SameLine(110); if (UIWidgets::Button("All##collectible2", { .size = UIWidgets::Sizes::Inline, .color = UIWidgets::Colors::Gray })) { gPlayState->actorCtx.sceneFlags.collectible[2] = UINT32_MAX; } ImGui::SameLine(); if (UIWidgets::Button("Clear##collectible2", { .size = UIWidgets::Sizes::Inline, .color = UIWidgets::Colors::Red })) { gPlayState->actorCtx.sceneFlags.collectible[2] = 0; } UIWidgets::DrawFlagArray32("##collectible2", gPlayState->actorCtx.sceneFlags.collectible[2]); ImGui::EndGroup(); ImGui::BeginGroup(); ImGui::AlignTextToFramePadding(); ImGui::Text("collectible[3]"); ImGui::SameLine(110); if (UIWidgets::Button("All##collectible3", { .size = UIWidgets::Sizes::Inline, .color = UIWidgets::Colors::Gray })) { gPlayState->actorCtx.sceneFlags.collectible[3] = UINT32_MAX; } ImGui::SameLine(); if (UIWidgets::Button("Clear##collectible3", { .size = UIWidgets::Sizes::Inline, .color = UIWidgets::Colors::Red })) { gPlayState->actorCtx.sceneFlags.collectible[3] = 0; } UIWidgets::DrawFlagArray32("##collectible3", gPlayState->actorCtx.sceneFlags.collectible[3]); ImGui::EndGroup(); break; case WEEK_EVENT_REG: for (int i = 0; i < 100; i++) { ImGui::PushID(i); ImGui::Text("%02d", i); ImGui::SameLine(); UIWidgets::DrawFlagTableArray8Mask(flagTables.at(WEEK_EVENT_REG), i, gSaveContext.save.saveInfo.weekEventReg[i]); ImGui::PopID(); } break; case EVENT_INF: for (int i = 0; i < 8; i++) { ImGui::PushID(i); ImGui::Text("%02d", i); ImGui::SameLine(); UIWidgets::DrawFlagTableArray8(flagTables.at(EVENT_INF), i, gSaveContext.eventInf[i]); ImGui::PopID(); } break; case SCENES_VISIBLE: if (UIWidgets::Button("All##scenesVisible", { .size = UIWidgets::Sizes::Inline, .color = UIWidgets::Colors::Gray })) { for (int i = 0; i < 7; i++) { gSaveContext.save.saveInfo.scenesVisible[i] = UINT32_MAX; } } ImGui::SameLine(); if (UIWidgets::Button("Clear##scenesVisible", { .size = UIWidgets::Sizes::Inline, .color = UIWidgets::Colors::Red })) { for (int i = 0; i < 7; i++) { gSaveContext.save.saveInfo.scenesVisible[i] = 0; } } for (int i = 0; i < 7; i++) { ImGui::PushID(i); UIWidgets::DrawFlagArray32("##", gSaveContext.save.saveInfo.scenesVisible[i]); ImGui::PopID(); } break; case OWL_ACTIVATION: if (UIWidgets::Button("All##owlActivationFlags", { .size = UIWidgets::Sizes::Inline, .color = UIWidgets::Colors::Gray })) { gSaveContext.save.saveInfo.playerData.owlActivationFlags = UINT16_MAX; } ImGui::SameLine(); if (UIWidgets::Button("Clear##owlActivationFlags", { .size = UIWidgets::Sizes::Inline, .color = UIWidgets::Colors::Red })) { gSaveContext.save.saveInfo.playerData.owlActivationFlags = 0; } UIWidgets::DrawFlagTableArray16(flagTables.at(OWL_ACTIVATION), gSaveContext.save.saveInfo.playerData.owlActivationFlags); break; case PERMANENT_SCENE_FLAGS: ImGui::BeginGroup(); ImGui::AlignTextToFramePadding(); ImGui::Text("chest"); ImGui::SameLine(110); if (UIWidgets::Button("All##chest", { .size = UIWidgets::Sizes::Inline, .color = UIWidgets::Colors::Gray })) { gSaveContext.save.saveInfo.permanentSceneFlags[selectedScene].chest = UINT32_MAX; } ImGui::SameLine(); if (UIWidgets::Button("Clear##chest", { .size = UIWidgets::Sizes::Inline, .color = UIWidgets::Colors::Red })) { gSaveContext.save.saveInfo.permanentSceneFlags[selectedScene].chest = 0; } UIWidgets::DrawFlagArray32("##chest", gSaveContext.save.saveInfo.permanentSceneFlags[selectedScene].chest); ImGui::EndGroup(); ImGui::SameLine(0, 10.0f); ImGui::BeginGroup(); ImGui::AlignTextToFramePadding(); ImGui::Text("switch0"); ImGui::SameLine(110); if (UIWidgets::Button("All##switch0", { .size = UIWidgets::Sizes::Inline, .color = UIWidgets::Colors::Gray })) { gSaveContext.save.saveInfo.permanentSceneFlags[selectedScene].switch0 = UINT32_MAX; } ImGui::SameLine(); if (UIWidgets::Button("Clear##switch0", { .size = UIWidgets::Sizes::Inline, .color = UIWidgets::Colors::Red })) { gSaveContext.save.saveInfo.permanentSceneFlags[selectedScene].switch0 = 0; } UIWidgets::DrawFlagArray32("##switch0", gSaveContext.save.saveInfo.permanentSceneFlags[selectedScene].switch0); ImGui::EndGroup(); ImGui::BeginGroup(); ImGui::AlignTextToFramePadding(); ImGui::Text("collectible"); ImGui::SameLine(110); if (UIWidgets::Button("All##collectible", { .size = UIWidgets::Sizes::Inline, .color = UIWidgets::Colors::Gray })) { gSaveContext.save.saveInfo.permanentSceneFlags[selectedScene].collectible = UINT32_MAX; } ImGui::SameLine(); if (UIWidgets::Button("Clear##collectible", { .size = UIWidgets::Sizes::Inline, .color = UIWidgets::Colors::Red })) { gSaveContext.save.saveInfo.permanentSceneFlags[selectedScene].collectible = 0; } UIWidgets::DrawFlagArray32("##collectible", gSaveContext.save.saveInfo.permanentSceneFlags[selectedScene].collectible); ImGui::EndGroup(); ImGui::SameLine(0, 10.0f); ImGui::BeginGroup(); ImGui::AlignTextToFramePadding(); ImGui::Text("switch1"); ImGui::SameLine(110); if (UIWidgets::Button("All##switch1", { .size = UIWidgets::Sizes::Inline, .color = UIWidgets::Colors::Gray })) { gSaveContext.save.saveInfo.permanentSceneFlags[selectedScene].switch1 = UINT32_MAX; } ImGui::SameLine(); if (UIWidgets::Button("Clear##switch1", { .size = UIWidgets::Sizes::Inline, .color = UIWidgets::Colors::Red })) { gSaveContext.save.saveInfo.permanentSceneFlags[selectedScene].switch1 = 0; } UIWidgets::DrawFlagArray32("##switch1", gSaveContext.save.saveInfo.permanentSceneFlags[selectedScene].switch1); ImGui::EndGroup(); ImGui::BeginGroup(); ImGui::AlignTextToFramePadding(); ImGui::Text("clearedRoom"); ImGui::SameLine(110); if (UIWidgets::Button("All##clearedRoom", { .size = UIWidgets::Sizes::Inline, .color = UIWidgets::Colors::Gray })) { gSaveContext.save.saveInfo.permanentSceneFlags[selectedScene].clearedRoom = UINT32_MAX; } ImGui::SameLine(); if (UIWidgets::Button("Clear##clearedRoom", { .size = UIWidgets::Sizes::Inline, .color = UIWidgets::Colors::Red })) { gSaveContext.save.saveInfo.permanentSceneFlags[selectedScene].clearedRoom = 0; } UIWidgets::DrawFlagArray32("##clearedRoom", gSaveContext.save.saveInfo.permanentSceneFlags[selectedScene].clearedRoom); ImGui::EndGroup(); ImGui::SameLine(0, 10.0f); ImGui::BeginGroup(); ImGui::AlignTextToFramePadding(); ImGui::Text("unk_14"); ImGui::SameLine(110); if (UIWidgets::Button("All##unk_14", { .size = UIWidgets::Sizes::Inline, .color = UIWidgets::Colors::Gray })) { gSaveContext.save.saveInfo.permanentSceneFlags[selectedScene].unk_14 = UINT32_MAX; } ImGui::SameLine(); if (UIWidgets::Button("Clear##unk_14", { .size = UIWidgets::Sizes::Inline, .color = UIWidgets::Colors::Red })) { gSaveContext.save.saveInfo.permanentSceneFlags[selectedScene].unk_14 = 0; } UIWidgets::DrawFlagArray32("##unk_14", gSaveContext.save.saveInfo.permanentSceneFlags[selectedScene].unk_14); ImGui::EndGroup(); ImGui::BeginGroup(); ImGui::AlignTextToFramePadding(); ImGui::Text("rooms"); ImGui::SameLine(110); if (UIWidgets::Button("All##rooms", { .size = UIWidgets::Sizes::Inline, .color = UIWidgets::Colors::Gray })) { gSaveContext.save.saveInfo.permanentSceneFlags[selectedScene].rooms = UINT32_MAX; } ImGui::SameLine(); if (UIWidgets::Button("Clear##rooms", { .size = UIWidgets::Sizes::Inline, .color = UIWidgets::Colors::Red })) { gSaveContext.save.saveInfo.permanentSceneFlags[selectedScene].rooms = 0; } UIWidgets::DrawFlagArray32("##rooms", gSaveContext.save.saveInfo.permanentSceneFlags[selectedScene].rooms); ImGui::EndGroup(); break; case CYCLE_SCENE_FLAGS: ImGui::BeginGroup(); ImGui::AlignTextToFramePadding(); ImGui::Text("chest"); ImGui::SameLine(110); if (UIWidgets::Button("All##chest", { .size = UIWidgets::Sizes::Inline, .color = UIWidgets::Colors::Gray })) { gSaveContext.cycleSceneFlags[selectedScene].chest = UINT32_MAX; } ImGui::SameLine(); if (UIWidgets::Button("Clear##chest", { .size = UIWidgets::Sizes::Inline, .color = UIWidgets::Colors::Red })) { gSaveContext.cycleSceneFlags[selectedScene].chest = 0; } UIWidgets::DrawFlagArray32("##chest", gSaveContext.cycleSceneFlags[selectedScene].chest); ImGui::EndGroup(); ImGui::SameLine(0, 10.0f); ImGui::BeginGroup(); ImGui::AlignTextToFramePadding(); ImGui::Text("switch0"); ImGui::SameLine(110); if (UIWidgets::Button("All##switch0", { .size = UIWidgets::Sizes::Inline, .color = UIWidgets::Colors::Gray })) { gSaveContext.cycleSceneFlags[selectedScene].switch0 = UINT32_MAX; } ImGui::SameLine(); if (UIWidgets::Button("Clear##switch0", { .size = UIWidgets::Sizes::Inline, .color = UIWidgets::Colors::Red })) { gSaveContext.cycleSceneFlags[selectedScene].switch0 = 0; } UIWidgets::DrawFlagArray32("##switch0", gSaveContext.cycleSceneFlags[selectedScene].switch0); ImGui::EndGroup(); ImGui::BeginGroup(); ImGui::AlignTextToFramePadding(); ImGui::Text("collectible"); ImGui::SameLine(110); if (UIWidgets::Button("All##collectible", { .size = UIWidgets::Sizes::Inline, .color = UIWidgets::Colors::Gray })) { gSaveContext.cycleSceneFlags[selectedScene].collectible = UINT32_MAX; } ImGui::SameLine(); if (UIWidgets::Button("Clear##collectible", { .size = UIWidgets::Sizes::Inline, .color = UIWidgets::Colors::Red })) { gSaveContext.cycleSceneFlags[selectedScene].collectible = 0; } UIWidgets::DrawFlagArray32("##collectible", gSaveContext.cycleSceneFlags[selectedScene].collectible); ImGui::EndGroup(); ImGui::SameLine(0, 10.0f); ImGui::BeginGroup(); ImGui::AlignTextToFramePadding(); ImGui::Text("switch1"); ImGui::SameLine(110); if (UIWidgets::Button("All##switch1", { .size = UIWidgets::Sizes::Inline, .color = UIWidgets::Colors::Gray })) { gSaveContext.cycleSceneFlags[selectedScene].switch1 = UINT32_MAX; } ImGui::SameLine(); if (UIWidgets::Button("Clear##switch1", { .size = UIWidgets::Sizes::Inline, .color = UIWidgets::Colors::Red })) { gSaveContext.cycleSceneFlags[selectedScene].switch1 = 0; } UIWidgets::DrawFlagArray32("##switch1", gSaveContext.cycleSceneFlags[selectedScene].switch1); ImGui::EndGroup(); ImGui::BeginGroup(); ImGui::AlignTextToFramePadding(); ImGui::Text("clearedRoom"); ImGui::SameLine(110); if (UIWidgets::Button("All##clearedRoom", { .size = UIWidgets::Sizes::Inline, .color = UIWidgets::Colors::Gray })) { gSaveContext.cycleSceneFlags[selectedScene].clearedRoom = UINT32_MAX; } ImGui::SameLine(); if (UIWidgets::Button("Clear##clearedRoom", { .size = UIWidgets::Sizes::Inline, .color = UIWidgets::Colors::Red })) { gSaveContext.cycleSceneFlags[selectedScene].clearedRoom = 0; } UIWidgets::DrawFlagArray32("##clearedRoom", gSaveContext.cycleSceneFlags[selectedScene].clearedRoom); ImGui::EndGroup(); break; } ImGui::PopStyleVar(); ImGui::EndChild(); ImGui::PopStyleVar(2); } void DrawRandoTab() { if (UIWidgets::Button("Generate Spoiler from Save", { .size = UIWidgets::Sizes::Inline })) { nlohmann::json spoiler = Rando::Spoiler::GenerateFromSaveContext(); std::string inputSeed = std::to_string(Ship_Random(0, 1000000)); spoiler["inputSeed"] = inputSeed; std::string fileName = inputSeed + ".json"; Rando::Spoiler::SaveToFile(fileName, spoiler); } static ImGuiTextFilter rcFilter; UIWidgets::PushStyleCombobox(); rcFilter.Draw("##filter", ImGui::GetContentRegionAvail().x); UIWidgets::PopStyleCombobox(); if (!rcFilter.IsActive()) { ImGui::SameLine(18.0f); ImGui::Text("Search"); } ImGui::BeginChild("RandoChild"); ImGui::PushStyleColor(ImGuiCol_Button, ImVec4(1.0f, 1.0f, 1.0f, 0.0f)); ImGui::PushStyleColor(ImGuiCol_ButtonHovered, ImVec4(1.0f, 1.0f, 1.0f, 0.2f)); ImGui::PushStyleColor(ImGuiCol_ButtonActive, ImVec4(1.0f, 1.0f, 1.0f, 0.1f)); ImGui::BeginTable("Check List", 5); ImGui::TableSetupColumn("Shuffled", ImGuiTableColumnFlags_NoHeaderLabel | ImGuiTableColumnFlags_WidthFixed, 30.0f); ImGui::TableSetupColumn("Eligible", ImGuiTableColumnFlags_NoHeaderLabel | ImGuiTableColumnFlags_WidthFixed, 30.0f); ImGui::TableSetupColumn("Obtained", ImGuiTableColumnFlags_NoHeaderLabel | ImGuiTableColumnFlags_WidthFixed, 30.0f); ImGui::TableSetupColumn("Check Name"); ImGui::TableSetupColumn("Reward"); ImGui::TableSetupScrollFreeze(5, 1); ImGui::TableHeadersRow(); for (auto& [_, randoStaticCheck] : Rando::StaticData::Checks) { RandoSaveCheck& randoSaveCheck = RANDO_SAVE_CHECKS[randoStaticCheck.randoCheckId]; if (!rcFilter.PassFilter(randoStaticCheck.name) && !rcFilter.PassFilter(Rando::StaticData::Items[randoSaveCheck.randoItemId].spoilerName)) { continue; } if (randoStaticCheck.randoCheckId == RC_UNKNOWN) { continue; } std::string hiddenName = "##"; hiddenName += randoStaticCheck.name; ImGui::TableNextColumn(); UIWidgets::Checkbox((hiddenName + "shuffled").c_str(), &randoSaveCheck.shuffled); UIWidgets::Tooltip("Shuffled"); ImGui::TableNextColumn(); UIWidgets::Checkbox((hiddenName + "eligible").c_str(), &randoSaveCheck.eligible); UIWidgets::Tooltip("Eligible"); ImGui::TableNextColumn(); UIWidgets::Checkbox((hiddenName + "obtained").c_str(), &randoSaveCheck.obtained); UIWidgets::Tooltip("Obtained"); ImGui::TableNextColumn(); ImGui::TextColored(randoSaveCheck.obtained ? UIWidgets::ColorValues.at(UIWidgets::Colors::Green) : UIWidgets::ColorValues.at(UIWidgets::Colors::White), randoStaticCheck.name); ImGui::TableNextColumn(); UIWidgets::ComboboxWithSearch((hiddenName + "reward").c_str(), &randoSaveCheck.randoItemId, &randoItemIdComboboxMap, { .labelPosition = UIWidgets::LabelPosition::None }); } ImGui::EndTable(); ImGui::PopStyleColor(3); ImGui::EndChild(); } void SaveEditorWindow::DrawElement() { UIWidgets::PushStyleTabs(UIWidgets::Colors(CVarGetInteger("gSettings.Menu.Theme", 5))); if (ImGui::BeginTabBar("SaveContextTabBar", ImGuiTabBarFlags_NoCloseWithMiddleMouseButton)) { if (ImGui::BeginTabItem("General")) { DrawGeneralTab(); ImGui::EndTabItem(); } if (ImGui::BeginTabItem("Items & Masks")) { DrawItemsAndMasksTab(); ImGui::EndTabItem(); } if (ImGui::BeginTabItem("Quest Status")) { DrawQuestStatusTab(); ImGui::EndTabItem(); } if (ImGui::BeginTabItem("Dungeon Items")) { DrawDungeonItemTab(); ImGui::EndTabItem(); } if (ImGui::BeginTabItem("Reg Editor")) { DrawRegEditorTab(); ImGui::EndTabItem(); } // if (ImGui::BeginTabItem("Equipment")) { // DrawEquipmentTab(); // ImGui::EndTabItem(); // } if (ImGui::BeginTabItem("Flags")) { DrawFlagsTab(); ImGui::EndTabItem(); } if (ImGui::BeginTabItem("Player")) { DrawPlayerTab(); ImGui::EndTabItem(); } if (IS_RANDO) { if (ImGui::BeginTabItem("Rando")) { DrawRandoTab(); ImGui::EndTabItem(); } } ImGui::EndTabBar(); } UIWidgets::PopStyleTabs(); } void SaveEditorWindow::InitElement() { initSafeItemsForInventorySlot(); randoItemIdComboboxMap.clear(); for (auto& [_, randoItem] : Rando::StaticData::Items) { randoItemIdComboboxMap[randoItem.randoItemId] = randoItem.spoilerName; } }