Track keys, heart pieces, and heart containers collected (#1849)

* Track keys, heart pieces, and heart containers collected

* Update soh/soh/Enhancements/randomizer/randomizer_item_tracker.cpp
This commit is contained in:
Garrett Cox 2022-11-02 13:09:25 -05:00 committed by GitHub
parent 897e70b6fc
commit 3b7b4913eb
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 144 additions and 28 deletions

View File

@ -25,6 +25,12 @@ typedef struct {
/* 0x5C */ s16 gsTokens;
} Inventory; // size = 0x5E
typedef struct {
/* */ u8 heartPieces;
/* */ u8 heartContainers;
/* */ u8 dungeonKeys[19];
} SohStats;
typedef struct {
/* 0x00 */ u32 chest;
/* 0x04 */ u32 swch;
@ -189,6 +195,7 @@ typedef struct {
u16 adultTradeItems;
u8 pendingIceTrapCount;
u8 mqDungeonCount;
SohStats sohStats;
} SaveContext; // size = 0x1428
typedef enum {

View File

@ -14,6 +14,8 @@ typedef enum {
TEXT_ALTAR_ADULT = 0x7088,
TEXT_GANONDORF = 0x70CC,
TEXT_GANONDORF_NOHINT = 0x70CD,
TEXT_HEART_CONTAINER = 0xC6,
TEXT_HEART_PIECE = 0xC2,
TEXT_BLUE_RUPEE = 0xCC,
TEXT_RED_RUPEE = 0xF0,
TEXT_PURPLE_RUPEE = 0xF1,

View File

@ -134,6 +134,7 @@ std::map<uint32_t, ItemMapEntry> itemMapping = {
ITEM_MAP_ENTRY(ITEM_DUNGEON_MAP),
ITEM_MAP_ENTRY(ITEM_KEY_SMALL),
ITEM_MAP_ENTRY(ITEM_HEART_CONTAINER),
ITEM_MAP_ENTRY(ITEM_HEART_PIECE),
ITEM_MAP_ENTRY(ITEM_MAGIC_SMALL),
ITEM_MAP_ENTRY(ITEM_MAGIC_LARGE)
};
@ -1302,7 +1303,9 @@ void DrawQuestStatusTab() {
float lineHeight = ImGui::GetTextLineHeightWithSpacing();
ImGui::Image(SohImGui::GetTextureByName(itemMapping[ITEM_KEY_SMALL].name), ImVec2(lineHeight, lineHeight));
ImGui::SameLine();
ImGui::InputScalar("##Keys", ImGuiDataType_S8, gSaveContext.inventory.dungeonKeys + dungeonItemsScene);
if (ImGui::InputScalar("##Keys", ImGuiDataType_S8, gSaveContext.inventory.dungeonKeys + dungeonItemsScene)) {
gSaveContext.sohStats.dungeonKeys[dungeonItemsScene] = gSaveContext.inventory.dungeonKeys[dungeonItemsScene];
};
} else {
// dungeonItems is size 20 but dungeonKeys is size 19, so there are no keys for the last scene (Barinade's Lair)
ImGui::Text("Barinade's Lair does not have small keys");

View File

@ -55,8 +55,8 @@ std::vector<ItemTrackerItem> equipmentItems = {
std::vector<ItemTrackerItem> miscItems = {
ITEM_TRACKER_ITEM(ITEM_BRACELET, 0, DrawItem), ITEM_TRACKER_ITEM(ITEM_SCALE_SILVER, 0, DrawItem), ITEM_TRACKER_ITEM(ITEM_WALLET_ADULT, 0, DrawItem),
ITEM_TRACKER_ITEM(ITEM_HEART_CONTAINER, 0, DrawItem), ITEM_TRACKER_ITEM(ITEM_MAGIC_SMALL, 0, DrawItem), ITEM_TRACKER_ITEM(QUEST_STONE_OF_AGONY, 1 << 21, DrawQuest),
ITEM_TRACKER_ITEM(QUEST_GERUDO_CARD, 1 << 22, DrawQuest), ITEM_TRACKER_ITEM(QUEST_SKULL_TOKEN, 1 << 23, DrawQuest),
ITEM_TRACKER_ITEM(ITEM_HEART_CONTAINER, 0, DrawItem), ITEM_TRACKER_ITEM(ITEM_HEART_PIECE, 0, DrawItem), ITEM_TRACKER_ITEM(ITEM_MAGIC_SMALL, 0, DrawItem),
ITEM_TRACKER_ITEM(QUEST_GERUDO_CARD, 1 << 22, DrawQuest), ITEM_TRACKER_ITEM(QUEST_SKULL_TOKEN, 1 << 23, DrawQuest), ITEM_TRACKER_ITEM(QUEST_STONE_OF_AGONY, 1 << 21, DrawQuest),
};
std::vector<ItemTrackerItem> dungeonRewardStones = {
@ -297,35 +297,46 @@ ItemTrackerNumbers GetItemCurrentAndMax(ItemTrackerItem item) {
result.maxCapacity = result.currentCapacity = 100;
result.currentAmmo = gSaveContext.inventory.gsTokens;
break;
case ITEM_HEART_CONTAINER:
result.maxCapacity = result.currentCapacity = 8;
result.currentAmmo = gSaveContext.sohStats.heartContainers;
break;
case ITEM_HEART_PIECE:
result.maxCapacity = result.currentCapacity = 36;
result.currentAmmo = gSaveContext.sohStats.heartPieces;
break;
case ITEM_KEY_SMALL:
// Though the ammo/capacity naming doesn't really make sense for keys, we are
// hijacking the same system to display key counts as there are enough similarities
result.currentAmmo = MAX(gSaveContext.inventory.dungeonKeys[item.data], 0);
result.currentCapacity = gSaveContext.sohStats.dungeonKeys[item.data];
switch (item.data) {
case SCENE_BMORI1:
result.maxCapacity = result.currentCapacity = FOREST_TEMPLE_SMALL_KEY_MAX;
result.maxCapacity = FOREST_TEMPLE_SMALL_KEY_MAX;
break;
case SCENE_HIDAN:
result.maxCapacity = result.currentCapacity = FIRE_TEMPLE_SMALL_KEY_MAX;
result.maxCapacity = FIRE_TEMPLE_SMALL_KEY_MAX;
break;
case SCENE_MIZUSIN:
result.maxCapacity = result.currentCapacity = WATER_TEMPLE_SMALL_KEY_MAX;
result.maxCapacity = WATER_TEMPLE_SMALL_KEY_MAX;
break;
case SCENE_JYASINZOU:
result.maxCapacity = result.currentCapacity = SPIRIT_TEMPLE_SMALL_KEY_MAX;
result.maxCapacity = SPIRIT_TEMPLE_SMALL_KEY_MAX;
break;
case SCENE_HAKADAN:
result.maxCapacity = result.currentCapacity = SHADOW_TEMPLE_SMALL_KEY_MAX;
result.maxCapacity = SHADOW_TEMPLE_SMALL_KEY_MAX;
break;
case SCENE_HAKADANCH:
result.maxCapacity = result.currentCapacity = BOTTOM_OF_THE_WELL_SMALL_KEY_MAX;
result.maxCapacity = BOTTOM_OF_THE_WELL_SMALL_KEY_MAX;
break;
case SCENE_MEN:
result.maxCapacity = result.currentCapacity = GERUDO_TRAINING_GROUNDS_SMALL_KEY_MAX;
result.maxCapacity = GERUDO_TRAINING_GROUNDS_SMALL_KEY_MAX;
break;
case SCENE_GERUDOWAY:
result.maxCapacity = result.currentCapacity = GERUDO_FORTRESS_SMALL_KEY_MAX;
result.maxCapacity = GERUDO_FORTRESS_SMALL_KEY_MAX;
break;
case SCENE_GANONTIKA:
result.maxCapacity = result.currentCapacity = GANONS_CASTLE_SMALL_KEY_MAX;
result.maxCapacity = GANONS_CASTLE_SMALL_KEY_MAX;
break;
}
break;
@ -345,8 +356,32 @@ void DrawItemCount(ItemTrackerItem item) {
ItemTrackerNumbers currentAndMax = GetItemCurrentAndMax(item);
ImVec2 p = ImGui::GetCursorScreenPos();
int32_t trackerNumberDisplayMode = CVar_GetS32("gItemTrackerCapacityTrack", 1);
int32_t trackerKeyNumberDisplayMode = CVar_GetS32("gItemTrackerKeyTrack", 0);
if (currentAndMax.currentCapacity > 0 && trackerNumberDisplayMode != ITEM_TRACKER_NUMBER_NONE && IsValidSaveFile()) {
if (item.id == ITEM_KEY_SMALL && IsValidSaveFile()) {
std::string currentString = "";
std::string maxString = std::to_string(currentAndMax.maxCapacity);
ImU32 currentColor = IM_COL_WHITE;
ImU32 maxColor = IM_COL_GREEN;
// "Collected / Max", "Current / Collected / Max", "Current / Max"
if (trackerKeyNumberDisplayMode == 1 || trackerKeyNumberDisplayMode == 2) {
currentString+= std::to_string(currentAndMax.currentAmmo);
currentString+= "/";
}
if (trackerKeyNumberDisplayMode == 0 || trackerKeyNumberDisplayMode == 1) {
currentString+= std::to_string(currentAndMax.currentCapacity);
currentString+= "/";
}
ImGui::SetCursorScreenPos(ImVec2(p.x + (iconSize / 2) - (ImGui::CalcTextSize((currentString + maxString).c_str()).x / 2), p.y - 14));
ImGui::PushStyleColor(ImGuiCol_Text, currentColor);
ImGui::Text(currentString.c_str());
ImGui::PopStyleColor();
ImGui::SameLine(0, 0.0f);
ImGui::PushStyleColor(ImGuiCol_Text, maxColor);
ImGui::Text(maxString.c_str());
ImGui::PopStyleColor();
} else if (currentAndMax.currentCapacity > 0 && trackerNumberDisplayMode != ITEM_TRACKER_NUMBER_NONE && IsValidSaveFile()) {
std::string currentString = "";
std::string maxString = "";
ImU32 currentColor = IM_COL_WHITE;
@ -362,7 +397,8 @@ void DrawItemCount(ItemTrackerItem item) {
item.id == ITEM_BOMBCHU ||
item.id == ITEM_BEAN ||
item.id == QUEST_SKULL_TOKEN ||
item.id == ITEM_KEY_SMALL;
item.id == ITEM_HEART_CONTAINER ||
item.id == ITEM_HEART_PIECE;
bool shouldDisplayMax = !(trackerNumberDisplayMode == ITEM_TRACKER_NUMBER_CURRENT_CAPACITY_ONLY || trackerNumberDisplayMode == ITEM_TRACKER_NUMBER_CURRENT_AMMO_ONLY);
@ -445,7 +481,11 @@ void DrawItem(ItemTrackerItem item) {
switch (item.id) {
case ITEM_HEART_CONTAINER:
actualItemId = item.id;
hasItem = !!gSaveContext.doubleDefense;
hasItem = gSaveContext.sohStats.heartContainers > 0;
break;
case ITEM_HEART_PIECE:
actualItemId = item.id;
hasItem = gSaveContext.sohStats.heartPieces > 0;
break;
case ITEM_MAGIC_SMALL:
case ITEM_MAGIC_LARGE:
@ -793,9 +833,6 @@ void UpdateVectors() {
mainWindowItems.insert(mainWindowItems.end(), miscItems.begin(), miscItems.end());
}
if (CVar_GetS32("gItemTrackerDungeonRewardsDisplayType", 1) == 1) {
if (CVar_GetS32("gItemTrackerMiscItemsDisplayType", 1) == 1) {
mainWindowItems.push_back(ITEM_TRACKER_ITEM(ITEM_NONE, 0, DrawItem));
}
mainWindowItems.insert(mainWindowItems.end(), dungeonRewardStones.begin(), dungeonRewardStones.end());
mainWindowItems.insert(mainWindowItems.end(), dungeonRewardMedallions.begin(), dungeonRewardMedallions.end());
}
@ -804,7 +841,6 @@ void UpdateVectors() {
mainWindowItems.push_back(ITEM_TRACKER_ITEM(ITEM_NONE, 0, DrawItem));
mainWindowItems.push_back(ITEM_TRACKER_ITEM(ITEM_NONE, 0, DrawItem));
mainWindowItems.push_back(ITEM_TRACKER_ITEM(ITEM_NONE, 0, DrawItem));
mainWindowItems.push_back(ITEM_TRACKER_ITEM(ITEM_NONE, 0, DrawItem));
}
mainWindowItems.insert(mainWindowItems.end(), songItems.begin(), songItems.end());
}
@ -910,6 +946,7 @@ void DrawItemTracker(bool& open) {
}
const char* itemTrackerCapacityTrackOptions[5] = { "No Numbers", "Current Capacity", "Current Ammo", "Current Capacity / Max Capacity", "Current Ammo / Current Capacity" };
const char* itemTrackerKeyTrackOptions[3] = { "Collected / Max", "Current / Collected / Max", "Current / Max" };
void DrawItemTrackerOptions(bool& open) {
if (!open) {
@ -965,6 +1002,9 @@ void DrawItemTrackerOptions(bool& open) {
if (CVar_GetS32("gItemTrackerCapacityTrack", 1) == ITEM_TRACKER_NUMBER_CURRENT_CAPACITY_ONLY || CVar_GetS32("gItemTrackerCapacityTrack", 1) == ITEM_TRACKER_NUMBER_CURRENT_AMMO_ONLY) {
PaddedEnhancementCheckbox("Align count to left side", "gItemTrackerCurrentOnLeft", 0);
}
ImGui::Text("Key Count Tracking");
UIWidgets::EnhancementCombobox("gItemTrackerKeyTrack", itemTrackerKeyTrackOptions, 3, 0);
UIWidgets::InsertHelpHoverText("Customize what numbers are shown for key tracking.");
ImGui::TableNextColumn();

View File

@ -254,8 +254,8 @@ namespace GameMenuBar {
CVar_SetS32("gGuardVision", 0);
// Enable passage of time on file select
CVar_SetS32("gTimeFlowFileSelect", 0);
// Count Golden Skulltulas
CVar_SetS32("gInjectSkulltulaCount", 0);
// Inject Item Counts in messages
CVar_SetS32("gInjectItemCounts", 0);
// Pull grave during the day
CVar_SetS32("gDayGravePull", 0);
// Pull out Ocarina to Summon Scarecrow
@ -353,8 +353,8 @@ namespace GameMenuBar {
CVar_SetS32("gAssignableTunicsAndBoots", 1);
// Enable passage of time on file select
CVar_SetS32("gTimeFlowFileSelect", 1);
// Count Golden Skulltulas
CVar_SetS32("gInjectSkulltulaCount", 1);
// Inject Item Counts in messages
CVar_SetS32("gInjectItemCounts", 1);
// Pause link animation (0 to 16)
CVar_SetS32("gPauseLiveLink", 1);
@ -998,8 +998,8 @@ namespace GameMenuBar {
UIWidgets::Tooltip("Allows the Lon Lon Ranch obstacle course reward to be shared across time periods");
UIWidgets::PaddedEnhancementCheckbox("Enable visible guard vision", "gGuardVision", true, false);
UIWidgets::PaddedEnhancementCheckbox("Enable passage of time on file select", "gTimeFlowFileSelect", true, false);
UIWidgets::PaddedEnhancementCheckbox("Count Golden Skulltulas", "gInjectSkulltulaCount", true, false);
UIWidgets::Tooltip("Injects Golden Skulltula total count in pickup messages");
UIWidgets::PaddedEnhancementCheckbox("Item counts in messages", "gInjectItemCounts", true, false);
UIWidgets::Tooltip("Injects item counts in pickup messages, like golden skulltula tokens and heart pieces");
UIWidgets::PaddedEnhancementCheckbox("Pull grave during the day", "gDayGravePull", true, false);
UIWidgets::Tooltip("Allows graves to be pulled when child during the day");

View File

@ -1986,7 +1986,7 @@ extern "C" int CustomMessage_RetrieveIfExists(GlobalContext* globalCtx) {
}
}
if (textId == TEXT_GS_NO_FREEZE || textId == TEXT_GS_FREEZE) {
if (CVar_GetS32("gInjectSkulltulaCount", 0) != 0) {
if (CVar_GetS32("gInjectItemCounts", 0) != 0) {
// The freeze text cannot be manually dismissed and must be auto-dismissed.
// This is fine and even wanted when skull tokens are not shuffled, but when
// when they are shuffled we don't want to be able to manually dismiss the box.
@ -2004,6 +2004,14 @@ extern "C" int CustomMessage_RetrieveIfExists(GlobalContext* globalCtx) {
CustomMessageManager::ReplaceStringInMessage(messageEntry, "{{gsCount}}", std::to_string(gSaveContext.inventory.gsTokens + 1));
}
}
if (textId == TEXT_HEART_CONTAINER && CVar_GetS32("gInjectItemCounts", 0)) {
messageEntry = CustomMessageManager::Instance->RetrieveMessage(customMessageTableID, TEXT_HEART_CONTAINER);
CustomMessageManager::ReplaceStringInMessage(messageEntry, "{{heartContainerCount}}", std::to_string(gSaveContext.sohStats.heartContainers + 1));
}
if (textId == TEXT_HEART_PIECE && CVar_GetS32("gInjectItemCounts", 0)) {
messageEntry = CustomMessageManager::Instance->RetrieveMessage(customMessageTableID, TEXT_HEART_PIECE);
CustomMessageManager::ReplaceStringInMessage(messageEntry, "{{heartPieceCount}}", std::to_string(gSaveContext.sohStats.heartPieces + 1));
}
if (messageEntry.textBoxType != -1) {
font->charTexBuf[0] = (messageEntry.textBoxType << 4) | messageEntry.textBoxPos;
switch (gSaveContext.language) {

View File

@ -416,6 +416,11 @@ void SaveManager::InitFileNormal() {
}
gSaveContext.inventory.defenseHearts = 0;
gSaveContext.inventory.gsTokens = 0;
gSaveContext.sohStats.heartPieces = 0;
gSaveContext.sohStats.heartContainers = 0;
for (int dungeon = 0; dungeon < ARRAY_COUNT(gSaveContext.sohStats.dungeonKeys); dungeon++) {
gSaveContext.sohStats.dungeonKeys[dungeon] = 0;
}
for (int scene = 0; scene < ARRAY_COUNT(gSaveContext.sceneFlags); scene++) {
gSaveContext.sceneFlags[scene].chest = 0;
gSaveContext.sceneFlags[scene].swch = 0;
@ -561,6 +566,11 @@ void SaveManager::InitFileDebug() {
}
gSaveContext.inventory.defenseHearts = 0;
gSaveContext.inventory.gsTokens = 0;
gSaveContext.sohStats.heartPieces = 8;
gSaveContext.sohStats.heartContainers = 8;
for (int dungeon = 0; dungeon < ARRAY_COUNT(gSaveContext.sohStats.dungeonKeys); dungeon++) {
gSaveContext.sohStats.dungeonKeys[dungeon] = 8;
}
gSaveContext.horseData.scene = SCENE_SPOT00;
gSaveContext.horseData.pos.x = -1840;
@ -955,6 +965,13 @@ void SaveManager::LoadBaseVersion2() {
SaveManager::Instance->LoadData("defenseHearts", gSaveContext.inventory.defenseHearts);
SaveManager::Instance->LoadData("gsTokens", gSaveContext.inventory.gsTokens);
});
SaveManager::Instance->LoadStruct("sohStats", []() {
SaveManager::Instance->LoadData("heartPieces", gSaveContext.sohStats.heartPieces);
SaveManager::Instance->LoadData("heartContainers", gSaveContext.sohStats.heartContainers);
SaveManager::Instance->LoadArray("dungeonKeys", ARRAY_COUNT(gSaveContext.sohStats.dungeonKeys), [](size_t i) {
SaveManager::Instance->LoadData("", gSaveContext.sohStats.dungeonKeys[i]);
});
});
SaveManager::Instance->LoadArray("sceneFlags", ARRAY_COUNT(gSaveContext.sceneFlags), [](size_t i) {
SaveManager::Instance->LoadStruct("", [&i]() {
SaveManager::Instance->LoadData("chest", gSaveContext.sceneFlags[i].chest);
@ -1109,6 +1126,13 @@ void SaveManager::SaveBase() {
SaveManager::Instance->SaveData("defenseHearts", gSaveContext.inventory.defenseHearts);
SaveManager::Instance->SaveData("gsTokens", gSaveContext.inventory.gsTokens);
});
SaveManager::Instance->SaveStruct("sohStats", []() {
SaveManager::Instance->SaveData("heartPieces", gSaveContext.sohStats.heartPieces);
SaveManager::Instance->SaveData("heartContainers", gSaveContext.sohStats.heartContainers);
SaveManager::Instance->SaveArray("dungeonKeys", ARRAY_COUNT(gSaveContext.sohStats.dungeonKeys), [](size_t i) {
SaveManager::Instance->SaveData("", gSaveContext.sohStats.dungeonKeys[i]);
});
});
SaveManager::Instance->SaveArray("sceneFlags", ARRAY_COUNT(gSaveContext.sceneFlags), [](size_t i) {
SaveManager::Instance->SaveStruct("", [&i]() {
SaveManager::Instance->SaveData("chest", gSaveContext.sceneFlags[i].chest);

View File

@ -75,7 +75,7 @@ std::vector<std::string> sceneNames = {
"Castle Hedge Maze (Day)",
"Castle Hedge Maze (Night)",
"Cutscene Map",
"Dampé's Grave & Windmill",
"Damp<EFBFBD>'s Grave & Windmill",
"Fishing Pond",
"Castle Courtyard",
"Bombchu Bowling Alley",
@ -231,7 +231,7 @@ std::vector<std::string> itemNames = {
"Gerudo's Card",
"Gold Skulltula Token",
"Heart Container",
"Piece of Heart [?]",
"Piece of Heart",
"Big Key",
"Compass",
"Dungeon Map",

View File

@ -132,4 +132,22 @@ extern "C" void OTRMessage_Init()
"\x08Missiles 10 unités 99 Rubis\x09&&\x1B%gAcheter&Ne pas acheter%w",
}
);
CustomMessageManager::Instance->CreateGetItemMessage(
customMessageTableID, (GetItemID)TEXT_HEART_CONTAINER, ITEM_HEART_CONTAINER,
{
TEXTBOX_TYPE_BLUE, TEXTBOX_POS_BOTTOM,
"You got a %rHeart Container%w!&You've collected %r{{heartContainerCount}}%w containers&in total!",
"Du erhältst ein %rHerzgefäß%w! Du&hast insgesamt %r{{heartContainerCount}}%w Gefäße&gesammelt!",
"Vous obtenez un %rRécipient de&coeur%w! Vous avez&collecté %r{{heartContainerCount}}%w récipients en tout!"
}
);
CustomMessageManager::Instance->CreateGetItemMessage(
customMessageTableID, (GetItemID)TEXT_HEART_PIECE, ITEM_HEART_PIECE,
{
TEXTBOX_TYPE_BLUE, TEXTBOX_POS_BOTTOM,
"You got a %rHeart Piece%w!&You've collected %r{{heartPieceCount}}%w pieces&in total!",
"Du erhältst ein %rHerzteil%w! Du hast&insgesamt %r{{heartPieceCount}}%w Teile&gesammelt!",
"Vous obtenez un %rMorceau de&coeur%w! Vous avez&collecté %r{{heartPieceCount}}%w morceaux en tout!"
}
);
}

View File

@ -1760,6 +1760,7 @@ u8 Item_Give(GlobalContext* globalCtx, u8 item) {
// but we check for a globalCtx here so the game won't crash if we do somehow get here.
if (gSaveContext.n64ddFlag && globalCtx != NULL) {
if (globalCtx->sceneNum == 10) { // ganon's tower -> ganon's castle
gSaveContext.sohStats.dungeonKeys[13]++;
if (gSaveContext.inventory.dungeonKeys[13] < 0) {
gSaveContext.inventory.dungeonKeys[13] = 1;
PerformAutosave(globalCtx, item);
@ -1772,6 +1773,7 @@ u8 Item_Give(GlobalContext* globalCtx, u8 item) {
}
if (globalCtx->sceneNum == 92) { // Desert Colossus -> Spirit Temple.
gSaveContext.sohStats.dungeonKeys[6]++;
if (gSaveContext.inventory.dungeonKeys[6] < 0) {
gSaveContext.inventory.dungeonKeys[6] = 1;
PerformAutosave(globalCtx, item);
@ -1783,6 +1785,7 @@ u8 Item_Give(GlobalContext* globalCtx, u8 item) {
}
}
}
gSaveContext.sohStats.dungeonKeys[gSaveContext.mapIndex]++;
if (gSaveContext.inventory.dungeonKeys[gSaveContext.mapIndex] < 0) {
gSaveContext.inventory.dungeonKeys[gSaveContext.mapIndex] = 1;
PerformAutosave(globalCtx, item);
@ -2132,11 +2135,13 @@ u8 Item_Give(GlobalContext* globalCtx, u8 item) {
return ITEM_NONE;
} else if ((item == ITEM_HEART_PIECE_2) || (item == ITEM_HEART_PIECE)) {
gSaveContext.inventory.questItems += 1 << (QUEST_HEART_PIECE + 4);
gSaveContext.sohStats.heartPieces++;
PerformAutosave(globalCtx, item);
return ITEM_NONE;
} else if (item == ITEM_HEART_CONTAINER) {
gSaveContext.healthCapacity += 0x10;
gSaveContext.health += 0x10;
gSaveContext.sohStats.heartContainers++;
PerformAutosave(globalCtx, item);
return ITEM_NONE;
} else if (item == ITEM_HEART) {
@ -2447,6 +2452,7 @@ u16 Randomizer_Item_Give(GlobalContext* globalCtx, GetItemEntry giEntry) {
}
if ((item >= RG_FOREST_TEMPLE_SMALL_KEY) && (item <= RG_GANONS_CASTLE_SMALL_KEY)) {
gSaveContext.sohStats.dungeonKeys[mapIndex]++;
if (gSaveContext.inventory.dungeonKeys[mapIndex] < 0) {
gSaveContext.inventory.dungeonKeys[mapIndex] = 1;
return RG_NONE;

View File

@ -461,13 +461,21 @@ void Sram_InitSave(FileChooseContext* fileChooseCtx) {
if(Randomizer_GetSettingValue(RSK_KEYSANITY) == 0) {
// TODO: If master quest there are different key counts
gSaveContext.inventory.dungeonKeys[SCENE_BMORI1] = FOREST_TEMPLE_SMALL_KEY_MAX; // Forest
gSaveContext.sohStats.dungeonKeys[SCENE_BMORI1] = FOREST_TEMPLE_SMALL_KEY_MAX; // Forest
gSaveContext.inventory.dungeonKeys[SCENE_HIDAN] = FIRE_TEMPLE_SMALL_KEY_MAX; // Fire
gSaveContext.sohStats.dungeonKeys[SCENE_HIDAN] = FIRE_TEMPLE_SMALL_KEY_MAX; // Fire
gSaveContext.inventory.dungeonKeys[SCENE_MIZUSIN] = WATER_TEMPLE_SMALL_KEY_MAX; // Water
gSaveContext.sohStats.dungeonKeys[SCENE_MIZUSIN] = WATER_TEMPLE_SMALL_KEY_MAX; // Water
gSaveContext.inventory.dungeonKeys[SCENE_JYASINZOU] = SPIRIT_TEMPLE_SMALL_KEY_MAX; // Spirit
gSaveContext.sohStats.dungeonKeys[SCENE_JYASINZOU] = SPIRIT_TEMPLE_SMALL_KEY_MAX; // Spirit
gSaveContext.inventory.dungeonKeys[SCENE_HAKADAN] = SHADOW_TEMPLE_SMALL_KEY_MAX; // Shadow
gSaveContext.sohStats.dungeonKeys[SCENE_HAKADAN] = SHADOW_TEMPLE_SMALL_KEY_MAX; // Shadow
gSaveContext.inventory.dungeonKeys[SCENE_HAKADANCH] = BOTTOM_OF_THE_WELL_SMALL_KEY_MAX; // BotW
gSaveContext.sohStats.dungeonKeys[SCENE_HAKADANCH] = BOTTOM_OF_THE_WELL_SMALL_KEY_MAX; // BotW
gSaveContext.inventory.dungeonKeys[SCENE_MEN] = GERUDO_TRAINING_GROUNDS_SMALL_KEY_MAX; // GTG
gSaveContext.sohStats.dungeonKeys[SCENE_MEN] = GERUDO_TRAINING_GROUNDS_SMALL_KEY_MAX; // GTG
gSaveContext.inventory.dungeonKeys[SCENE_GANONTIKA] = GANONS_CASTLE_SMALL_KEY_MAX; // Ganon
gSaveContext.sohStats.dungeonKeys[SCENE_GANONTIKA] = GANONS_CASTLE_SMALL_KEY_MAX; // Ganon
}
// "Start with" == 0 for Boss Kesanity