Save editor v2 (#6474)

This commit is contained in:
PurpleHato
2026-08-09 04:31:11 +02:00
committed by GitHub
parent af485bf64c
commit 426b37ddf0
7 changed files with 1984 additions and 968 deletions
+1
View File
@@ -1533,6 +1533,7 @@ void KaleidoScopeCall_Init(PlayState* play);
void KaleidoScopeCall_Destroy(PlayState* play);
void KaleidoScopeCall_Update(PlayState* play);
void KaleidoScopeCall_Draw(PlayState* play);
void KaleidoScope_InitVertices(PlayState* play, GraphicsContext* gfxCtx);
void Play_SetViewpoint(PlayState* play, s16 viewpoint);
s32 Play_CheckViewpoint(PlayState* play, s16 viewpoint);
void Play_SetShopBrowsingViewpoint(PlayState* play);
File diff suppressed because it is too large Load Diff
@@ -19,7 +19,9 @@ static uint8_t groundTimer = 0;
static f32 effectsScale = 1.0f;
void RegisterRocsFeather() {
bool shouldRegister = IS_RANDO && RAND_GET_OPTION(RSK_ROCS_FEATHER);
// Always register hooks - they only activate when ITEM_ROCS_FEATHER is actually used
// Rando-specific logic (item pool, accessibility, etc.) is handled elsewhere
bool shouldRegister = true;
COND_HOOK(OnPlayerUpdate, shouldRegister, []() {
Player* player = GET_PLAYER(gPlayState);
@@ -99,4 +101,4 @@ void RegisterRocsFeather() {
});
}
static RegisterShipInitFunc registerRocsFeather(RegisterRocsFeather, { "IS_RANDO" });
static RegisterShipInitFunc registerRocsFeather(RegisterRocsFeather);
@@ -989,6 +989,14 @@ void DrawItem(ItemTrackerItem item) {
case ITEM_NAYRUS_LOVE:
if (IS_RANDO && OTRGlobals::Instance->gRandomizer->GetRandoSettingValue(RSK_ROCS_FEATHER)) {
hasItem = Flags_GetRandomizerInf(RAND_INF_OBTAINED_NAYRUS_LOVE);
} else if (!IS_RANDO) {
// In non-rando, check if player has Roc's Feather in inventory
for (int i = 0; i < 24; i++) {
if (gSaveContext.inventory.items[i] == ITEM_ROCS_FEATHER) {
hasItem = true;
break;
}
}
}
break;
case RG_ROCS_FEATHER:
+193
View File
@@ -1075,6 +1075,12 @@ void DrawFlagArray32(const std::string& name, uint32_t& flags, Colors color) {
}
ImGui::PopStyleVar();
PopStyleCheckbox();
// Show tooltip with bit number
if (ImGui::IsItemHovered()) {
ImGui::BeginTooltip();
ImGui::Text("%s Bit %d", name.c_str(), flagIndex);
ImGui::EndTooltip();
}
ImGui::PopID();
}
ImGui::PopID();
@@ -1101,6 +1107,12 @@ void DrawFlagArray16(const std::string& name, uint16_t& flags, Colors color) {
}
ImGui::PopStyleVar();
PopStyleCheckbox();
// Show tooltip with bit number
if (ImGui::IsItemHovered()) {
ImGui::BeginTooltip();
ImGui::Text("%s Bit %d", name.c_str(), flagIndex);
ImGui::EndTooltip();
}
ImGui::PopID();
}
ImGui::PopID();
@@ -1127,6 +1139,12 @@ void DrawFlagArray8(const std::string& name, uint8_t& flags, Colors color) {
}
ImGui::PopStyleVar();
PopStyleCheckbox();
// Show tooltip with bit number
if (ImGui::IsItemHovered()) {
ImGui::BeginTooltip();
ImGui::Text("%s Bit %d", name.c_str(), flagIndex);
ImGui::EndTooltip();
}
ImGui::PopID();
}
ImGui::PopID();
@@ -1153,6 +1171,12 @@ void DrawFlagArray8Mask(const std::string& name, uint8_t& flags, Colors color) {
}
ImGui::PopStyleVar();
PopStyleCheckbox();
// Show tooltip with bit number
if (ImGui::IsItemHovered()) {
ImGui::BeginTooltip();
ImGui::Text("%s Bit %d", name.c_str(), flagIndex);
ImGui::EndTooltip();
}
ImGui::PopID();
}
ImGui::PopID();
@@ -1255,6 +1279,175 @@ bool CVarBtnSelector(const char* label, const char* cvarName, const BtnSelectorO
return dirty;
}
// Internal state stored per layout instance
struct CardLayoutState {
std::vector<float> columnWidths;
std::vector<float> columnHeights;
std::vector<float> columnXPositions;
int currentCardColumn;
float startY;
int columnsPerRow;
float spacing;
bool autoItemWidth;
ImGuiChildFlags childFlags;
bool syncLastColumnToMax;
};
static CardLayoutState* gCurrentCardLayout = nullptr;
void BeginCardLayout(const CardLayoutOptions& options) {
CardLayoutState* state = new CardLayoutState();
float availWidth = ImGui::GetContentRegionAvail().x;
int columnsPerRow = ImClamp(options.columnsPerRow, 1, options.columnsPerRow);
if (options.minColumnWidth > 0.0f) {
float denom = options.minColumnWidth + options.spacing;
if (denom > 0.0f) {
int widthLimitedColumns = static_cast<int>(ImFloor((availWidth + options.spacing) / denom));
columnsPerRow = ImClamp(widthLimitedColumns, 1, options.columnsPerRow);
}
}
columnsPerRow = ImMax(columnsPerRow, 1);
// Per-column fixed widths calculation
std::vector<float> columnWidths;
columnWidths.resize(columnsPerRow);
float totalFixedWidth = 0.0f;
int autoSizedColumns = 0;
// First pass: calculate fixed widths and count auto-sized columns
for (int i = 0; i < columnsPerRow; i++) {
if (i < options.fixedColumnWidths.size() && options.fixedColumnWidths[i] > 0.0f) {
columnWidths[i] = options.fixedColumnWidths[i];
totalFixedWidth += columnWidths[i];
} else {
autoSizedColumns++;
}
}
// Calculate spacing: (columns - 1) * spacing
float totalSpacing = options.spacing * (columnsPerRow - 1);
float remainingWidth = availWidth - totalFixedWidth - totalSpacing;
// Distribute remaining width to auto-sized columns
float autoWidth = (autoSizedColumns > 0) ? (remainingWidth / static_cast<float>(autoSizedColumns)) : 0.0f;
// Second pass: assign auto-width to columns that need it
for (int i = 0; i < columnsPerRow; i++) {
if (i >= options.fixedColumnWidths.size() || options.fixedColumnWidths[i] <= 0.0f) {
columnWidths[i] = autoWidth;
}
}
// Initialize column widths with individual values
state->columnWidths = columnWidths;
state->columnHeights.resize(columnsPerRow, 0.0f);
state->columnXPositions.resize(columnsPerRow);
// Calculate X positions for each column (accounting for varying widths)
float currentX = ImGui::GetCursorPosX();
for (int i = 0; i < columnsPerRow; i++) {
state->columnXPositions[i] = currentX;
currentX += columnWidths[i] + options.spacing;
}
state->startY = ImGui::GetCursorPosY();
state->currentCardColumn = 0;
state->columnsPerRow = columnsPerRow;
state->spacing = options.spacing;
state->autoItemWidth = options.autoItemWidth;
state->childFlags = options.childFlags;
state->syncLastColumnToMax = options.syncLastColumnToMax;
gCurrentCardLayout = state;
}
void BeginCard(const char* id, int32_t forceColumn) {
CardLayoutState* state = gCurrentCardLayout;
if (!state)
return;
int targetCol;
if (forceColumn >= 0 && forceColumn < state->columnsPerRow) {
// Force to specific column
targetCol = forceColumn;
} else {
// Find shortest column
int shortestCol = 0;
float shortestHeight = state->columnHeights[0];
for (int i = 1; i < state->columnsPerRow; i++) {
if (state->columnHeights[i] < shortestHeight) {
shortestHeight = state->columnHeights[i];
shortestCol = i;
}
}
targetCol = shortestCol;
}
state->currentCardColumn = targetCol;
// Position cursor at this column's current height
ImGui::SetCursorPosX(state->columnXPositions[state->currentCardColumn]);
ImGui::SetCursorPosY(state->startY + state->columnHeights[state->currentCardColumn]);
ImGui::BeginChild(id, ImVec2(state->columnWidths[state->currentCardColumn], 0), state->childFlags);
// Auto-push item width to fill card
if (state->autoItemWidth) {
ImGui::PushItemWidth(-FLT_MIN);
}
}
void EndCard() {
CardLayoutState* state = gCurrentCardLayout;
if (!state)
return;
// Auto-pop item width
if (state->autoItemWidth) {
ImGui::PopItemWidth();
}
ImGui::EndChild();
// Get the height of the card we just rendered
ImVec2 itemSize = ImGui::GetItemRectSize();
// Update this column's height (add card height + spacing)
state->columnHeights[state->currentCardColumn] += itemSize.y + state->spacing;
// Sync last column to max height of other columns (keeps it empty)
if (state->syncLastColumnToMax && state->columnHeights.size() >= 2) {
int lastCol = (int)state->columnHeights.size() - 1;
float maxOtherHeight = 0.0f;
for (int i = 0; i < lastCol; i++) {
maxOtherHeight = ImMax(maxOtherHeight, state->columnHeights[i]);
}
state->columnHeights[lastCol] = maxOtherHeight;
}
}
void EndCardLayout() {
if (!gCurrentCardLayout) {
return;
}
CardLayoutState* state = gCurrentCardLayout;
float maxHeight = 0.0f;
for (float height : state->columnHeights) {
maxHeight = ImMax(maxHeight, height);
}
if (maxHeight > 0.0f) {
maxHeight -= state->spacing;
ImGui::SetCursorPosY(state->startY + maxHeight);
ImGui::Dummy(ImVec2(0.0f, 0.0f));
}
delete state;
gCurrentCardLayout = nullptr;
}
} // namespace UIWidgets
ImVec4 GetRandomValue(uint64_t* state) {
+24
View File
@@ -508,6 +508,30 @@ void DrawFlagArray8Mask(const std::string& name, uint8_t& flags, Colors color =
bool BtnSelector(const char* label, int32_t* value, const BtnSelectorOptions& options);
bool CVarBtnSelector(const char* label, const char* cvarName, const BtnSelectorOptions& options);
// Card Layout System - creates a responsive grid of card containers
// Example usage:
// BeginCardLayout({ .columnsPerRow = 2 });
// BeginCard("cardId");
// // ... card content ...
// EndCard();
// EndCardLayout();
struct CardLayoutOptions {
int32_t columnsPerRow = 2;
float spacing = 8.0f;
float minColumnWidth = 0.0f;
bool autoItemWidth = true;
ImGuiChildFlags childFlags = ImGuiChildFlags_Border | ImGuiChildFlags_AutoResizeY;
bool syncLastColumnToMax = false; // Keep last column height synced to max of others (keeps it empty)
// Per-column fixed widths (0 = auto-size). Example: { 400.0f, 0.0f, 300.0f }
// means column 0 is 400px, column 1 auto-sizes, column 2 is 300px
std::vector<float> fixedColumnWidths;
};
void BeginCardLayout(const CardLayoutOptions& options = {});
void BeginCard(const char* id, int32_t forceColumn = -1); // -1 = auto (shortest column), 0+ = force to column
void EndCard();
void EndCardLayout();
void InsertHelpHoverText(const std::string& text);
void InsertHelpHoverText(const char* text);
} // namespace UIWidgets
+6
View File
@@ -282,6 +282,8 @@ std::vector<std::string> itemNamesEng = {
"Deku Stick Upgrade (30)",
"Deku Nut Upgrade (30)",
"Deku Nut Upgrade (40)",
"[Removed]", // ITEM_CUSTOM
"Roc's Feather",
};
std::vector<std::string> itemNamesFra = {
@@ -441,6 +443,8 @@ std::vector<std::string> itemNamesFra = {
"Amélioration des Bâtons Mojo (30)",
"Amélioration des Noix Mojo (30)",
"Amélioration des Noix Mojo (40)",
"[Retiré]", // ITEM_CUSTOM
"Plume de Roc",
};
std::vector<std::string> itemNamesGer = {
@@ -600,6 +604,8 @@ std::vector<std::string> itemNamesGer = {
"Deku-Stab-Kapazität (30)",
"Deku-Nuß-Kapazität (30)",
"Deku-Nuß-Kapazität (40)",
"[Entfernt]", // ITEM_CUSTOM
"Greifenfeder",
};
std::vector<std::string> questItemNamesEng = {