diff --git a/CMakeLists.txt b/CMakeLists.txt index 7ba457735..abb8d5fdc 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -5,12 +5,13 @@ set(CMAKE_CXX_STANDARD 20 CACHE STRING "The C++ standard to use") set(CMAKE_OSX_DEPLOYMENT_TARGET "10.15" CACHE STRING "Minimum OS X deployment version") -project(Ship VERSION 7.0.1 LANGUAGES C CXX) -set(PROJECT_BUILD_NAME "Spock Bravo" CACHE STRING "") +project(Ship VERSION 7.0.2 LANGUAGES C CXX) +set(PROJECT_BUILD_NAME "Spock Charlie" CACHE STRING "") set(PROJECT_TEAM "github.com/harbourmasters" CACHE STRING "") set_property(DIRECTORY ${CMAKE_SOURCE_DIR} PROPERTY VS_STARTUP_PROJECT soh) add_compile_options($<$:/MP>) +add_compile_options($<$:/utf-8>) if (CMAKE_SYSTEM_NAME MATCHES "Windows|Linux") if(NOT DEFINED BUILD_CROWD_CONTROL) diff --git a/soh/CMakeLists.txt b/soh/CMakeLists.txt index 5aaeb6fd4..23e657f0d 100644 --- a/soh/CMakeLists.txt +++ b/soh/CMakeLists.txt @@ -500,6 +500,7 @@ if(MSVC) /INCREMENTAL:NO; /FORCE:MULTIPLE > + /MANIFEST:NO; /DEBUG; /SUBSYSTEM:WINDOWS ) @@ -514,6 +515,7 @@ if(MSVC) /INCREMENTAL:NO; /FORCE:MULTIPLE > + /MANIFEST:NO; /DEBUG; /SUBSYSTEM:WINDOWS ) diff --git a/soh/Resource.rc b/soh/Resource.rc index ad27c5471..7d9f356d0 100644 --- a/soh/Resource.rc +++ b/soh/Resource.rc @@ -91,6 +91,13 @@ END // remains consistent on all systems. IDI_ICON1 ICON "SHIPOFHARKINIAN.ico" +///////////////////////////////////////////////////////////////////////////// +// +// RT_MANIFEST +// + +1 RT_MANIFEST "SHIPOFHARKINIAN.manifest" + #endif // English (United States) resources ///////////////////////////////////////////////////////////////////////////// diff --git a/soh/SHIPOFHARKINIAN.manifest b/soh/SHIPOFHARKINIAN.manifest new file mode 100644 index 000000000..80d9f6036 --- /dev/null +++ b/soh/SHIPOFHARKINIAN.manifest @@ -0,0 +1,31 @@ + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/soh/include/variables.h b/soh/include/variables.h index a2ac65faf..55adf0d2e 100644 --- a/soh/include/variables.h +++ b/soh/include/variables.h @@ -103,6 +103,7 @@ extern "C" extern u32 gGsFlagsShifts[4]; extern void* gItemIcons[0x82]; extern u8 gItemAgeReqs[]; + extern u8 gSlotAgeReqs[]; extern u8 gItemSlots[56]; extern void (*gSceneCmdHandlers[SCENE_CMD_ID_MAX])(PlayState*, SceneCmd*); extern s16 gLinkObjectIds[2]; diff --git a/soh/include/z64save.h b/soh/include/z64save.h index 97a8837fa..a6f1b96be 100644 --- a/soh/include/z64save.h +++ b/soh/include/z64save.h @@ -92,6 +92,8 @@ typedef struct { /* */ u32 entrancesDiscovered[SAVEFILE_ENTRANCES_DISCOVERED_IDX_COUNT]; /* */ u32 scenesDiscovered[SAVEFILE_SCENES_DISCOVERED_IDX_COUNT]; /* */ u8 locationsSkipped[RC_MAX]; + /* */ bool rtaTiming; + /* */ uint64_t fileCreatedAt; } SohStats; typedef struct { diff --git a/soh/soh/Enhancements/controls/GameControlEditor.cpp b/soh/soh/Enhancements/controls/GameControlEditor.cpp index 719edaf0e..5b2d42902 100644 --- a/soh/soh/Enhancements/controls/GameControlEditor.cpp +++ b/soh/soh/Enhancements/controls/GameControlEditor.cpp @@ -267,10 +267,10 @@ namespace GameControlEditor { UIWidgets::PaddedEnhancementCheckbox("Invert Camera Y Axis", "gInvertYAxis", true, true, false, "", UIWidgets::CheckboxGraphics::Cross, true); DrawHelpIcon("Inverts the Camera Y Axis in:\n-Free camera"); UIWidgets::Spacer(0); -+ UIWidgets::PaddedEnhancementSliderFloat("Third-Person Horizontal Sensitivity: %d %%", "##ThirdPersonSensitivity Horizontal", -+ "gThirdPersonCameraSensitivityX", 0.01f, 5.0f, "", 1.0f, true, true, false, true); -+ UIWidgets::PaddedEnhancementSliderFloat("Third-Person Vertical Sensitivity: %d %%", "##ThirdPersonSensitivity Vertical", -+ "gThirdPersonCameraSensitivityY", 0.01f, 5.0f, "", 1.0f, true, true, false, true); + UIWidgets::PaddedEnhancementSliderFloat("Third-Person Horizontal Sensitivity: %d %%", "##ThirdPersonSensitivity Horizontal", + "gThirdPersonCameraSensitivityX", 0.01f, 5.0f, "", 1.0f, true, true, false, true); + UIWidgets::PaddedEnhancementSliderFloat("Third-Person Vertical Sensitivity: %d %%", "##ThirdPersonSensitivity Vertical", + "gThirdPersonCameraSensitivityY", 0.01f, 5.0f, "", 1.0f, true, true, false, true); UIWidgets::PaddedEnhancementSliderInt("Camera Distance: %d", "##CamDist", "gFreeCameraDistMax", 100, 900, "", 185, true, false, true); UIWidgets::PaddedEnhancementSliderInt("Camera Transition Speed: %d", "##CamTranSpeed", diff --git a/soh/soh/Enhancements/game-interactor/GameInteractor_Hooks.h b/soh/soh/Enhancements/game-interactor/GameInteractor_Hooks.h index b8029914f..07843c950 100644 --- a/soh/soh/Enhancements/game-interactor/GameInteractor_Hooks.h +++ b/soh/soh/Enhancements/game-interactor/GameInteractor_Hooks.h @@ -1,40 +1,46 @@ #include "GameInteractor.h" +#ifdef __cplusplus +extern "C" { +#endif // MARK: - Gameplay -extern "C" void GameInteractor_ExecuteOnLoadGame(int32_t fileNum); -extern "C" void GameInteractor_ExecuteOnExitGame(int32_t fileNum); -extern "C" void GameInteractor_ExecuteOnGameFrameUpdate(); -extern "C" void GameInteractor_ExecuteOnItemReceiveHooks(GetItemEntry itemEntry); -extern "C" void GameInteractor_ExecuteOnSaleEndHooks(GetItemEntry itemEntry); -extern "C" void GameInteractor_ExecuteOnTransitionEndHooks(int16_t sceneNum); -extern "C" void GameInteractor_ExecuteOnSceneInit(int16_t sceneNum); -extern "C" void GameInteractor_ExecuteOnSceneSpawnActors(); -extern "C" void GameInteractor_ExecuteOnPlayerUpdate(); -extern "C" void GameInteractor_ExecuteOnOcarinaSongAction(); -extern "C" void GameInteractor_ExecuteOnActorUpdate(void* actor); -extern "C" void GameInteractor_ExecuteOnPlayerBonk(); -extern "C" void GameInteractor_ExecuteOnOcarinaSongAction(); +void GameInteractor_ExecuteOnLoadGame(int32_t fileNum); +void GameInteractor_ExecuteOnExitGame(int32_t fileNum); +void GameInteractor_ExecuteOnGameFrameUpdate(); +void GameInteractor_ExecuteOnItemReceiveHooks(GetItemEntry itemEntry); +void GameInteractor_ExecuteOnSaleEndHooks(GetItemEntry itemEntry); +void GameInteractor_ExecuteOnTransitionEndHooks(int16_t sceneNum); +void GameInteractor_ExecuteOnSceneInit(int16_t sceneNum); +void GameInteractor_ExecuteOnSceneSpawnActors(); +void GameInteractor_ExecuteOnPlayerUpdate(); +void GameInteractor_ExecuteOnOcarinaSongAction(); +void GameInteractor_ExecuteOnActorUpdate(void* actor); +void GameInteractor_ExecuteOnPlayerBonk(); +void GameInteractor_ExecuteOnOcarinaSongAction(); // MARK: - Save Files -extern "C" void GameInteractor_ExecuteOnSaveFile(int32_t fileNum); -extern "C" void GameInteractor_ExecuteOnLoadFile(int32_t fileNum); -extern "C" void GameInteractor_ExecuteOnDeleteFile(int32_t fileNum); +void GameInteractor_ExecuteOnSaveFile(int32_t fileNum); +void GameInteractor_ExecuteOnLoadFile(int32_t fileNum); +void GameInteractor_ExecuteOnDeleteFile(int32_t fileNum); // MARK: - Dialog -extern "C" void GameInteractor_ExecuteOnDialogMessage(); -extern "C" void GameInteractor_ExecuteOnPresentTitleCard(); -extern "C" void GameInteractor_ExecuteOnInterfaceUpdate(); -extern "C" void GameInteractor_ExecuteOnKaleidoscopeUpdate(int16_t inDungeonScene); +void GameInteractor_ExecuteOnDialogMessage(); +void GameInteractor_ExecuteOnPresentTitleCard(); +void GameInteractor_ExecuteOnInterfaceUpdate(); +void GameInteractor_ExecuteOnKaleidoscopeUpdate(int16_t inDungeonScene); // MARK: - Main Menu -extern "C" void GameInteractor_ExecuteOnPresentFileSelect(); -extern "C" void GameInteractor_ExecuteOnUpdateFileSelectSelection(uint16_t optionIndex); -extern "C" void GameInteractor_ExecuteOnUpdateFileCopySelection(uint16_t optionIndex); -extern "C" void GameInteractor_ExecuteOnUpdateFileCopyConfirmationSelection(uint16_t optionIndex); -extern "C" void GameInteractor_ExecuteOnUpdateFileEraseSelection(uint16_t optionIndex); -extern "C" void GameInteractor_ExecuteOnUpdateFileEraseConfirmationSelection(uint16_t optionIndex); -extern "C" void GameInteractor_ExecuteOnUpdateFileAudioSelection(uint8_t optionIndex); -extern "C" void GameInteractor_ExecuteOnUpdateFileTargetSelection(uint8_t optionIndex); +void GameInteractor_ExecuteOnPresentFileSelect(); +void GameInteractor_ExecuteOnUpdateFileSelectSelection(uint16_t optionIndex); +void GameInteractor_ExecuteOnUpdateFileCopySelection(uint16_t optionIndex); +void GameInteractor_ExecuteOnUpdateFileCopyConfirmationSelection(uint16_t optionIndex); +void GameInteractor_ExecuteOnUpdateFileEraseSelection(uint16_t optionIndex); +void GameInteractor_ExecuteOnUpdateFileEraseConfirmationSelection(uint16_t optionIndex); +void GameInteractor_ExecuteOnUpdateFileAudioSelection(uint8_t optionIndex); +void GameInteractor_ExecuteOnUpdateFileTargetSelection(uint8_t optionIndex); // MARK: - Game -extern "C" void GameInteractor_ExecuteOnSetGameLanguage(); +void GameInteractor_ExecuteOnSetGameLanguage(); +#ifdef __cplusplus +} +#endif diff --git a/soh/soh/Enhancements/gameplaystats.cpp b/soh/soh/Enhancements/gameplaystats.cpp index b4b1c9732..dd4ededed 100644 --- a/soh/soh/Enhancements/gameplaystats.cpp +++ b/soh/soh/Enhancements/gameplaystats.cpp @@ -13,126 +13,219 @@ extern "C" { #include #include -//#define ARRAY_COUNT(arr) (s32)(sizeof(arr) / sizeof(arr[0])) - extern "C" { #include #include "variables.h" extern PlayState* gPlayState; +uint64_t GetUnixTimestamp(); } -const std::vector sceneMappings = { - {"Inside the Deku Tree"}, - {"Dodongo's Cavern"}, - {"Inside Jabu-Jabu's Belly"}, - {"Forest Temple"}, - {"Fire Temple"}, - {"Water Temple"}, - {"Spirit Temple"}, - {"Shadow Temple"}, - {"Bottom of the Well"}, - {"Ice Cavern"}, - {"Ganon's Tower"}, - {"Gerudo Training Ground"}, - {"Theives' Hideout"}, - {"Inside Ganon's Castle"}, - {"Tower Collapse"}, - {"Castle Collapse"}, - {"Treasure Box Shop"}, - {"Gohma's Lair"}, - {"King Dodongo's Lair"}, - {"Barinade's Lair"}, - {"Phantom Ganon's Lair"}, - {"Volvagia's Lair"}, - {"Morpha's Lair"}, - {"Twinrova's Lair"}, - {"Bongo Bongo's Lair"}, - {"Ganondorf's Lair"}, - {"Ganon's Lair"}, - {"Market Entrance (Day)"}, - {"Market Entrance (Night)"}, - {"Market Entrance (Adult)"}, - {"Back Alley (Day)"}, - {"Back Alley (Night)"}, - {"Market (Day)"}, - {"Market (Night)"}, - {"Market (Adult)"}, - {"Outside ToT (Day)"}, - {"Outside ToT (Night)"}, - {"Outside ToT (Adult)"}, - {"Know-It-All Bros' House"}, - {"Twins' House"}, - {"Mido's House"}, - {"Saria's House"}, - {"Carpenter Boss's House"}, - {"Man in Green's House"}, - {"Bazaar"}, - {"Kokiri Shop"}, - {"Goron Shop"}, - {"Zora Shop"}, - {"Kakariko Potion Shop"}, - {"Market Potion Shop"}, - {"Bombchu Shop"}, - {"Happy Mask Shop"}, - {"Link's House"}, - {"Richard's House"}, - {"Stable"}, - {"Impa's House"}, - {"Lakeside Lab"}, - {"Carpenters' Tent"}, - {"Gravekeeper's Hut"}, - {"Great Fairy"}, - {"Fairy Fountain"}, - {"Great Fairy"}, - {"Grotto"}, - {"Redead Grave"}, - {"Fairy Fountain Grave"}, - {"Royal Family's Tomb"}, - {"Shooting Gallery"}, - {"Temple of Time"}, - {"Chamber of Sages"}, - {"Castle Maze (Day)"}, - {"Castle Maze (Night)"}, - {"Cutscene Map"}, - {"Dampe's Grave"}, - {"Fishing Pond"}, - {"Castle Courtyard"}, - {"Bombchu Bowling Alley"}, - {"Ranch House"}, - {"Guard House"}, - {"Granny's Potion Shop"}, - {"Ganon Fight"}, - {"House of Skulltula"}, - {"Hyrule Field"}, - {"Kakariko Village"}, - {"Graveyard"}, - {"Zora's River"}, - {"Kokiri Forest"}, - {"Sacred Forest Meadow"}, - {"Lake Hylia"}, - {"Zora's Domain"}, - {"Zora's Fountain"}, - {"Gerudo Valley"}, - {"Lost Woods"}, - {"Desert Colossus"}, - {"Gerudo's Fortress"}, - {"Haunted Wasteland"}, - {"Hyrule Castle"}, - {"Death Mountain Trail"}, - {"Death Mountain Crater"}, - {"Goron City"}, - {"Lon Lon Ranch"}, - {"Outside Ganon's Castle"}, +const char* const sceneMappings[] = { + "Inside the Deku Tree", + "Dodongo's Cavern", + "Inside Jabu-Jabu's Belly", + "Forest Temple", + "Fire Temple", + "Water Temple", + "Spirit Temple", + "Shadow Temple", + "Bottom of the Well", + "Ice Cavern", + "Ganon's Tower", + "Gerudo Training Ground", + "Theives' Hideout", + "Inside Ganon's Castle", + "Tower Collapse", + "Castle Collapse", + "Treasure Box Shop", + "Gohma's Lair", + "King Dodongo's Lair", + "Barinade's Lair", + "Phantom Ganon's Lair", + "Volvagia's Lair", + "Morpha's Lair", + "Twinrova's Lair", + "Bongo Bongo's Lair", + "Ganondorf's Lair", + "Ganon's Lair", + "Market Entrance (Day)", + "Market Entrance (Night)", + "Market Entrance (Adult)", + "Back Alley (Day)", + "Back Alley (Night)", + "Market (Day)", + "Market (Night)", + "Market (Adult)", + "Outside ToT (Day)", + "Outside ToT (Night)", + "Outside ToT (Adult)", + "Know-It-All Bros' House", + "Twins' House", + "Mido's House", + "Saria's House", + "Carpenter Boss's House", + "Man in Green's House", + "Bazaar", + "Kokiri Shop", + "Goron Shop", + "Zora Shop", + "Kakariko Potion Shop", + "Market Potion Shop", + "Bombchu Shop", + "Happy Mask Shop", + "Link's House", + "Richard's House", + "Stable", + "Impa's House", + "Lakeside Lab", + "Carpenters' Tent", + "Gravekeeper's Hut", + "Great Fairy", + "Fairy Fountain", + "Great Fairy", + "Grotto", + "Redead Grave", + "Fairy Fountain Grave", + "Royal Family's Tomb", + "Shooting Gallery", + "Temple of Time", + "Chamber of Sages", + "Castle Maze (Day)", + "Castle Maze (Night)", + "Cutscene Map", + "Dampe's Grave", + "Fishing Pond", + "Castle Courtyard", + "Bombchu Bowling Alley", + "Ranch House", + "Guard House", + "Granny's Potion Shop", + "Ganon Fight", + "House of Skulltula", + "Hyrule Field", + "Kakariko Village", + "Graveyard", + "Zora's River", + "Kokiri Forest", + "Sacred Forest Meadow", + "Lake Hylia", + "Zora's Domain", + "Zora's Fountain", + "Gerudo Valley", + "Lost Woods", + "Desert Colossus", + "Gerudo's Fortress", + "Haunted Wasteland", + "Hyrule Castle", + "Death Mountain Trail", + "Death Mountain Crater", + "Goron City", + "Lon Lon Ranch", + "Outside Ganon's Castle", //Debug Rooms - {"Test Map"}, - {"Test Room"}, - {"Depth Test"}, - {"Stalfos Mini-Boss"}, - {"Stalfos Boss"}, - {"Dark Link"}, - {"Castle Maze (Broken)"}, - {"SRD Room"}, - {"Chest Room"} + "Test Map", + "Test Room", + "Depth Test", + "Stalfos Mini-Boss", + "Stalfos Boss", + "Dark Link", + "Castle Maze (Broken)", + "SRD Room", + "Chest Room", +}; + +const char* const countMappings[] = { + "Anubis:", + "Armos:", + "Arwing:", + "Bari:", + "Biri:", + "Beamos:", + "Big Octo:", + "Bubble (Blue):", + "Bubble (Green):", + "Bubble (Red):", + "Bubble (White):", + "Business Scrub:", + "Dark Link:", + "Dead Hand:", + "Deku Baba:", + "Deku Baba (Big):", + "Deku Scrub:", + "Dinolfos:", + "Dodongo:", + "Dodongo (Baby):", + "Door Mimic:", + "Flare Dancer:", + "Floormaster:", + "Flying Floor Tile:", + "Flying Pot:", + "Freezard:", + "Gerudo Thief:", + "Gibdo:", + "Gohma Larva:", + "Guay:", + "Iron Knuckle:", + "Iron Knuckle (Nab):", + "Keese:", + "Keese (Fire):", + "Keese (Ice):", + "Leever:", + "Leever (Big):", + "Like-Like:", + "Lizalfos:", + "Mad Scrub:", + "Moblin:", + "Moblin (Club):", + "Octorok:", + "Parasitic Tentacle:", + "Peahat:", + "Peahat Larva:", + "Poe:", + "Poe (Big):", + "Poe (Composer):", + "Poe Sisters:", + "Redead:", + "Shabom:", + "Shellblade:", + "Skull Kid:", + "Skulltula:", + "Skulltula (Big):", + "Skulltula (Gold):", + "Skullwalltula:", + "Spike:", + "Stalchild:", + "Stalfos:", + "Stinger:", + "Tailpasaran:", + "Tektite (Blue):", + "Tektite (Red):", + "Torch Slug:", + "Wallmaster:", + "Withered Deku Baba:", + "Wolfos:", + "Wolfos (White):", + "Deku Sticks:", + "Deku Nuts:", + "Bombs:", + "Arrows:", + "Deku Seeds:", + "Bombchus:", + "Beans:", + "A:", + "B:", + "L:", + "R:", + "Z:", + "C-Up:", + "C-Right:", + "C-Down:", + "C-Left:", + "D-Up:", + "D-Right:", + "D-Down:", + "D-Left:", + "Start:", }; #define COLOR_WHITE ImVec4(1.00f, 1.00f, 1.00f, 1.00f) @@ -162,21 +255,25 @@ TimestampInfo itemTimestampDisplay[TIMESTAMP_MAX]; TimestampInfo sceneTimestampDisplay[8191]; //std::vector sceneTimestampDisplay; -void DisplayTimeHHMMSS(uint32_t timeInTenthsOfSeconds, std::string text, ImVec4 color) { - - uint32_t sec = timeInTenthsOfSeconds / 10; +std::string formatTimestampGameplayStat(uint32_t value) { + uint32_t sec = value / 10; uint32_t hh = sec / 3600; uint32_t mm = (sec - hh * 3600) / 60; uint32_t ss = sec - hh * 3600 - mm * 60; - uint32_t ds = timeInTenthsOfSeconds % 10; + uint32_t ds = value % 10; + return fmt::format("{}:{:0>2}:{:0>2}.{}", hh, mm, ss, ds); +} - ImGui::PushStyleColor(ImGuiCol_Text, color); +std::string formatIntGameplayStat(uint32_t value) { + return fmt::format("{}", value); +} - std::string padded = fmt::format("{:<40}", text); - ImGui::Text(padded.c_str()); - ImGui::SameLine(); - ImGui::Text("%2u:%02u:%02u.%u", hh, mm, ss, ds); - ImGui::PopStyleColor(); +std::string formatHexGameplayStat(uint32_t value) { + return fmt::format("{:#x} ({:d})", value, value); +} + +std::string formatHexOnlyGameplayStat(uint32_t value) { + return fmt::format("{:#x}", value, value); } void LoadStatsVersion1() { @@ -193,6 +290,8 @@ void LoadStatsVersion1() { SaveManager::Instance->LoadArray("dungeonKeys", ARRAY_COUNT(gSaveContext.sohStats.dungeonKeys), [](size_t i) { SaveManager::Instance->LoadData("", gSaveContext.sohStats.dungeonKeys[i]); }); + SaveManager::Instance->LoadData("rtaTiming", gSaveContext.sohStats.rtaTiming); + SaveManager::Instance->LoadData("fileCreatedAt", gSaveContext.sohStats.fileCreatedAt); SaveManager::Instance->LoadData("playTimer", gSaveContext.sohStats.playTimer); SaveManager::Instance->LoadData("pauseTimer", gSaveContext.sohStats.pauseTimer); SaveManager::Instance->LoadArray("itemTimestamps", ARRAY_COUNT(gSaveContext.sohStats.itemTimestamp), [](size_t i) { @@ -200,20 +299,12 @@ void LoadStatsVersion1() { }); SaveManager::Instance->LoadArray("sceneTimestamps", ARRAY_COUNT(gSaveContext.sohStats.sceneTimestamps), [](size_t i) { SaveManager::Instance->LoadStruct("", [&i]() { - int scene, room, sceneTime, roomTime, isRoom; - SaveManager::Instance->LoadData("scene", scene); - SaveManager::Instance->LoadData("room", room); - SaveManager::Instance->LoadData("sceneTime", sceneTime); - SaveManager::Instance->LoadData("roomTime", roomTime); - SaveManager::Instance->LoadData("isRoom", isRoom); - if (scene == 0 && room == 0 && sceneTime == 0 && roomTime == 0 && isRoom == 0) { - return; - } - gSaveContext.sohStats.sceneTimestamps[i].scene = scene; - gSaveContext.sohStats.sceneTimestamps[i].room = room; - gSaveContext.sohStats.sceneTimestamps[i].sceneTime = sceneTime; - gSaveContext.sohStats.sceneTimestamps[i].roomTime = roomTime; - gSaveContext.sohStats.sceneTimestamps[i].isRoom = isRoom; + SaveManager::Instance->LoadData("scene", gSaveContext.sohStats.sceneTimestamps[i].scene); + SaveManager::Instance->LoadData("room", gSaveContext.sohStats.sceneTimestamps[i].room); + SaveManager::Instance->LoadData("sceneTime", gSaveContext.sohStats.sceneTimestamps[i].sceneTime); + SaveManager::Instance->LoadData("roomTime", gSaveContext.sohStats.sceneTimestamps[i].roomTime); + SaveManager::Instance->LoadData("isRoom", gSaveContext.sohStats.sceneTimestamps[i].isRoom); + }); }); SaveManager::Instance->LoadData("tsIdx", gSaveContext.sohStats.tsIdx); @@ -232,60 +323,335 @@ void LoadStatsVersion1() { } void SaveStats(SaveContext* saveContext, int sectionID) { - if (sectionID == SECTION_ID_BASE) { - std::string buildVersion; - SaveManager::Instance->LoadData("buildVersion", buildVersion); - strncpy(gSaveContext.sohStats.buildVersion, buildVersion.c_str(), ARRAY_COUNT(gSaveContext.sohStats.buildVersion) - 1); - gSaveContext.sohStats.buildVersion[ARRAY_COUNT(gSaveContext.sohStats.buildVersion) - 1] = 0; - SaveManager::Instance->LoadData("buildVersionMajor", gSaveContext.sohStats.buildVersionMajor); - SaveManager::Instance->LoadData("buildVersionMinor", gSaveContext.sohStats.buildVersionMinor); - SaveManager::Instance->LoadData("buildVersionPatch", gSaveContext.sohStats.buildVersionPatch); + SaveManager::Instance->SaveData("buildVersion", saveContext->sohStats.buildVersion); + SaveManager::Instance->SaveData("buildVersionMajor", saveContext->sohStats.buildVersionMajor); + SaveManager::Instance->SaveData("buildVersionMinor", saveContext->sohStats.buildVersionMinor); + SaveManager::Instance->SaveData("buildVersionPatch", saveContext->sohStats.buildVersionPatch); - 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->SaveData("heartPieces", saveContext->sohStats.heartPieces); + SaveManager::Instance->SaveData("heartContainers", saveContext->sohStats.heartContainers); + SaveManager::Instance->SaveArray("dungeonKeys", ARRAY_COUNT(saveContext->sohStats.dungeonKeys), [&](size_t i) { + SaveManager::Instance->SaveData("", saveContext->sohStats.dungeonKeys[i]); + }); + SaveManager::Instance->SaveData("rtaTiming", saveContext->sohStats.rtaTiming); + SaveManager::Instance->SaveData("fileCreatedAt", saveContext->sohStats.fileCreatedAt); + SaveManager::Instance->SaveData("playTimer", saveContext->sohStats.playTimer); + SaveManager::Instance->SaveData("pauseTimer", saveContext->sohStats.pauseTimer); + SaveManager::Instance->SaveArray("itemTimestamps", ARRAY_COUNT(saveContext->sohStats.itemTimestamp), [&](size_t i) { + SaveManager::Instance->SaveData("", saveContext->sohStats.itemTimestamp[i]); + }); + SaveManager::Instance->SaveArray("sceneTimestamps", ARRAY_COUNT(saveContext->sohStats.sceneTimestamps), [&](size_t i) { + SaveManager::Instance->SaveStruct("", [&]() { + SaveManager::Instance->SaveData("scene", saveContext->sohStats.sceneTimestamps[i].scene); + SaveManager::Instance->SaveData("room", saveContext->sohStats.sceneTimestamps[i].room); + SaveManager::Instance->SaveData("sceneTime", saveContext->sohStats.sceneTimestamps[i].sceneTime); + SaveManager::Instance->SaveData("roomTime", saveContext->sohStats.sceneTimestamps[i].roomTime); + SaveManager::Instance->SaveData("isRoom", saveContext->sohStats.sceneTimestamps[i].isRoom); }); - SaveManager::Instance->LoadData("playTimer", gSaveContext.sohStats.playTimer); - SaveManager::Instance->LoadData("pauseTimer", gSaveContext.sohStats.pauseTimer); - SaveManager::Instance->LoadArray("itemTimestamps", ARRAY_COUNT(gSaveContext.sohStats.itemTimestamp), [](size_t i) { - SaveManager::Instance->LoadData("", gSaveContext.sohStats.itemTimestamp[i]); - }); - SaveManager::Instance->LoadArray("sceneTimestamps", ARRAY_COUNT(gSaveContext.sohStats.sceneTimestamps), [](size_t i) { - SaveManager::Instance->LoadStruct("", [&i]() { - SaveManager::Instance->LoadData("scene", gSaveContext.sohStats.sceneTimestamps[i].scene); - SaveManager::Instance->LoadData("room", gSaveContext.sohStats.sceneTimestamps[i].room); - SaveManager::Instance->LoadData("sceneTime", gSaveContext.sohStats.sceneTimestamps[i].sceneTime); - SaveManager::Instance->LoadData("roomTime", gSaveContext.sohStats.sceneTimestamps[i].roomTime); - SaveManager::Instance->LoadData("isRoom", gSaveContext.sohStats.sceneTimestamps[i].isRoom); - }); - }); - SaveManager::Instance->LoadData("tsIdx", gSaveContext.sohStats.tsIdx); - SaveManager::Instance->LoadArray("counts", ARRAY_COUNT(gSaveContext.sohStats.count), [](size_t i) { - SaveManager::Instance->LoadData("", gSaveContext.sohStats.count[i]); - }); - SaveManager::Instance->LoadArray("locationsSkipped", ARRAY_COUNT(gSaveContext.sohStats.locationsSkipped), [](size_t i) { - SaveManager::Instance->LoadData("", gSaveContext.sohStats.locationsSkipped[i]); - }); - } - if (sectionID == SECTION_ID_ENTRANCES || sectionID == SECTION_ID_BASE) { - SaveManager::Instance->SaveArray("entrancesDiscovered", ARRAY_COUNT(saveContext->sohStats.entrancesDiscovered), [&](size_t i) { - SaveManager::Instance->SaveData("", saveContext->sohStats.entrancesDiscovered[i]); - }); - } - if (sectionID == SECTION_ID_SCENES || sectionID == SECTION_ID_BASE) { - SaveManager::Instance->SaveArray("scenesDiscovered", ARRAY_COUNT(saveContext->sohStats.scenesDiscovered), [&](size_t i) { - SaveManager::Instance->SaveData("", saveContext->sohStats.scenesDiscovered[i]); - }); - } + }); + SaveManager::Instance->SaveData("tsIdx", saveContext->sohStats.tsIdx); + SaveManager::Instance->SaveArray("counts", ARRAY_COUNT(saveContext->sohStats.count), [&](size_t i) { + SaveManager::Instance->SaveData("", saveContext->sohStats.count[i]); + }); + SaveManager::Instance->SaveArray("scenesDiscovered", ARRAY_COUNT(saveContext->sohStats.scenesDiscovered), [&](size_t i) { + SaveManager::Instance->SaveData("", saveContext->sohStats.scenesDiscovered[i]); + }); + SaveManager::Instance->SaveArray("entrancesDiscovered", ARRAY_COUNT(saveContext->sohStats.entrancesDiscovered), [&](size_t i) { + SaveManager::Instance->SaveData("", saveContext->sohStats.entrancesDiscovered[i]); + }); + SaveManager::Instance->SaveArray("locationsSkipped", ARRAY_COUNT(saveContext->sohStats.locationsSkipped), [&](size_t i) { + SaveManager::Instance->SaveData("", saveContext->sohStats.locationsSkipped[i]); + }); } +void GameplayStatsRow(const char* label, std::string value, ImVec4 color = COLOR_WHITE) { + ImGui::PushStyleColor(ImGuiCol_Text, color); + ImGui::TableNextRow(); + ImGui::TableNextColumn(); + ImGui::Text(label); + ImGui::SameLine(ImGui::GetContentRegionAvail().x - (ImGui::CalcTextSize(value.c_str()).x - 8.0f)); + ImGui::Text("%s", value.c_str()); + ImGui::PopStyleColor(); +} + +bool compareTimestampInfoByTime(const TimestampInfo& a, const TimestampInfo& b) { + return CVarGetInteger("gGameplayStats.TimestampsReverse", 0) ? a.time > b.time : a.time < b.time; +} + +const char* ResolveSceneID(int sceneID, int roomID){ + if (sceneID == SCENE_KAKUSIANA) { + switch (roomID) { + case 0: + return "Generic Grotto"; + case 1: + return "Lake Hylia Scrub Grotto"; + case 2: + return "Redead Grotto"; + case 3: + return "Cow Grotto"; + case 4: + return "Scrub Trio"; + case 5: + return "Flooded Grotto"; + case 6: + return "Scrub Duo (Upgrade)"; + case 7: + return "Wolfos Grotto"; + case 8: + return "Hyrule Castle Storms Grotto"; + case 9: + return "Scrub Duo"; + case 10: + return "Tektite Grotto"; + case 11: + return "Forest Stage"; + case 12: + return "Webbed Grotto"; + case 13: + return "Big Skulltula Grotto"; + }; + } else if (sceneID == SCENE_HAKASITARELAY) { + //Only the last room of Dampe's Grave (rm 6) is considered the windmill + return roomID == 6 ? "Windmill" : "Dampe's Grave"; + } else if (sceneID < SCENE_ID_MAX) { + return sceneMappings[sceneID]; + } + + return "???"; +} + +void DrawGameplayStatsHeader() { + ImGui::PushStyleVar(ImGuiStyleVar_CellPadding, { 4.0f, 4.0f }); + ImGui::BeginTable("gameplayStatsHeader", 1, ImGuiTableFlags_BordersOuter); + ImGui::TableSetupColumn("stat", ImGuiTableColumnFlags_WidthStretch); + GameplayStatsRow("Build Version:", gSaveContext.sohStats.buildVersion); + if (gSaveContext.sohStats.rtaTiming) { + GameplayStatsRow("Total Time (RTA):", formatTimestampGameplayStat(GAMEPLAYSTAT_TOTAL_TIME), gSaveContext.sohStats.gameComplete ? COLOR_GREEN : COLOR_WHITE); + } else { + GameplayStatsRow("Total Game Time:", formatTimestampGameplayStat(GAMEPLAYSTAT_TOTAL_TIME), gSaveContext.sohStats.gameComplete ? COLOR_GREEN : COLOR_WHITE); + } + if (CVarGetInteger("gGameplayStats.ShowAdditionalTimers", 0)) { // !Only display total game time + GameplayStatsRow("Gameplay Time:", formatTimestampGameplayStat(gSaveContext.sohStats.playTimer / 2), COLOR_GREY); + GameplayStatsRow("Pause Menu Time:", formatTimestampGameplayStat(gSaveContext.sohStats.pauseTimer / 3), COLOR_GREY); + GameplayStatsRow("Time in scene:", formatTimestampGameplayStat(gSaveContext.sohStats.sceneTimer / 2), COLOR_LIGHT_BLUE); + GameplayStatsRow("Time in room:", formatTimestampGameplayStat(gSaveContext.sohStats.roomTimer / 2), COLOR_LIGHT_BLUE); + } + if (gPlayState != NULL && CVarGetInteger("gGameplayStats.ShowDebugInfo", 0)) { // && display debug info + GameplayStatsRow("play->sceneNum:", formatHexGameplayStat(gPlayState->sceneNum), COLOR_YELLOW); + GameplayStatsRow("gSaveContext.entranceIndex:", formatHexGameplayStat(gSaveContext.entranceIndex), COLOR_YELLOW); + GameplayStatsRow("gSaveContext.cutsceneIndex:", formatHexOnlyGameplayStat(gSaveContext.cutsceneIndex), COLOR_YELLOW); + GameplayStatsRow("play->roomCtx.curRoom.num:", formatIntGameplayStat(gPlayState->roomCtx.curRoom.num), COLOR_YELLOW); + } + ImGui::EndTable(); + ImGui::PopStyleVar(1); +} + +void DrawGameplayStatsTimestampsTab() { + // Set up the array of item timestamps and then sort it chronologically + for (int i = 0; i < TIMESTAMP_MAX; i++) { + strcpy(itemTimestampDisplay[i].name, itemTimestampDisplayName[i]); + itemTimestampDisplay[i].time = gSaveContext.sohStats.itemTimestamp[i]; + itemTimestampDisplay[i].color = itemTimestampDisplayColor[i]; + } + + std::sort(itemTimestampDisplay, itemTimestampDisplay + TIMESTAMP_MAX, compareTimestampInfoByTime); + + ImGui::PushStyleVar(ImGuiStyleVar_CellPadding, { 4.0f, 4.0f }); + ImGui::BeginTable("gameplayStatsTimestamps", 1, ImGuiTableFlags_BordersOuter); + ImGui::TableSetupColumn("stat", ImGuiTableColumnFlags_WidthStretch); + for (int i = 0; i < TIMESTAMP_MAX; i++) { + // To be shown, the entry must have a non-zero time and a string for its display name + if (itemTimestampDisplay[i].time > 0 && strnlen(itemTimestampDisplay[i].name, 21) > 1) { + GameplayStatsRow(itemTimestampDisplay[i].name, formatTimestampGameplayStat(itemTimestampDisplay[i].time), itemTimestampDisplay[i].color); + } + } + ImGui::EndTable(); + ImGui::PopStyleVar(1); + +} + +void DrawGameplayStatsCountsTab() { + u32 enemiesDefeated = 0; + u32 ammoUsed = 0; + u32 buttonPresses = 0; + + // Sum of all enemies defeated + for (int i = COUNT_ENEMIES_DEFEATED_ANUBIS; i <= COUNT_ENEMIES_DEFEATED_WOLFOS; i++) { + if (i == COUNT_ENEMIES_DEFEATED_FLOORMASTER) { + // Special case: You must kill 3 mini Floormasters for it count as one defeated Floormaster + enemiesDefeated += gSaveContext.sohStats.count[i] / 3; + } else { + enemiesDefeated += gSaveContext.sohStats.count[i]; + } + } + // Sum of all ammo used + for (int i = COUNT_AMMO_USED_STICK; i <= COUNT_AMMO_USED_BEAN; i++) { + ammoUsed += gSaveContext.sohStats.count[i]; + } + // Sum of all button presses + for (int i = COUNT_BUTTON_PRESSES_A; i <= COUNT_BUTTON_PRESSES_START; i++) { + buttonPresses += gSaveContext.sohStats.count[i]; + } + + ImGui::PushStyleVar(ImGuiStyleVar_CellPadding, { 4.0f, 4.0f }); + ImGui::BeginTable("gameplayStatsCounts", 1, ImGuiTableFlags_BordersOuter); + ImGui::TableSetupColumn("stat", ImGuiTableColumnFlags_WidthStretch); + GameplayStatsRow("Enemies Defeated:", formatIntGameplayStat(enemiesDefeated)); + if (enemiesDefeated > 0) { + ImGui::TableNextRow(); ImGui::TableNextColumn(); + if (ImGui::TreeNodeEx("Enemy Details...", ImGuiTreeNodeFlags_NoTreePushOnOpen)) { + for (int i = COUNT_ENEMIES_DEFEATED_ANUBIS; i <= COUNT_ENEMIES_DEFEATED_WOLFOS; i++) { + if (i == COUNT_ENEMIES_DEFEATED_FLOORMASTER) { + GameplayStatsRow(countMappings[i], formatIntGameplayStat(gSaveContext.sohStats.count[i] / 3)); + } else { + GameplayStatsRow(countMappings[i], formatIntGameplayStat(gSaveContext.sohStats.count[i])); + } + } + } + } + GameplayStatsRow("Rupees Collected:", formatIntGameplayStat(gSaveContext.sohStats.count[COUNT_RUPEES_COLLECTED])); + UIWidgets::Tooltip("Includes rupees collected with a full wallet."); + GameplayStatsRow("Rupees Spent:", formatIntGameplayStat(gSaveContext.sohStats.count[COUNT_RUPEES_SPENT])); + GameplayStatsRow("Chests Opened:", formatIntGameplayStat(gSaveContext.sohStats.count[COUNT_CHESTS_OPENED])); + GameplayStatsRow("Ammo Used:", formatIntGameplayStat(ammoUsed)); + if (ammoUsed > 0) { + ImGui::TableNextRow(); ImGui::TableNextColumn(); + if (ImGui::TreeNodeEx("Ammo Details...", ImGuiTreeNodeFlags_NoTreePushOnOpen)) { + for (int i = COUNT_AMMO_USED_STICK; i <= COUNT_AMMO_USED_BEAN; i++) { + GameplayStatsRow(countMappings[i], formatIntGameplayStat(gSaveContext.sohStats.count[i])); + } + } + } + GameplayStatsRow("Damage Taken:", formatIntGameplayStat(gSaveContext.sohStats.count[COUNT_DAMAGE_TAKEN])); + GameplayStatsRow("Sword Swings:", formatIntGameplayStat(gSaveContext.sohStats.count[COUNT_SWORD_SWINGS])); + GameplayStatsRow("Steps Taken:", formatIntGameplayStat(gSaveContext.sohStats.count[COUNT_STEPS])); + // If using MM Bunny Hood enhancement, show how long it's been equipped (not counting pause time) + if (CVarGetInteger("gMMBunnyHood", 0) || gSaveContext.sohStats.count[COUNT_TIME_BUNNY_HOOD] > 0) { + GameplayStatsRow("Bunny Hood Time:", formatTimestampGameplayStat(gSaveContext.sohStats.count[COUNT_TIME_BUNNY_HOOD] / 2)); + } + GameplayStatsRow("Rolls:", formatIntGameplayStat(gSaveContext.sohStats.count[COUNT_ROLLS])); + GameplayStatsRow("Bonks:", formatIntGameplayStat(gSaveContext.sohStats.count[COUNT_BONKS])); + GameplayStatsRow("Sidehops:", formatIntGameplayStat(gSaveContext.sohStats.count[COUNT_SIDEHOPS])); + GameplayStatsRow("Backflips:", formatIntGameplayStat(gSaveContext.sohStats.count[COUNT_BACKFLIPS])); + GameplayStatsRow("Ice Traps:", formatIntGameplayStat(gSaveContext.sohStats.count[COUNT_ICE_TRAPS])); + GameplayStatsRow("Pauses:", formatIntGameplayStat(gSaveContext.sohStats.count[COUNT_PAUSES])); + GameplayStatsRow("Pots Smashed:", formatIntGameplayStat(gSaveContext.sohStats.count[COUNT_POTS_BROKEN])); + GameplayStatsRow("Bushes Cut:", formatIntGameplayStat(gSaveContext.sohStats.count[COUNT_BUSHES_CUT])); + GameplayStatsRow("Buttons Pressed:", formatIntGameplayStat(buttonPresses)); + if (buttonPresses > 0) { + ImGui::TableNextRow(); ImGui::TableNextColumn(); + if (ImGui::TreeNodeEx("Buttons...", ImGuiTreeNodeFlags_NoTreePushOnOpen)) { + for (int i = COUNT_BUTTON_PRESSES_A; i <= COUNT_BUTTON_PRESSES_START; i++) { + GameplayStatsRow(countMappings[i], formatIntGameplayStat(gSaveContext.sohStats.count[i])); + } + } + } + ImGui::EndTable(); + ImGui::PopStyleVar(1); +} + +void DrawGameplayStatsBreakdownTab() { + for (int i = 0; i < gSaveContext.sohStats.tsIdx; i++) { + std::string sceneName = ResolveSceneID(gSaveContext.sohStats.sceneTimestamps[i].scene, gSaveContext.sohStats.sceneTimestamps[i].room); + std::string name; + if (CVarGetInteger("gGameplayStats.RoomBreakdown", 0) && gSaveContext.sohStats.sceneTimestamps[i].scene != SCENE_KAKUSIANA) { + name = fmt::format("{:s} Room {:d}", sceneName, gSaveContext.sohStats.sceneTimestamps[i].room); + } else { + name = sceneName; + } + strcpy(sceneTimestampDisplay[i].name, name.c_str()); + sceneTimestampDisplay[i].time = CVarGetInteger("gGameplayStats.RoomBreakdown", 0) ? + gSaveContext.sohStats.sceneTimestamps[i].roomTime : gSaveContext.sohStats.sceneTimestamps[i].sceneTime; + sceneTimestampDisplay[i].color = COLOR_GREY; + sceneTimestampDisplay[i].isRoom = gSaveContext.sohStats.sceneTimestamps[i].isRoom; + } + + ImGui::PushStyleVar(ImGuiStyleVar_CellPadding, { 4.0f, 4.0f }); + ImGui::BeginTable("gameplayStatsCounts", 1, ImGuiTableFlags_BordersOuter); + ImGui::TableSetupColumn("stat", ImGuiTableColumnFlags_WidthStretch); + for (int i = 0; i < gSaveContext.sohStats.tsIdx; i++) { + TimestampInfo tsInfo = sceneTimestampDisplay[i]; + bool canShow = !tsInfo.isRoom || CVarGetInteger("gGameplayStats.RoomBreakdown", 0); + if (tsInfo.time > 0 && strnlen(tsInfo.name, 40) > 1 && canShow) { + GameplayStatsRow(tsInfo.name, formatTimestampGameplayStat(tsInfo.time), tsInfo.color); + } + } + std::string toPass; + if (CVarGetInteger("gGameplayStats.RoomBreakdown", 0) && gSaveContext.sohStats.sceneNum != SCENE_KAKUSIANA) { + toPass = fmt::format("{:s} Room {:d}", ResolveSceneID(gSaveContext.sohStats.sceneNum, gSaveContext.sohStats.roomNum), gSaveContext.sohStats.roomNum); + } else { + toPass = ResolveSceneID(gSaveContext.sohStats.sceneNum, gSaveContext.sohStats.roomNum); + } + GameplayStatsRow(toPass.c_str(), formatTimestampGameplayStat(CURRENT_MODE_TIMER / 2)); + ImGui::EndTable(); + ImGui::PopStyleVar(1); +} + +void DrawGameplayStatsOptionsTab() { + UIWidgets::PaddedEnhancementCheckbox("Show latest timestamps on top", "gGameplayStats.TimestampsReverse"); + UIWidgets::PaddedEnhancementCheckbox("Room Breakdown", "gGameplayStats.RoomBreakdown"); + ImGui::SameLine(); + UIWidgets::InsertHelpHoverText("Allows a more in-depth perspective of time spent in a certain map."); + UIWidgets::PaddedEnhancementCheckbox("RTA Timing on new files", "gGameplayStats.RTATiming"); + ImGui::SameLine(); + UIWidgets::InsertHelpHoverText( + "Timestamps are relative to starting timestamp rather than in game time, usually necessary for races/speedruns.\n\n" + "Starting timestamp is on first non-c-up input after intro cutscene.\n\n" + "NOTE: THIS NEEDS TO BE SET BEFORE CREATING A FILE TO TAKE EFFECT" + ); + UIWidgets::PaddedEnhancementCheckbox("Show additional detail timers", "gGameplayStats.ShowAdditionalTimers"); + UIWidgets::PaddedEnhancementCheckbox("Show Debug Info", "gGameplayStats.ShowDebugInfo"); +} + +void DrawStatsTracker(bool& open) { + if (!open) { + if (CVarGetInteger("gGameplayStatsEnabled", 0)) { + CVarClear("gGameplayStatsEnabled"); + LUS::RequestCvarSaveOnNextTick(); + } + return; + } + + ImGui::SetNextWindowSize(ImVec2(480, 550), ImGuiCond_Appearing); + if (!ImGui::Begin("Gameplay Stats", &open, ImGuiWindowFlags_NoFocusOnAppearing)) { + ImGui::End(); + return; + } + + DrawGameplayStatsHeader(); + + if (ImGui::BeginTabBar("Stats", ImGuiTabBarFlags_NoCloseWithMiddleMouseButton)) { + if (ImGui::BeginTabItem("Timestamps")) { + DrawGameplayStatsTimestampsTab(); + ImGui::EndTabItem(); + } + if (ImGui::BeginTabItem("Counts")) { + DrawGameplayStatsCountsTab(); + ImGui::EndTabItem(); + } + if (ImGui::BeginTabItem("Breakdown")) { + DrawGameplayStatsBreakdownTab(); + ImGui::EndTabItem(); + } + if (ImGui::BeginTabItem("Options")) { + DrawGameplayStatsOptionsTab(); + ImGui::EndTabItem(); + } + ImGui::EndTabBar(); + } + + ImGui::Text("Note: Gameplay stats are saved to the current file and will be\nlost if you quit without saving."); + + ImGui::End(); +} void InitStats(bool isDebug) { gSaveContext.sohStats.heartPieces = isDebug ? 8 : 0; gSaveContext.sohStats.heartContainers = isDebug ? 8 : 0; for (int dungeon = 0; dungeon < ARRAY_COUNT(gSaveContext.sohStats.dungeonKeys); dungeon++) { gSaveContext.sohStats.dungeonKeys[dungeon] = isDebug ? 8 : 0; } + gSaveContext.sohStats.rtaTiming = CVarGetInteger("gGameplayStats.RTATiming", 0); + gSaveContext.sohStats.fileCreatedAt = 0; gSaveContext.sohStats.playTimer = 0; gSaveContext.sohStats.pauseTimer = 0; for (int timestamp = 0; timestamp < ARRAY_COUNT(gSaveContext.sohStats.itemTimestamp); timestamp++) { @@ -313,366 +679,13 @@ void InitStats(bool isDebug) { gSaveContext.sohStats.locationsSkipped[rc] = 0; } - strncpy(gSaveContext.sohStats.buildVersion, (const char*)gBuildVersion, - sizeof(gSaveContext.sohStats.buildVersion) - 1); + strncpy(gSaveContext.sohStats.buildVersion, (const char*) gBuildVersion, sizeof(gSaveContext.sohStats.buildVersion) - 1); gSaveContext.sohStats.buildVersion[sizeof(gSaveContext.sohStats.buildVersion) - 1] = 0; gSaveContext.sohStats.buildVersionMajor = gBuildVersionMajor; gSaveContext.sohStats.buildVersionMinor = gBuildVersionMinor; gSaveContext.sohStats.buildVersionPatch = gBuildVersionPatch; } -void SortChronological(TimestampInfo* arr, size_t len) { - TimestampInfo temp; - for (int i = 0; i < len; i++) { - for (int j = 0; j + 1 < len - i; j++) { - if (arr[j].time > arr[j + 1].time) { - temp = arr[j]; - arr[j] = arr[j + 1]; - arr[j + 1] = temp; - } - } - } -} - -void DisplayStat(const char* text, uint32_t value) { - - ImGui::Text(text); - ImGui::SameLine(); - ImGui::Text("%7u", value); -} - -void DisplayStatIfNonZero(const char* text, uint32_t value) { - if (value > 0) { - DisplayStat(text, value); - } - return; -} - -std::string ResolveSceneID(int sceneID, int roomID){ - std::string scene = ""; - if (sceneID == SCENE_KAKUSIANA) { - switch (roomID) { - case 0: - scene = "Generic Grotto"; - break; - case 1: - scene = "Lake Hylia Scrub Grotto"; - break; - case 2: - scene = "Redead Grotto"; - break; - case 3: - scene = "Cow Grotto"; - break; - case 4: - scene = "Scrub Trio"; - break; - case 5: - scene = "Flooded Grotto"; - break; - case 6: - scene = "Scrub Duo (Upgrade)"; - break; - case 7: - scene = "Wolfos Grotto"; - break; - case 8: - scene = "Hyrule Castle Storms Grotto"; - break; - case 9: - scene = "Scrub Duo"; - break; - case 10: - scene = "Tektite Grotto"; - break; - case 11: - scene = "Forest Stage"; - break; - case 12: - scene = "Webbed Grotto"; - break; - case 13: - scene = "Big Skulltula Grotto"; - break; - default: - scene = "???"; - }; - } else if (sceneID == SCENE_HAKASITARELAY) { - //Only the last room of Dampe's Grave (rm 6) is considered the windmill - scene = roomID == 6 ? "Windmill" : "Dampe's Grave"; - } else if (sceneID < SCENE_ID_MAX) { - scene = sceneMappings[sceneID]; - } else { - scene = "???"; - } - return scene; -} - -void DrawStatsTracker(bool& open) { - if (!open) { - if (CVarGetInteger("gGameplayStatsEnabled", 0)) { - CVarClear("gGameplayStatsEnabled"); - LUS::RequestCvarSaveOnNextTick(); - } - return; - } - - ImGui::SetNextWindowSize(ImVec2(480, 550), ImGuiCond_Appearing); - if (!ImGui::Begin("Gameplay Stats", &open, ImGuiWindowFlags_NoFocusOnAppearing)) { - ImGui::End(); - return; - } - u32 totalTimer = GAMEPLAYSTAT_TOTAL_TIME; - u32 enemiesDefeated = 0; - u32 ammoUsed = 0; - u32 buttonPresses = 0; - - // Sum of all enemies defeated - for (int i = COUNT_ENEMIES_DEFEATED_ANUBIS; i <= COUNT_ENEMIES_DEFEATED_WOLFOS; i++) { - if (i == COUNT_ENEMIES_DEFEATED_FLOORMASTER) { - // Special case: You must kill 3 mini Floormasters for it count as one defeated Floormaster - enemiesDefeated += gSaveContext.sohStats.count[i] / 3; - } else { - enemiesDefeated += gSaveContext.sohStats.count[i]; - } - } - // Sum of all ammo used - for (int i = COUNT_AMMO_USED_STICK; i <= COUNT_AMMO_USED_BEAN; i++) { - ammoUsed += gSaveContext.sohStats.count[i]; - } - // Sum of all button presses - for (int i = COUNT_BUTTON_PRESSES_A; i <= COUNT_BUTTON_PRESSES_START; i++) { - buttonPresses += gSaveContext.sohStats.count[i]; - } - // Set up the array of item timestamps and then sort it chronologically - for (int i = 0; i < TIMESTAMP_MAX; i++) { - strcpy(itemTimestampDisplay[i].name, itemTimestampDisplayName[i]); - itemTimestampDisplay[i].time = gSaveContext.sohStats.itemTimestamp[i]; - itemTimestampDisplay[i].color = itemTimestampDisplayColor[i]; - } - - for (int i = 0; i < gSaveContext.sohStats.tsIdx; i++) { - std::string sceneName = ResolveSceneID(gSaveContext.sohStats.sceneTimestamps[i].scene, gSaveContext.sohStats.sceneTimestamps[i].room); - std::string name; - if (CVarGetInteger("gGameplayStatRoomBreakdown", 0) && gSaveContext.sohStats.sceneTimestamps[i].scene != SCENE_KAKUSIANA) { - name = fmt::format("{:s} Room {:d}", sceneName, gSaveContext.sohStats.sceneTimestamps[i].room); - } else { - name = sceneName; - } - strcpy(sceneTimestampDisplay[i].name, name.c_str()); - sceneTimestampDisplay[i].time = CVarGetInteger("gGameplayStatRoomBreakdown", 0) ? - gSaveContext.sohStats.sceneTimestamps[i].roomTime : gSaveContext.sohStats.sceneTimestamps[i].sceneTime; - sceneTimestampDisplay[i].color = COLOR_GREY; - sceneTimestampDisplay[i].isRoom = gSaveContext.sohStats.sceneTimestamps[i].isRoom; - } - - SortChronological(itemTimestampDisplay, sizeof(itemTimestampDisplay) / sizeof(itemTimestampDisplay[0])); - - - // Begin drawing the table and showing the stats - - ImGui::PushStyleVar(ImGuiStyleVar_CellPadding, { 8.0f, 8.0f }); - ImGui::BeginTable("timers", 1, ImGuiTableFlags_BordersH | ImGuiTableFlags_BordersV); - ImGui::TableSetupColumn("Timers", ImGuiTableColumnFlags_WidthStretch, 200.0f); - ImGui::TableNextColumn(); - - DisplayTimeHHMMSS(totalTimer, "Total Game Time: ", COLOR_WHITE); - UIWidgets::Tooltip("Timer accuracy may be affected by game performance and loading."); - DisplayTimeHHMMSS(gSaveContext.sohStats.playTimer / 2, "Gameplay Time: ", COLOR_WHITE); - UIWidgets::Tooltip("Timer accuracy may be affected by game performance and loading."); - DisplayTimeHHMMSS(gSaveContext.sohStats.pauseTimer / 3, "Pause Menu Time: ", COLOR_WHITE); - DisplayTimeHHMMSS(gSaveContext.sohStats.sceneTimer / 2, "Time in scene: ", COLOR_LIGHT_BLUE); - UIWidgets::Tooltip("Timer accuracy may be affected by game performance and loading."); - DisplayTimeHHMMSS(gSaveContext.sohStats.roomTimer / 2, "Time in room: ", COLOR_LIGHT_BLUE); - UIWidgets::Tooltip("Timer accuracy may be affected by game performance and loading."); - ImGui::Text("Current room: %d", gSaveContext.sohStats.roomNum); - - ImGui::PopStyleVar(1); - ImGui::EndTable(); - - ImGui::PushStyleVar(ImGuiStyleVar_CellPadding, { 8.0f, 8.0f }); - if (ImGui::BeginTabBar("Stats", ImGuiTabBarFlags_NoCloseWithMiddleMouseButton)) { - if (ImGui::BeginTabItem("Timestamps")) { - // Display chronological timestamps of items obtained and bosses defeated - for (int i = 0; i < TIMESTAMP_MAX; i++) { - // To be shown, the entry must have a non-zero time and a string for its display name - if (itemTimestampDisplay[i].time > 0 && strnlen(itemTimestampDisplay[i].name, 21) > 1) { - DisplayTimeHHMMSS(itemTimestampDisplay[i].time, itemTimestampDisplay[i].name, itemTimestampDisplay[i].color); - } - } - ImGui::EndTabItem(); - } - if (ImGui::BeginTabItem("Counts")) { - DisplayStat("Enemies Defeated: ", enemiesDefeated); - // Show breakdown of enemies defeated in a tree. Only show counts for enemies if they've been defeated at least once. - if (enemiesDefeated > 0) { - if (ImGui::TreeNode("Enemy Details...")) { - DisplayStatIfNonZero("Anubis: ", gSaveContext.sohStats.count[COUNT_ENEMIES_DEFEATED_ANUBIS]); - DisplayStatIfNonZero("Armos: ", gSaveContext.sohStats.count[COUNT_ENEMIES_DEFEATED_ARMOS]); - DisplayStatIfNonZero("Arwing: ", gSaveContext.sohStats.count[COUNT_ENEMIES_DEFEATED_ARWING]); - DisplayStatIfNonZero("Bari: ", gSaveContext.sohStats.count[COUNT_ENEMIES_DEFEATED_BARI]); - DisplayStatIfNonZero("Biri: ", gSaveContext.sohStats.count[COUNT_ENEMIES_DEFEATED_BIRI]); - DisplayStatIfNonZero("Beamos: ", gSaveContext.sohStats.count[COUNT_ENEMIES_DEFEATED_BEAMOS]); - DisplayStatIfNonZero("Big Octo: ", gSaveContext.sohStats.count[COUNT_ENEMIES_DEFEATED_BIG_OCTO]); - DisplayStatIfNonZero("Bubble (Blue): ", gSaveContext.sohStats.count[COUNT_ENEMIES_DEFEATED_BUBBLE_BLUE]); - DisplayStatIfNonZero("Bubble (Green): ", gSaveContext.sohStats.count[COUNT_ENEMIES_DEFEATED_BUBBLE_GREEN]); - DisplayStatIfNonZero("Bubble (Red): ", gSaveContext.sohStats.count[COUNT_ENEMIES_DEFEATED_BUBBLE_RED]); - DisplayStatIfNonZero("Bubble (White): ", gSaveContext.sohStats.count[COUNT_ENEMIES_DEFEATED_BUBBLE_WHITE]); - DisplayStatIfNonZero("Business Scrub: ", gSaveContext.sohStats.count[COUNT_ENEMIES_DEFEATED_BUSINESS_SCRUB]); - DisplayStatIfNonZero("Dark Link: ", gSaveContext.sohStats.count[COUNT_ENEMIES_DEFEATED_DARK_LINK]); - DisplayStatIfNonZero("Dead Hand: ", gSaveContext.sohStats.count[COUNT_ENEMIES_DEFEATED_DEAD_HAND]); - DisplayStatIfNonZero("Deku Baba: ", gSaveContext.sohStats.count[COUNT_ENEMIES_DEFEATED_DEKU_BABA]); - DisplayStatIfNonZero("Deku Baba (Big): ", gSaveContext.sohStats.count[COUNT_ENEMIES_DEFEATED_DEKU_BABA_BIG]); - DisplayStatIfNonZero("Deku Scrub: ", gSaveContext.sohStats.count[COUNT_ENEMIES_DEFEATED_DEKU_SCRUB]); - DisplayStatIfNonZero("Dinolfos: ", gSaveContext.sohStats.count[COUNT_ENEMIES_DEFEATED_DINOLFOS]); - DisplayStatIfNonZero("Dodongo: ", gSaveContext.sohStats.count[COUNT_ENEMIES_DEFEATED_DODONGO]); - DisplayStatIfNonZero("Dodongo (Baby): ", gSaveContext.sohStats.count[COUNT_ENEMIES_DEFEATED_DODONGO_BABY]); - DisplayStatIfNonZero("Door Mimic: ", gSaveContext.sohStats.count[COUNT_ENEMIES_DEFEATED_DOOR_TRAP]); - DisplayStatIfNonZero("Flare Dancer: ", gSaveContext.sohStats.count[COUNT_ENEMIES_DEFEATED_FLARE_DANCER]); - DisplayStatIfNonZero("Floormaster: ", gSaveContext.sohStats.count[COUNT_ENEMIES_DEFEATED_FLOORMASTER]/3); - DisplayStatIfNonZero("Flying Floor Tile: ", gSaveContext.sohStats.count[COUNT_ENEMIES_DEFEATED_FLOOR_TILE]); - DisplayStatIfNonZero("Flying Pot: ", gSaveContext.sohStats.count[COUNT_ENEMIES_DEFEATED_FLYING_POT]); - DisplayStatIfNonZero("Freezard: ", gSaveContext.sohStats.count[COUNT_ENEMIES_DEFEATED_FREEZARD]); - DisplayStatIfNonZero("Gerudo Thief: ", gSaveContext.sohStats.count[COUNT_ENEMIES_DEFEATED_GERUDO_THIEF]); - DisplayStatIfNonZero("Gibdo: ", gSaveContext.sohStats.count[COUNT_ENEMIES_DEFEATED_GIBDO]); - DisplayStatIfNonZero("Gohma Larva: ", gSaveContext.sohStats.count[COUNT_ENEMIES_DEFEATED_GOHMA_LARVA]); - DisplayStatIfNonZero("Guay: ", gSaveContext.sohStats.count[COUNT_ENEMIES_DEFEATED_GUAY]); - DisplayStatIfNonZero("Iron Knuckle: ", gSaveContext.sohStats.count[COUNT_ENEMIES_DEFEATED_IRON_KNUCKLE]); - DisplayStatIfNonZero("Iron Knuckle (Nab): ", gSaveContext.sohStats.count[COUNT_ENEMIES_DEFEATED_IRON_KNUCKLE_NABOORU]); - DisplayStatIfNonZero("Keese: ", gSaveContext.sohStats.count[COUNT_ENEMIES_DEFEATED_KEESE]); - DisplayStatIfNonZero("Keese (Fire): ", gSaveContext.sohStats.count[COUNT_ENEMIES_DEFEATED_KEESE_FIRE]); - DisplayStatIfNonZero("Keese (Ice): ", gSaveContext.sohStats.count[COUNT_ENEMIES_DEFEATED_KEESE_ICE]); - DisplayStatIfNonZero("Leever: ", gSaveContext.sohStats.count[COUNT_ENEMIES_DEFEATED_LEEVER]); - DisplayStatIfNonZero("Leever (Big): ", gSaveContext.sohStats.count[COUNT_ENEMIES_DEFEATED_LEEVER_BIG]); - DisplayStatIfNonZero("Like-Like: ", gSaveContext.sohStats.count[COUNT_ENEMIES_DEFEATED_LIKE_LIKE]); - DisplayStatIfNonZero("Lizalfos: ", gSaveContext.sohStats.count[COUNT_ENEMIES_DEFEATED_LIZALFOS]); - DisplayStatIfNonZero("Mad Scrub: ", gSaveContext.sohStats.count[COUNT_ENEMIES_DEFEATED_MAD_SCRUB]); - DisplayStatIfNonZero("Moblin: ", gSaveContext.sohStats.count[COUNT_ENEMIES_DEFEATED_MOBLIN]); - DisplayStatIfNonZero("Moblin (Club): ", gSaveContext.sohStats.count[COUNT_ENEMIES_DEFEATED_MOBLIN_CLUB]); - DisplayStatIfNonZero("Octorok: ", gSaveContext.sohStats.count[COUNT_ENEMIES_DEFEATED_OCTOROK]); - DisplayStatIfNonZero("Parasitic Tentacle: ", gSaveContext.sohStats.count[COUNT_ENEMIES_DEFEATED_PARASITIC_TENTACLE]); - DisplayStatIfNonZero("Peahat: ", gSaveContext.sohStats.count[COUNT_ENEMIES_DEFEATED_PEAHAT]); - DisplayStatIfNonZero("Peahat Larva: ", gSaveContext.sohStats.count[COUNT_ENEMIES_DEFEATED_PEAHAT_LARVA]); - DisplayStatIfNonZero("Poe: ", gSaveContext.sohStats.count[COUNT_ENEMIES_DEFEATED_POE]); - DisplayStatIfNonZero("Poe (Big): ", gSaveContext.sohStats.count[COUNT_ENEMIES_DEFEATED_POE_BIG]); - DisplayStatIfNonZero("Poe (Composer): ", gSaveContext.sohStats.count[COUNT_ENEMIES_DEFEATED_POE_COMPOSER]); - DisplayStatIfNonZero("Poe Sisters: ", gSaveContext.sohStats.count[COUNT_ENEMIES_DEFEATED_POE_SISTERS]); - DisplayStatIfNonZero("Redead: ", gSaveContext.sohStats.count[COUNT_ENEMIES_DEFEATED_REDEAD]); - DisplayStatIfNonZero("Shabom: ", gSaveContext.sohStats.count[COUNT_ENEMIES_DEFEATED_SHABOM]); - DisplayStatIfNonZero("Shellblade: ", gSaveContext.sohStats.count[COUNT_ENEMIES_DEFEATED_SHELLBLADE]); - DisplayStatIfNonZero("Skull Kid: ", gSaveContext.sohStats.count[COUNT_ENEMIES_DEFEATED_SKULL_KID]); - DisplayStatIfNonZero("Skulltula: ", gSaveContext.sohStats.count[COUNT_ENEMIES_DEFEATED_SKULLTULA]); - DisplayStatIfNonZero("Skulltula (Big): ", gSaveContext.sohStats.count[COUNT_ENEMIES_DEFEATED_SKULLTULA_BIG]); - DisplayStatIfNonZero("Skulltula (Gold): ", gSaveContext.sohStats.count[COUNT_ENEMIES_DEFEATED_SKULLTULA_GOLD]); - DisplayStatIfNonZero("Skullwalltula: ", gSaveContext.sohStats.count[COUNT_ENEMIES_DEFEATED_SKULLWALLTULA]); - DisplayStatIfNonZero("Spike: ", gSaveContext.sohStats.count[COUNT_ENEMIES_DEFEATED_SPIKE]); - DisplayStatIfNonZero("Stalchild: ", gSaveContext.sohStats.count[COUNT_ENEMIES_DEFEATED_STALCHILD]); - DisplayStatIfNonZero("Stalfos: ", gSaveContext.sohStats.count[COUNT_ENEMIES_DEFEATED_STALFOS]); - DisplayStatIfNonZero("Stinger: ", gSaveContext.sohStats.count[COUNT_ENEMIES_DEFEATED_STINGER]); - DisplayStatIfNonZero("Tailpasaran: ", gSaveContext.sohStats.count[COUNT_ENEMIES_DEFEATED_TAILPASARAN]); - DisplayStatIfNonZero("Tektite (Blue): ", gSaveContext.sohStats.count[COUNT_ENEMIES_DEFEATED_TEKTITE_BLUE]); - DisplayStatIfNonZero("Tektite (Red): ", gSaveContext.sohStats.count[COUNT_ENEMIES_DEFEATED_TEKTITE_RED]); - DisplayStatIfNonZero("Torch Slug: ", gSaveContext.sohStats.count[COUNT_ENEMIES_DEFEATED_TORCH_SLUG]); - DisplayStatIfNonZero("Wallmaster: ", gSaveContext.sohStats.count[COUNT_ENEMIES_DEFEATED_WALLMASTER]); - DisplayStatIfNonZero("Withered Deku Baba: ", gSaveContext.sohStats.count[COUNT_ENEMIES_DEFEATED_WITHERED_DEKU_BABA]); - DisplayStatIfNonZero("Wolfos: ", gSaveContext.sohStats.count[COUNT_ENEMIES_DEFEATED_WOLFOS]); - DisplayStatIfNonZero("Wolfos (White): ", gSaveContext.sohStats.count[COUNT_ENEMIES_DEFEATED_WOLFOS_WHITE]); - ImGui::NewLine(); - ImGui::TreePop(); - } - } - - DisplayStat("Rupees Collected: ", gSaveContext.sohStats.count[COUNT_RUPEES_COLLECTED]); - UIWidgets::Tooltip("Includes rupees collected with a full wallet."); - DisplayStat("Rupees Spent: ", gSaveContext.sohStats.count[COUNT_RUPEES_SPENT]); - DisplayStat("Chests Opened: ", gSaveContext.sohStats.count[COUNT_CHESTS_OPENED]); - - DisplayStat("Ammo Used: ", ammoUsed); - // Show breakdown of ammo used in a collapsible tree. Only show ammo types if they've been used at least once. - if (ammoUsed > 0) { - if (ImGui::TreeNode("Ammo Details...")) { - DisplayStatIfNonZero("Deku Sticks: ", gSaveContext.sohStats.count[COUNT_AMMO_USED_STICK]); - DisplayStatIfNonZero("Deku Nuts: ", gSaveContext.sohStats.count[COUNT_AMMO_USED_NUT]); - DisplayStatIfNonZero("Deku Seeds: ", gSaveContext.sohStats.count[COUNT_AMMO_USED_SEED]); - DisplayStatIfNonZero("Bombs: ", gSaveContext.sohStats.count[COUNT_AMMO_USED_BOMB]); - DisplayStatIfNonZero("Bombchus: ", gSaveContext.sohStats.count[COUNT_AMMO_USED_BOMBCHU]); - DisplayStatIfNonZero("Arrows: ", gSaveContext.sohStats.count[COUNT_AMMO_USED_ARROW]); - DisplayStatIfNonZero("Beans: ", gSaveContext.sohStats.count[COUNT_AMMO_USED_BEAN]); - ImGui::NewLine(); - ImGui::TreePop(); - } - } - DisplayStat("Damage Taken: ", gSaveContext.sohStats.count[COUNT_DAMAGE_TAKEN]); - DisplayStat("Sword Swings: ", gSaveContext.sohStats.count[COUNT_SWORD_SWINGS]); - DisplayStat("Steps Taken: ", gSaveContext.sohStats.count[COUNT_STEPS]); - // If using MM Bunny Hood enhancement, show how long it's been equipped (not counting pause time) - if (CVarGetInteger("gMMBunnyHood", 0) || gSaveContext.sohStats.count[COUNT_TIME_BUNNY_HOOD] > 0) { - DisplayTimeHHMMSS(gSaveContext.sohStats.count[COUNT_TIME_BUNNY_HOOD] / 2, "Bunny Hood Time: ", COLOR_WHITE); - } - DisplayStat("Rolls: ", gSaveContext.sohStats.count[COUNT_ROLLS]); - DisplayStat("Bonks: ", gSaveContext.sohStats.count[COUNT_BONKS]); - DisplayStat("Sidehops: ", gSaveContext.sohStats.count[COUNT_SIDEHOPS]); - DisplayStat("Backflips: ", gSaveContext.sohStats.count[COUNT_BACKFLIPS]); - DisplayStat("Ice Traps: ", gSaveContext.sohStats.count[COUNT_ICE_TRAPS]); - DisplayStat("Pauses: ", gSaveContext.sohStats.count[COUNT_PAUSES]); - DisplayStat("Pots Smashed: ", gSaveContext.sohStats.count[COUNT_POTS_BROKEN]); - DisplayStat("Bushes Cut: ", gSaveContext.sohStats.count[COUNT_BUSHES_CUT]); - DisplayStat("Buttons Pressed: ", buttonPresses); - // Show breakdown of ammo used in a collapsible tree. Only show ammo types if they've been used at least once. - if (buttonPresses > 0) { - if (ImGui::TreeNode("Buttons...")) { - DisplayStatIfNonZero("A: ", gSaveContext.sohStats.count[COUNT_BUTTON_PRESSES_A]); - DisplayStatIfNonZero("B: ", gSaveContext.sohStats.count[COUNT_BUTTON_PRESSES_B]); - DisplayStatIfNonZero("L: ", gSaveContext.sohStats.count[COUNT_BUTTON_PRESSES_L]); - DisplayStatIfNonZero("R: ", gSaveContext.sohStats.count[COUNT_BUTTON_PRESSES_R]); - DisplayStatIfNonZero("Z: ", gSaveContext.sohStats.count[COUNT_BUTTON_PRESSES_Z]); - DisplayStatIfNonZero("C-Up: ", gSaveContext.sohStats.count[COUNT_BUTTON_PRESSES_CUP]); - DisplayStatIfNonZero("C-Right: ", gSaveContext.sohStats.count[COUNT_BUTTON_PRESSES_CRIGHT]); - DisplayStatIfNonZero("C-Down: ", gSaveContext.sohStats.count[COUNT_BUTTON_PRESSES_CDOWN]); - DisplayStatIfNonZero("C-Left: ", gSaveContext.sohStats.count[COUNT_BUTTON_PRESSES_CLEFT]); - DisplayStatIfNonZero("D-Up: ", gSaveContext.sohStats.count[COUNT_BUTTON_PRESSES_DUP]); - DisplayStatIfNonZero("D-Right: ", gSaveContext.sohStats.count[COUNT_BUTTON_PRESSES_DRIGHT]); - DisplayStatIfNonZero("D-Down: ", gSaveContext.sohStats.count[COUNT_BUTTON_PRESSES_DDOWN]); - DisplayStatIfNonZero("D-Left: ", gSaveContext.sohStats.count[COUNT_BUTTON_PRESSES_DLEFT]); - DisplayStatIfNonZero("Start: ", gSaveContext.sohStats.count[COUNT_BUTTON_PRESSES_START]); - ImGui::NewLine(); - ImGui::TreePop(); - } - } - ImGui::EndTabItem(); - } - if (ImGui::BeginTabItem("Breakdown")) { - UIWidgets::PaddedEnhancementCheckbox("Room Breakdown", "gGameplayStatRoomBreakdown"); - ImGui::SameLine(); - UIWidgets::InsertHelpHoverText("Allows a more in-depth perspective of time spent in a certain map."); - if (gPlayState == NULL) { - ImGui::Text("Waiting for file load..."); - } else { - for (int i = 0; i < gSaveContext.sohStats.tsIdx; i++) { - TimestampInfo tsInfo = sceneTimestampDisplay[i]; - bool canShow = !tsInfo.isRoom || CVarGetInteger("gGameplayStatRoomBreakdown", 0); - if (tsInfo.time > 0 && strnlen(tsInfo.name, 40) > 1 && canShow) { - DisplayTimeHHMMSS(tsInfo.time, tsInfo.name, tsInfo.color); - } - } - std::string toPass; - if (CVarGetInteger("gGameplayStatRoomBreakdown", 0) && gSaveContext.sohStats.sceneNum != SCENE_KAKUSIANA) { - toPass = fmt::format("{:s} Room {:d}", ResolveSceneID(gSaveContext.sohStats.sceneNum, gSaveContext.sohStats.roomNum), gSaveContext.sohStats.roomNum); - } else { - toPass = ResolveSceneID(gSaveContext.sohStats.sceneNum, gSaveContext.sohStats.roomNum); - } - DisplayTimeHHMMSS(CURRENT_MODE_TIMER / 2, toPass.c_str(), COLOR_WHITE); - } - ImGui::EndTabItem(); - } - ImGui::EndTabBar(); - } - ImGui::PopStyleVar(1); - ImGui::Text("Note: Gameplay stats are saved to the current file and will be\nlost if you quit without saving."); - - ImGui::End(); -} - // Entries listed here will have a timestamp shown in the stat window void SetupDisplayNames() { // To add a timestamp for an item or event, add it to this list and ensure @@ -765,6 +778,7 @@ void SetupDisplayNames() { strcpy(itemTimestampDisplayName[TIMESTAMP_DEFEAT_TWINROVA], "Twinrova Defeated: "); strcpy(itemTimestampDisplayName[TIMESTAMP_DEFEAT_GANONDORF], "Ganondorf Defeated: "); strcpy(itemTimestampDisplayName[TIMESTAMP_DEFEAT_GANON], "Ganon Defeated: "); + strcpy(itemTimestampDisplayName[TIMESTAMP_FOUND_GREG], "Greg Found: "); } void SetupDisplayColors() { @@ -817,7 +831,7 @@ void SetupDisplayColors() { } extern "C" void InitStatTracker() { - LUS::AddWindow("Enhancements", "Gameplay Stats", DrawStatsTracker, CVarGetInteger("gGameplayStatsEnabled", 0)); + LUS::AddWindow("Enhancements", "Gameplay Stats", DrawStatsTracker, CVarGetInteger("gGameplayStats.Enabled", 0)); SetupDisplayNames(); SetupDisplayColors(); SaveManager::Instance->AddLoadFunction("sohStats", 1, LoadStatsVersion1); diff --git a/soh/soh/Enhancements/gameplaystats.h b/soh/soh/Enhancements/gameplaystats.h index d44056970..49dbe2296 100644 --- a/soh/soh/Enhancements/gameplaystats.h +++ b/soh/soh/Enhancements/gameplaystats.h @@ -1,10 +1,18 @@ #pragma once -// Total gameplay time is tracked in tenths of seconds -// I.E. game time counts frames at 20fps/2, pause time counts frames at 30fps/3 -// Frame counts in z_play.c and z_kaleido_scope_call.c -#define GAMEPLAYSTAT_TOTAL_TIME (gSaveContext.sohStats.playTimer / 2 + gSaveContext.sohStats.pauseTimer / 3) -#define CURRENT_MODE_TIMER (CVarGetInteger("gGameplayStatRoomBreakdown", 0) ?\ +// When using RTA timing + // get the diff since the save was created, + // unless the game is complete in which we use the defeated ganon timestamp +// When not using RTA timing + // Total gameplay time is tracked in tenths of seconds + // I.E. game time counts frames at 20fps/2, pause time counts frames at 30fps/3 + // Frame counts in z_play.c and z_kaleido_scope_call.c +#define GAMEPLAYSTAT_TOTAL_TIME (gSaveContext.sohStats.rtaTiming ?\ + (!gSaveContext.sohStats.gameComplete ?\ + (!gSaveContext.sohStats.fileCreatedAt ? 0 : ((GetUnixTimestamp() - gSaveContext.sohStats.fileCreatedAt) / 100)) :\ + (gSaveContext.sohStats.itemTimestamp[TIMESTAMP_DEFEAT_GANON])) :\ + (gSaveContext.sohStats.playTimer / 2 + gSaveContext.sohStats.pauseTimer / 3)) +#define CURRENT_MODE_TIMER (CVarGetInteger("gGameplayStats.RoomBreakdown", 0) ?\ gSaveContext.sohStats.roomTimer :\ gSaveContext.sohStats.sceneTimer) diff --git a/soh/soh/Enhancements/mods.cpp b/soh/soh/Enhancements/mods.cpp index 94d8853db..36ccef505 100644 --- a/soh/soh/Enhancements/mods.cpp +++ b/soh/soh/Enhancements/mods.cpp @@ -486,6 +486,24 @@ void RegisterBonkDamage() { }); } +void UpdateDirtPathFixState(int32_t sceneNum) { + switch (sceneNum) { + case SCENE_SPOT00: + case SCENE_SPOT04: + case SCENE_SPOT15: + CVarSetInteger("gDirtPathFix", CVarGetInteger("gSceneSpecificDirtPathFix", 0)); + return; + default: + CVarClear("gDirtPathFix"); + } +} + +void RegisterMenuPathFix() { + GameInteractor::Instance->RegisterGameHook([](int32_t sceneNum) { + UpdateDirtPathFixState(sceneNum); + }); +} + void InitMods() { RegisterTTS(); RegisterInfiniteMoney(); @@ -504,4 +522,5 @@ void InitMods() { RegisterRupeeDash(); RegisterHyperBosses(); RegisterBonkDamage(); + RegisterMenuPathFix(); } diff --git a/soh/soh/Enhancements/mods.h b/soh/soh/Enhancements/mods.h index 99f547d3a..16effa712 100644 --- a/soh/soh/Enhancements/mods.h +++ b/soh/soh/Enhancements/mods.h @@ -1,3 +1,5 @@ +#include + #ifndef MODS_H #define MODS_H @@ -5,6 +7,7 @@ extern "C" { #endif +void UpdateDirtPathFixState(int32_t sceneNum); void InitMods(); #ifdef __cplusplus diff --git a/soh/soh/Enhancements/randomizer/3drando/menu.cpp b/soh/soh/Enhancements/randomizer/3drando/menu.cpp index de6c20be0..c2fd5061d 100644 --- a/soh/soh/Enhancements/randomizer/3drando/menu.cpp +++ b/soh/soh/Enhancements/randomizer/3drando/menu.cpp @@ -518,7 +518,7 @@ std::string GenerateRandomizer(std::unordered_map srand(time(NULL)); // if a blank seed was entered, make a random one if (seedString.empty()) { - Settings::seed = rand() & 0xFFFFFFFF; + seedString = std::to_string(rand() % 0xFFFFFFFF); } else if (seedString.rfind("seed_testing_count", 0) == 0 && seedString.length() > 18) { int count; try { @@ -530,17 +530,12 @@ std::string GenerateRandomizer(std::unordered_map } Playthrough::Playthrough_Repeat(cvarSettings, excludedLocations, count); return ""; - } else { - try { - uint32_t seedHash = boost::hash_32{}(seedString); - int seed = seedHash & 0xFFFFFFFF; - Settings::seed = seed; - Settings::seedString = seedString; - } catch (...) { - return ""; - } } + Settings::seedString = seedString; + uint32_t seedHash = boost::hash_32{}(Settings::seedString); + Settings::seed = seedHash & 0xFFFFFFFF; + int ret = Playthrough::Playthrough_Init(Settings::seed, cvarSettings, excludedLocations); if (ret < 0) { if (ret == -1) { // Failed to generate after 5 tries diff --git a/soh/soh/Enhancements/randomizer/3drando/playthrough.cpp b/soh/soh/Enhancements/randomizer/3drando/playthrough.cpp index 67ccb5276..dbcb93e42 100644 --- a/soh/soh/Enhancements/randomizer/3drando/playthrough.cpp +++ b/soh/soh/Enhancements/randomizer/3drando/playthrough.cpp @@ -87,8 +87,9 @@ int Playthrough_Repeat(std::unordered_map cvarSet printf("\x1b[0;0HGENERATING %d SEEDS", count); uint32_t repeatedSeed = 0; for (int i = 0; i < count; i++) { - repeatedSeed = rand() % 0xFFFFFFFF; - Settings::seed = repeatedSeed; + Settings::seedString = std::to_string(rand() % 0xFFFFFFFF); + repeatedSeed = boost::hash_32{}(Settings::seedString); + Settings::seed = repeatedSeed % 0xFFFFFFFF; CitraPrint("testing seed: " + std::to_string(Settings::seed)); ClearProgress(); Playthrough_Init(Settings::seed, cvarSettings, excludedLocations); diff --git a/soh/soh/Enhancements/randomizer/randomizer.cpp b/soh/soh/Enhancements/randomizer/randomizer.cpp index dc03b070e..24efabf1a 100644 --- a/soh/soh/Enhancements/randomizer/randomizer.cpp +++ b/soh/soh/Enhancements/randomizer/randomizer.cpp @@ -3149,8 +3149,8 @@ void DrawRandoEditor(bool& open) { } UIWidgets::Spacer(0); - if (ImGui::Button("Generate Randomizer")) { - GenerateRandomizer(CVarGetInteger("gRandoManualSeedEntry", 0) ? seedString : std::to_string(rand() & 0xFFFFFFFF).c_str()); + if (ImGui::Button("Generate Randomizer")) { + GenerateRandomizer(CVarGetInteger("gRandoManualSeedEntry", 0) ? seedString : ""); } UIWidgets::Spacer(0); diff --git a/soh/soh/Extractor/Extract.cpp b/soh/soh/Extractor/Extract.cpp index a9ddc4f41..f8eed8579 100644 --- a/soh/soh/Extractor/Extract.cpp +++ b/soh/soh/Extractor/Extract.cpp @@ -222,9 +222,9 @@ void Extractor::GetRoms(std::vector& roms) { if (S_ISREG(path.st_mode)) { // Get the position of the extension character. - char* ext = strchr(dir->d_name, '.'); - if (ext != NULL && (strcmp(ext, ".z64") == 0) && (strcmp(ext, ".n64") == 0) && - (strcmp(ext, ".v64") == 0)) { + char* ext = strrchr(dir->d_name, '.'); + if (ext != NULL && (strcmp(ext, ".z64") == 0 || strcmp(ext, ".n64") == 0 || + strcmp(ext, ".v64") == 0)) { roms.push_back(dir->d_name); } } diff --git a/soh/soh/GameMenuBar.cpp b/soh/soh/GameMenuBar.cpp index 42ee23284..206b29188 100644 --- a/soh/soh/GameMenuBar.cpp +++ b/soh/soh/GameMenuBar.cpp @@ -29,6 +29,7 @@ #include "soh/SaveManager.h" #include "OTRGlobals.h" #include "soh/Enhancements/presets.h" +#include "soh/Enhancements/mods.h" #include "soh/resource/type/Skeleton.h" #ifdef ENABLE_CROWD_CONTROL @@ -46,6 +47,8 @@ extern "C" { void disableBetaQuest() { isBetaQuestEnabled = false; } } +extern "C" PlayState* gPlayState; + enum SeqPlayers { /* 0 */ SEQ_BGM_MAIN, /* 1 */ SEQ_FANFARE, @@ -860,7 +863,9 @@ namespace GameMenuBar { ImGui::EndMenu(); } UIWidgets::PaddedText("Fix Vanishing Paths", true, false); - UIWidgets::EnhancementCombobox("gDirtPathFix", zFightingOptions, 0); + if (UIWidgets::EnhancementCombobox("gSceneSpecificDirtPathFix", zFightingOptions, 0) && gPlayState != NULL) { + UpdateDirtPathFixState(gPlayState->sceneNum); + } UIWidgets::Tooltip("Disabled: Paths vanish more the higher the resolution (Z-fighting is based on resolution)\n" "Consistent: Certain paths vanish the same way in all resolutions\n" "No Vanish: Paths do not vanish, Link seems to sink in to some paths\n" @@ -1013,14 +1018,14 @@ namespace GameMenuBar { LUS::RequestCvarSaveOnNextTick(); LUS::EnableWindow("Audio Editor", CVarGetInteger("gAudioEditor.WindowOpen", 0)); } - if (ImGui::Button(GetWindowButtonText("Gameplay Stats", CVarGetInteger("gGameplayStatsEnabled", 0)).c_str(), ImVec2(-1.0f, 0.0f))) { - if (CVarGetInteger("gGameplayStatsEnabled", 0)) { - CVarClear("gGameplayStatsEnabled"); + if (ImGui::Button(GetWindowButtonText("Gameplay Stats", CVarGetInteger("gGameplayStats.Enabled", 0)).c_str(), ImVec2(-1.0f, 0.0f))) { + if (CVarGetInteger("gGameplayStats.Enabled", 0)) { + CVarClear("gGameplayStats.Enabled"); } else { - CVarSetInteger("gGameplayStatsEnabled", 1); + CVarSetInteger("gGameplayStats.Enabled", 1); } LUS::RequestCvarSaveOnNextTick(); - LUS::EnableWindow("Gameplay Stats", CVarGetInteger("gGameplayStatsEnabled", 0)); + LUS::EnableWindow("Gameplay Stats", CVarGetInteger("gGameplayStats.Enabled", 0)); } ImGui::PopStyleVar(3); ImGui::PopStyleColor(1); diff --git a/soh/soh/OTRGlobals.cpp b/soh/soh/OTRGlobals.cpp index 1082c35ab..7b3515a39 100644 --- a/soh/soh/OTRGlobals.cpp +++ b/soh/soh/OTRGlobals.cpp @@ -4,6 +4,7 @@ #include #include #include +#include #include #include @@ -688,9 +689,10 @@ std::unordered_map ItemIDtoGetItemID{ { ITEM_WEIRD_EGG, GI_WEIRD_EGG } }; -extern "C" uint32_t GetGIID(uint32_t itemID) { - if (ItemIDtoGetItemID.contains(itemID)) +extern "C" int32_t GetGIID(uint32_t itemID) { + if (ItemIDtoGetItemID.contains(itemID)) { return ItemIDtoGetItemID.at(itemID); + } return -1; } @@ -781,6 +783,8 @@ extern "C" void InitOTR() { } else { CVarClear("gLetItSnow"); } + + srand(now); #ifdef ENABLE_CROWD_CONTROL CrowdControl::Instance = new CrowdControl(); CrowdControl::Instance->Init(); @@ -831,6 +835,14 @@ extern "C" uint64_t GetPerfCounter() { } #endif +extern "C" uint64_t GetUnixTimestamp() { + auto time = std::chrono::system_clock::now(); + auto since_epoch = time.time_since_epoch(); + auto millis = std::chrono::duration_cast(since_epoch); + long now = millis.count(); + return now; +} + // C->C++ Bridge extern "C" void Graph_ProcessFrame(void (*run_one_game_iter)(void)) { OTRGlobals::Instance->context->GetWindow()->MainLoop(run_one_game_iter); diff --git a/soh/soh/OTRGlobals.h b/soh/soh/OTRGlobals.h index e1d448af8..5c8604f03 100644 --- a/soh/soh/OTRGlobals.h +++ b/soh/soh/OTRGlobals.h @@ -88,6 +88,7 @@ void Ctx_ReadSaveFile(uintptr_t addr, void* dramAddr, size_t size); void Ctx_WriteSaveFile(uintptr_t addr, void* dramAddr, size_t size); uint64_t GetPerfCounter(); +uint64_t GetUnixTimestamp(); struct SkeletonHeader* ResourceMgr_LoadSkeletonByName(const char* path, SkelAnime* skelAnime); void ResourceMgr_UnregisterSkeleton(SkelAnime* skelAnime); void ResourceMgr_ClearSkeletons(); @@ -145,7 +146,7 @@ void EntranceTracker_SetLastEntranceOverride(s16 entranceIndex); void Gfx_RegisterBlendedTexture(const char* name, u8* mask, u8* replacement); void SaveManager_ThreadPoolWait(); -uint32_t GetGIID(uint32_t itemID); +int32_t GetGIID(uint32_t itemID); #endif #endif diff --git a/soh/soh/SaveManager.cpp b/soh/soh/SaveManager.cpp index d5f82123c..db39527ef 100644 --- a/soh/soh/SaveManager.cpp +++ b/soh/soh/SaveManager.cpp @@ -403,6 +403,7 @@ void SaveManager::Init() { LoadFile(fileNum); saveBlock = nlohmann::json::object(); } + } } @@ -690,6 +691,8 @@ void SaveManager::InitFileDebug() { gSaveContext.sceneFlags[5].swch = 0x40000000; } +// Threaded SaveFile takes copy of gSaveContext for local unmodified storage + void SaveManager::SaveFileThreaded(int fileNum, SaveContext* saveContext, int sectionID) { // Needed for first time save, hasn't changed in forever anyway saveBlock["version"] = 1; @@ -744,7 +747,7 @@ void SaveManager::SaveFileThreaded(int fileNum, SaveContext* saveContext, int se } // SaveSection creates a copy of gSaveContext to prevent mid-save data modification, and passes its reference to SaveFileThreaded -void SaveManager::SaveSection(int fileNum, int sectionID) { +void SaveManager::SaveSection(int fileNum, int sectionID, bool threaded) { if (fileNum == 0xFF) { return; } @@ -755,12 +758,15 @@ void SaveManager::SaveSection(int fileNum, int sectionID) { } auto saveContext = new SaveContext; memcpy(saveContext, &gSaveContext, sizeof(gSaveContext)); - // Can't think of any time the promise would be needed, so use push_task instead of submit - smThreadPool->push_task_back(&SaveManager::SaveFileThreaded, this, fileNum, saveContext, sectionID); + if (threaded) { + smThreadPool->push_task_back(&SaveManager::SaveFileThreaded, this, fileNum, saveContext, sectionID); + } else { + SaveFileThreaded(fileNum, saveContext, sectionID); + } } void SaveManager::SaveFile(int fileNum) { - SaveSection(fileNum, SECTION_ID_BASE); + SaveSection(fileNum, SECTION_ID_BASE, true); } void SaveManager::SaveGlobal() { @@ -783,6 +789,7 @@ void SaveManager::LoadFile(int fileNum) { std::ifstream input(GetFileName(fileNum)); + nlohmann::json saveBlock; input >> saveBlock; if (!saveBlock.contains("version")) { SPDLOG_ERROR("Save at " + GetFileName(fileNum).string() + " contains no version"); @@ -831,7 +838,7 @@ void SaveManager::ThreadPoolWait() { bool SaveManager::SaveFile_Exist(int fileNum) { try { bool exists = std::filesystem::exists(GetFileName(fileNum)); - SPDLOG_INFO("File[{}] - {}", fileNum, exists ? "exists" : "does not exist"); + SPDLOG_INFO("File[{}] - {}", fileNum, exists ? "exists" : "does not exist" ); return exists; } catch(std::filesystem::filesystem_error const& ex) { @@ -864,6 +871,7 @@ void SaveManager::AddSaveFunction(const std::string& name, int version, SaveFunc assert(false); return; } + int index = sectionIndex; if (coreSectionIDsByName.contains(name)) { index = coreSectionIDsByName.find(name)->second; @@ -1126,6 +1134,8 @@ void SaveManager::LoadBaseVersion2() { SaveManager::Instance->LoadArray("dungeonKeys", ARRAY_COUNT(gSaveContext.sohStats.dungeonKeys), [](size_t i) { SaveManager::Instance->LoadData("", gSaveContext.sohStats.dungeonKeys[i]); }); + SaveManager::Instance->LoadData("rtaTiming", gSaveContext.sohStats.rtaTiming); + SaveManager::Instance->LoadData("fileCreatedAt", gSaveContext.sohStats.fileCreatedAt); SaveManager::Instance->LoadData("playTimer", gSaveContext.sohStats.playTimer); SaveManager::Instance->LoadData("pauseTimer", gSaveContext.sohStats.pauseTimer); SaveManager::Instance->LoadArray("timestamps", ARRAY_COUNT(gSaveContext.sohStats.itemTimestamp), [](size_t i) { @@ -1340,6 +1350,8 @@ void SaveManager::LoadBaseVersion3() { SaveManager::Instance->LoadArray("dungeonKeys", ARRAY_COUNT(gSaveContext.sohStats.dungeonKeys), [](size_t i) { SaveManager::Instance->LoadData("", gSaveContext.sohStats.dungeonKeys[i]); }); + SaveManager::Instance->LoadData("rtaTiming", gSaveContext.sohStats.rtaTiming); + SaveManager::Instance->LoadData("fileCreatedAt", gSaveContext.sohStats.fileCreatedAt); SaveManager::Instance->LoadData("playTimer", gSaveContext.sohStats.playTimer); SaveManager::Instance->LoadData("pauseTimer", gSaveContext.sohStats.pauseTimer); SaveManager::Instance->LoadArray("itemTimestamps", ARRAY_COUNT(gSaveContext.sohStats.itemTimestamp), [](size_t i) { @@ -1352,6 +1364,7 @@ void SaveManager::LoadBaseVersion3() { SaveManager::Instance->LoadData("sceneTime", gSaveContext.sohStats.sceneTimestamps[i].sceneTime); SaveManager::Instance->LoadData("roomTime", gSaveContext.sohStats.sceneTimestamps[i].roomTime); SaveManager::Instance->LoadData("isRoom", gSaveContext.sohStats.sceneTimestamps[i].isRoom); + }); }); SaveManager::Instance->LoadData("tsIdx", gSaveContext.sohStats.tsIdx); @@ -1853,6 +1866,7 @@ void SaveManager::LoadArray(const std::string& name, const size_t size, LoadArra currentJsonContext = saveJsonContext; } + void SaveManager::LoadStruct(const std::string& name, LoadStructFunc func) { // Create an empty struct and set it as the current load context, then call the function that loads the struct. // If it is an array entry, load it from the array instead. @@ -2280,7 +2294,7 @@ void SaveManager::ConvertFromUnversioned() { static SaveContext saveContextSave = gSaveContext; InitFile(false); CopyV0Save(*file, gSaveContext); - SaveFile(fileNum); + SaveSection(fileNum, SECTION_ID_BASE, false); InitMeta(fileNum); gSaveContext = saveContextSave; } @@ -2305,7 +2319,7 @@ extern "C" void Save_SaveFile(void) { } extern "C" void Save_SaveSection(int sectionID) { - SaveManager::Instance->SaveSection(gSaveContext.fileNum, sectionID); + SaveManager::Instance->SaveSection(gSaveContext.fileNum, sectionID, true); } extern "C" void Save_SaveGlobal(void) { @@ -2321,8 +2335,8 @@ extern "C" void Save_AddLoadFunction(char* name, int version, SaveManager::LoadF SaveManager::Instance->AddLoadFunction(name, version, func); } -extern "C" void Save_AddSaveFunction(char* name, int version, SaveManager::SaveFunc func, bool saveWithBase, int parentSection = SECTION_PARENT_NONE) { - SaveManager::Instance->AddSaveFunction(name, version, func, saveWithBase, parentSection); +extern "C" void Save_AddSaveFunction(char* name, int version, SaveManager::SaveFunc func, bool saveWithBase) { + SaveManager::Instance->AddSaveFunction(name, version, func, saveWithBase); } extern "C" SaveFileMetaInfo* Save_GetSaveMetaInfo(int fileNum) { diff --git a/soh/soh/SaveManager.h b/soh/soh/SaveManager.h index 17ac8715c..ec0a2d787 100644 --- a/soh/soh/SaveManager.h +++ b/soh/soh/SaveManager.h @@ -32,22 +32,21 @@ typedef struct { #include "thread-pool/BS_thread_pool.hpp" extern "C" { - #include "z64save.h" +#include "z64save.h" } #include class SaveManager { -public: - + public: static SaveManager* Instance; static void WriteSaveFile(const std::filesystem::path& savePath, uintptr_t addr, void* dramAddr, size_t size); static void ReadSaveFile(std::filesystem::path savePath, uintptr_t addr, void* dramAddr, size_t size); - using InitFunc = void(*)(bool isDebug); - using LoadFunc = void(*)(); - using SaveFunc = void(*)(SaveContext* saveContext, int sectionID); + using InitFunc = void (*)(bool isDebug); + using LoadFunc = void (*)(); + using SaveFunc = void (*)(SaveContext* saveContext, int sectionID); using PostFunc = void (*)(int version); typedef struct { @@ -63,7 +62,7 @@ public: void Init(); void InitFile(bool isDebug); void SaveFile(int fileNum); - void SaveSection(int fileNum, int sectionID); + void SaveSection(int fileNum, int sectionID, bool threaded); int GetSaveSectionID(std::string& name); void SaveGlobal(); void LoadFile(int fileNum); @@ -76,10 +75,12 @@ public: // Adds a function to handling loading a section void AddLoadFunction(const std::string& name, int version, LoadFunc func); - // Adds a function that is called when saving. This should only be called once for each function, the version is filled in automatically. + // Adds a function that is called when saving. This should only be called once for each function, the version is + // filled in automatically. void AddSaveFunction(const std::string& name, int version, SaveFunc func, bool saveWithBase, int parentSection); - // Adds a function to be called after loading is complete. This is to handle any cleanup required from loading old versions. + // Adds a function to be called after loading is complete. This is to handle any cleanup required from loading old + // versions. void AddPostFunction(const std::string& name, PostFunc func); void CopyZeldaFile(int from, int to); @@ -87,8 +88,7 @@ public: bool IsRandoFile(); // Use a name of "" to save to an array. You must be in a SaveArray callback. - template - void SaveData(const std::string& name, const T& data) { + template void SaveData(const std::string& name, const T& data) { if (name == "") { assert((*currentJsonContext).is_array()); (*currentJsonContext).push_back(data); @@ -96,16 +96,16 @@ public: (*currentJsonContext)[name.c_str()] = data; } } - + // In the SaveArrayFunc func, the name must be "" to save to the array. using SaveArrayFunc = std::function; void SaveArray(const std::string& name, const size_t size, SaveArrayFunc func); - + using SaveStructFunc = std::function; void SaveStruct(const std::string& name, SaveStructFunc func); // Use a name of "" to load from an array. You must be in a LoadArray callback. - template void LoadData(const std::string& name, T& data, const T& defaultValue = T{}) { + template void LoadData(const std::string& name, T& data, const T& defaultValue = T{}) { if (name == "") { if (currentJsonArrayContext == currentJsonContext->end()) { // This array member is past the data in the json file. Therefore, default construct it diff --git a/soh/src/code/z_parameter.c b/soh/src/code/z_parameter.c index 954ce1190..410fd22a4 100644 --- a/soh/src/code/z_parameter.c +++ b/soh/src/code/z_parameter.c @@ -15,6 +15,7 @@ #endif #include "soh/Enhancements/game-interactor/GameInteractor.h" +#include "soh/Enhancements/game-interactor/GameInteractor_Hooks.h" #define DO_ACTION_TEX_WIDTH() 48 @@ -1706,7 +1707,12 @@ u8 Return_Item_Entry(GetItemEntry itemEntry, ItemID returnItem ) { // Processes Item_Give returns u8 Return_Item(u8 itemID, ModIndex modId, ItemID returnItem) { - uint32_t get = GetGIID(itemID); + // ITEM_SOLD_OUT doesn't have an ItemTable entry, so pass custom entry instead + if (itemID == ITEM_SOLD_OUT) { + GetItemEntry gie = { ITEM_SOLD_OUT, 0, 0, 0, 0, 0, 0, 0, false, ITEM_FROM_NPC, ITEM_CATEGORY_LESSER, NULL }; + return Return_Item_Entry(gie, returnItem); + } + int32_t get = GetGIID(itemID); if (get == -1) { modId = MOD_RANDOMIZER; get = itemID; @@ -6158,8 +6164,13 @@ void Interface_Update(PlayState* play) { u16 tempSaleMod = gSaveContext.pendingSaleMod; gSaveContext.pendingSale = ITEM_NONE; gSaveContext.pendingSaleMod = MOD_NONE; - if (tempSaleMod == 0) { - tempSaleItem = GetGIID(tempSaleItem); + if (tempSaleMod == MOD_NONE) { + s16 giid = GetGIID(tempSaleItem); + if (giid == -1) { + tempSaleMod = MOD_RANDOMIZER; + } else { + tempSaleItem = giid; + } } GameInteractor_ExecuteOnSaleEndHooks(ItemTable_RetrieveEntry(tempSaleMod, tempSaleItem)); } diff --git a/soh/src/code/z_play.c b/soh/src/code/z_play.c index 9ac45cdcc..7a9d568bc 100644 --- a/soh/src/code/z_play.c +++ b/soh/src/code/z_play.c @@ -604,6 +604,15 @@ void Play_Init(GameState* thisx) { (s32)(zAllocAligned + zAllocSize) - (s32)(zAllocAligned - zAlloc)); Fault_AddClient(&D_801614B8, ZeldaArena_Display, NULL, NULL); + // In order to keep bunny hood equipped on first load, we need to pre-set the age reqs for the item and slot + if (CVarGetInteger("gMMBunnyHood", 0) || CVarGetInteger("gTimelessEquipment", 0)) { + gItemAgeReqs[ITEM_MASK_BUNNY] = 9; + if(INV_CONTENT(ITEM_TRADE_CHILD) == ITEM_MASK_BUNNY) + gSlotAgeReqs[SLOT_TRADE_CHILD] = 9; + } + else { + gItemAgeReqs[ITEM_MASK_BUNNY] = gSlotAgeReqs[SLOT_TRADE_CHILD] = 1; + } func_800304DC(play, &play->actorCtx, play->linkActorEntry); while (!func_800973FC(play, &play->roomCtx)) { @@ -751,6 +760,14 @@ void Play_Update(PlayState* play) { if (CHECK_BTN_ALL(input[0].press.button, BTN_R)) {gSaveContext.sohStats.count[COUNT_BUTTON_PRESSES_R]++;} if (CHECK_BTN_ALL(input[0].press.button, BTN_Z)) {gSaveContext.sohStats.count[COUNT_BUTTON_PRESSES_Z]++;} if (CHECK_BTN_ALL(input[0].press.button, BTN_START)) {gSaveContext.sohStats.count[COUNT_BUTTON_PRESSES_START]++;} + + // Start RTA timing on first non-c-up input after intro cutscene + if ( + !gSaveContext.sohStats.fileCreatedAt && !Player_InCsMode(play) && + ((input[0].press.button && input[0].press.button != 0x8) || input[0].rel.stick_x != 0 || input[0].rel.stick_y != 0) + ) { + gSaveContext.sohStats.fileCreatedAt = GetUnixTimestamp(); + } } if (gTrnsnUnkState != 0) { diff --git a/soh/src/overlays/actors/ovl_Bg_Dy_Yoseizo/z_bg_dy_yoseizo.c b/soh/src/overlays/actors/ovl_Bg_Dy_Yoseizo/z_bg_dy_yoseizo.c index 34a8e5f2a..49584e2cd 100644 --- a/soh/src/overlays/actors/ovl_Bg_Dy_Yoseizo/z_bg_dy_yoseizo.c +++ b/soh/src/overlays/actors/ovl_Bg_Dy_Yoseizo/z_bg_dy_yoseizo.c @@ -109,7 +109,9 @@ void BgDyYoseizo_Init(Actor* thisx, PlayState* play2) { this->actionFunc = BgDyYoseizo_CheckMagicAcquired; } -void BgDyYoseizo_Destroy(Actor* this, PlayState* play) { +void BgDyYoseizo_Destroy(Actor* thisx, PlayState* play) { + BgDyYoseizo* this = (BgDyYoseizo*)thisx; + ResourceMgr_UnregisterSkeleton(&this->skelAnime); } static Color_RGB8 sParticlePrimColors[] = { diff --git a/soh/src/overlays/actors/ovl_Boss_Ganon2/z_boss_ganon2.c b/soh/src/overlays/actors/ovl_Boss_Ganon2/z_boss_ganon2.c index a1cfaa684..9de9f7400 100644 --- a/soh/src/overlays/actors/ovl_Boss_Ganon2/z_boss_ganon2.c +++ b/soh/src/overlays/actors/ovl_Boss_Ganon2/z_boss_ganon2.c @@ -1680,8 +1680,8 @@ void func_8090120C(BossGanon2* this, PlayState* play) { if ((ABS(temp_a0_2) < 0x2000) && (sqrtf(SQ(temp_f14) + SQ(temp_f12)) < 70.0f) && (player->swordState != 0) && (player->heldItemAction == PLAYER_IA_SWORD_MASTER)) { func_80064520(play, &play->csCtx); - gSaveContext.sohStats.gameComplete = true; gSaveContext.sohStats.itemTimestamp[TIMESTAMP_DEFEAT_GANON] = GAMEPLAYSTAT_TOTAL_TIME; + gSaveContext.sohStats.gameComplete = true; this->unk_39E = Play_CreateSubCamera(play); Play_ChangeCameraStatus(play, MAIN_CAM, CAM_STAT_WAIT); Play_ChangeCameraStatus(play, this->unk_39E, CAM_STAT_ACTIVE); diff --git a/soh/src/overlays/actors/ovl_Boss_Sst/z_boss_sst.c b/soh/src/overlays/actors/ovl_Boss_Sst/z_boss_sst.c index 4f1d8244f..98d1da158 100644 --- a/soh/src/overlays/actors/ovl_Boss_Sst/z_boss_sst.c +++ b/soh/src/overlays/actors/ovl_Boss_Sst/z_boss_sst.c @@ -346,6 +346,8 @@ void BossSst_Destroy(Actor* thisx, PlayState* play) { Collider_DestroyJntSph(play, &this->colliderJntSph); Collider_DestroyCylinder(play, &this->colliderCyl); Audio_StopSfxByPos(&this->center); + + ResourceMgr_UnregisterSkeleton(&this->skelAnime); } void BossSst_HeadSetupLurk(BossSst* this) { diff --git a/soh/src/overlays/actors/ovl_Demo_Ik/z_demo_ik.c b/soh/src/overlays/actors/ovl_Demo_Ik/z_demo_ik.c index db9c80fe6..fb302bb27 100644 --- a/soh/src/overlays/actors/ovl_Demo_Ik/z_demo_ik.c +++ b/soh/src/overlays/actors/ovl_Demo_Ik/z_demo_ik.c @@ -24,6 +24,9 @@ void DemoIk_Type1Draw(DemoIk* this, PlayState* play); void DemoIk_Type2Draw(DemoIk* this, PlayState* play); void DemoIk_Destroy(Actor* thisx, PlayState* play) { + DemoIk* this = (DemoIk*)thisx; + + ResourceMgr_UnregisterSkeleton(&this->skelAnime); } void DemoIk_BgCheck(DemoIk* this, PlayState* play) { diff --git a/soh/src/overlays/actors/ovl_Demo_Im/z_demo_im.c b/soh/src/overlays/actors/ovl_Demo_Im/z_demo_im.c index 5ea59cef7..05a292f16 100644 --- a/soh/src/overlays/actors/ovl_Demo_Im/z_demo_im.c +++ b/soh/src/overlays/actors/ovl_Demo_Im/z_demo_im.c @@ -1163,7 +1163,10 @@ void DemoIm_Init(Actor* thisx, PlayState* play) { } void DemoIm_Destroy(Actor* thisx, PlayState* play) { + DemoIm* this = (DemoIm*)thisx; DemoIm_DestroyCollider(thisx, play); + + ResourceMgr_UnregisterSkeleton(&this->skelAnime); } s32 DemoIm_OverrideLimbDraw(PlayState* play, s32 limbIndex, Gfx** dList, Vec3f* pos, Vec3s* rot, void* thisx) { diff --git a/soh/src/overlays/actors/ovl_Door_Killer/z_door_killer.c b/soh/src/overlays/actors/ovl_Door_Killer/z_door_killer.c index 8784647b0..4558838cb 100644 --- a/soh/src/overlays/actors/ovl_Door_Killer/z_door_killer.c +++ b/soh/src/overlays/actors/ovl_Door_Killer/z_door_killer.c @@ -203,6 +203,8 @@ void DoorKiller_Destroy(Actor* thisx, PlayState* play) { if ((thisx->params & 0xFF) == DOOR_KILLER_DOOR) { Collider_DestroyCylinder(play, &this->colliderCylinder); Collider_DestroyJntSph(play, &this->colliderJntSph); + + ResourceMgr_UnregisterSkeleton(&this->skelAnime); } } diff --git a/soh/src/overlays/actors/ovl_Door_Warp1/z_door_warp1.c b/soh/src/overlays/actors/ovl_Door_Warp1/z_door_warp1.c index 5152d290a..965409ac2 100644 --- a/soh/src/overlays/actors/ovl_Door_Warp1/z_door_warp1.c +++ b/soh/src/overlays/actors/ovl_Door_Warp1/z_door_warp1.c @@ -97,7 +97,16 @@ void DoorWarp1_Destroy(Actor* thisx, PlayState* play) { play->envCtx.adjAmbientColor[i] = play->envCtx.adjFogColor[i] = play->envCtx.adjLight1Color[i] = 0; } - //! @bug SkelAnime_Free is not called for crystal variants + + switch (this->actor.params) { + case WARP_DUNGEON_ADULT: + case WARP_BLUE_CRYSTAL: + case WARP_PURPLE_CRYSTAL: + SkelAnime_Free(&this->skelAnime, play); + break; + default: + break; + } } void DoorWarp1_SetupWarp(DoorWarp1* this, PlayState* play) { diff --git a/soh/src/overlays/actors/ovl_En_Am/z_en_am.c b/soh/src/overlays/actors/ovl_En_Am/z_en_am.c index 44d6f036c..71b562eff 100644 --- a/soh/src/overlays/actors/ovl_En_Am/z_en_am.c +++ b/soh/src/overlays/actors/ovl_En_Am/z_en_am.c @@ -249,6 +249,8 @@ void EnAm_Destroy(Actor* thisx, PlayState* play) { Collider_DestroyCylinder(play, &this->hurtCollider); Collider_DestroyCylinder(play, &this->blockCollider); //! @bug Quad collider is not destroyed (though destroy doesnt really do anything anyway) + + ResourceMgr_UnregisterSkeleton(&this->skelAnime); } void EnAm_SpawnEffects(EnAm* this, PlayState* play) { diff --git a/soh/src/overlays/actors/ovl_En_Ani/z_en_ani.c b/soh/src/overlays/actors/ovl_En_Ani/z_en_ani.c index f9d76b60e..97664aa17 100644 --- a/soh/src/overlays/actors/ovl_En_Ani/z_en_ani.c +++ b/soh/src/overlays/actors/ovl_En_Ani/z_en_ani.c @@ -95,6 +95,8 @@ void EnAni_Destroy(Actor* thisx, PlayState* play) { EnAni* this = (EnAni*)thisx; Collider_DestroyCylinder(play, &this->collider); + + ResourceMgr_UnregisterSkeleton(&this->skelAnime); } s32 EnAni_SetText(EnAni* this, PlayState* play, u16 textId) { diff --git a/soh/src/overlays/actors/ovl_En_Anubice/z_en_anubice.c b/soh/src/overlays/actors/ovl_En_Anubice/z_en_anubice.c index 7b99e4bcb..08e06ac04 100644 --- a/soh/src/overlays/actors/ovl_En_Anubice/z_en_anubice.c +++ b/soh/src/overlays/actors/ovl_En_Anubice/z_en_anubice.c @@ -167,6 +167,8 @@ void EnAnubice_Destroy(Actor* thisx, PlayState* play) { tag->anubis = NULL; } } + + ResourceMgr_UnregisterSkeleton(&this->skelAnime); } void EnAnubice_FindFlameCircles(EnAnubice* this, PlayState* play) { diff --git a/soh/src/overlays/actors/ovl_En_Attack_Niw/z_en_attack_niw.c b/soh/src/overlays/actors/ovl_En_Attack_Niw/z_en_attack_niw.c index a73c542c6..fd824bed7 100644 --- a/soh/src/overlays/actors/ovl_En_Attack_Niw/z_en_attack_niw.c +++ b/soh/src/overlays/actors/ovl_En_Attack_Niw/z_en_attack_niw.c @@ -68,6 +68,8 @@ void EnAttackNiw_Destroy(Actor* thisx, PlayState* play) { cucco->unk_296--; } } + + ResourceMgr_UnregisterSkeleton(&this->skelAnime); } void func_809B5268(EnAttackNiw* this, PlayState* play, s16 arg2) { diff --git a/soh/src/overlays/actors/ovl_En_Bb/z_en_bb.c b/soh/src/overlays/actors/ovl_En_Bb/z_en_bb.c index 35f6eb064..9f07839da 100644 --- a/soh/src/overlays/actors/ovl_En_Bb/z_en_bb.c +++ b/soh/src/overlays/actors/ovl_En_Bb/z_en_bb.c @@ -403,6 +403,8 @@ void EnBb_Destroy(Actor* thisx, PlayState* play) { EnBb* this = (EnBb*)thisx; Collider_DestroyJntSph(play, &this->collider); + + ResourceMgr_UnregisterSkeleton(&this->skelAnime); } void EnBb_SetupFlameTrail(EnBb* this) { diff --git a/soh/src/overlays/actors/ovl_En_Bigokuta/z_en_bigokuta.c b/soh/src/overlays/actors/ovl_En_Bigokuta/z_en_bigokuta.c index 24b2ff9f3..8e544a151 100644 --- a/soh/src/overlays/actors/ovl_En_Bigokuta/z_en_bigokuta.c +++ b/soh/src/overlays/actors/ovl_En_Bigokuta/z_en_bigokuta.c @@ -196,6 +196,8 @@ void EnBigokuta_Destroy(Actor* thisx, PlayState* play) { for (i = 0; i < ARRAY_COUNT(this->cylinder); i++) { Collider_DestroyCylinder(play, &this->cylinder[i]); } + + ResourceMgr_UnregisterSkeleton(&this->skelAnime); } void func_809BCE3C(EnBigokuta* this) { diff --git a/soh/src/overlays/actors/ovl_En_Bili/z_en_bili.c b/soh/src/overlays/actors/ovl_En_Bili/z_en_bili.c index d2ce6686e..971ac0f20 100644 --- a/soh/src/overlays/actors/ovl_En_Bili/z_en_bili.c +++ b/soh/src/overlays/actors/ovl_En_Bili/z_en_bili.c @@ -136,6 +136,8 @@ void EnBili_Destroy(Actor* thisx, PlayState* play) { EnBili* this = (EnBili*)thisx; Collider_DestroyCylinder(play, &this->collider); + + ResourceMgr_UnregisterSkeleton(&this->skelAnime); } // Setup Action Functions diff --git a/soh/src/overlays/actors/ovl_En_Bird/z_en_bird.c b/soh/src/overlays/actors/ovl_En_Bird/z_en_bird.c index fc0c673a1..f521e1718 100644 --- a/soh/src/overlays/actors/ovl_En_Bird/z_en_bird.c +++ b/soh/src/overlays/actors/ovl_En_Bird/z_en_bird.c @@ -62,6 +62,9 @@ void EnBird_Init(Actor* thisx, PlayState* play) { } void EnBird_Destroy(Actor* thisx, PlayState* play) { + EnBird* this = (EnBird*)thisx; + + SkelAnime_Free(&this->skelAnime, play); } void func_809C1CAC(EnBird* this, s16 params) { diff --git a/soh/src/overlays/actors/ovl_En_Bom_Bowl_Man/z_en_bom_bowl_man.c b/soh/src/overlays/actors/ovl_En_Bom_Bowl_Man/z_en_bom_bowl_man.c index db092c862..75f27576b 100644 --- a/soh/src/overlays/actors/ovl_En_Bom_Bowl_Man/z_en_bom_bowl_man.c +++ b/soh/src/overlays/actors/ovl_En_Bom_Bowl_Man/z_en_bom_bowl_man.c @@ -89,6 +89,9 @@ void EnBomBowlMan_Init(Actor* thisx, PlayState* play2) { } void EnBomBowlMan_Destroy(Actor* thisx, PlayState* play) { + EnBomBowlMan* this = (EnBomBowlMan*)thisx; + + ResourceMgr_UnregisterSkeleton(&this->skelAnime); } void EnBomBowMan_SetupWaitAsleep(EnBomBowlMan* this, PlayState* play) { diff --git a/soh/src/overlays/actors/ovl_En_Box/z_en_box.c b/soh/src/overlays/actors/ovl_En_Box/z_en_box.c index 2a83e587e..54ec3cf29 100644 --- a/soh/src/overlays/actors/ovl_En_Box/z_en_box.c +++ b/soh/src/overlays/actors/ovl_En_Box/z_en_box.c @@ -208,6 +208,8 @@ void EnBox_Destroy(Actor* thisx, PlayState* play) { EnBox* this = (EnBox*)thisx; DynaPoly_DeleteBgActor(play, &play->colCtx.dyna, this->dyna.bgId); + + ResourceMgr_UnregisterSkeleton(&this->skelanime); } void EnBox_RandomDustKinematic(EnBox* this, Vec3f* pos, Vec3f* velocity, Vec3f* accel) { diff --git a/soh/src/overlays/actors/ovl_En_Brob/z_en_brob.c b/soh/src/overlays/actors/ovl_En_Brob/z_en_brob.c index a7c2d119c..78607e0fe 100644 --- a/soh/src/overlays/actors/ovl_En_Brob/z_en_brob.c +++ b/soh/src/overlays/actors/ovl_En_Brob/z_en_brob.c @@ -102,6 +102,8 @@ void EnBrob_Destroy(Actor* thisx, PlayState* play) { DynaPoly_DeleteBgActor(play, &play->colCtx.dyna, this->dyna.bgId); Collider_DestroyCylinder(play, &this->colliders[0]); Collider_DestroyCylinder(play, &this->colliders[1]); + + ResourceMgr_UnregisterSkeleton(&this->skelAnime); } void func_809CADDC(EnBrob* this, PlayState* play) { diff --git a/soh/src/overlays/actors/ovl_En_Butte/z_en_butte.c b/soh/src/overlays/actors/ovl_En_Butte/z_en_butte.c index cfe109185..a775137f4 100644 --- a/soh/src/overlays/actors/ovl_En_Butte/z_en_butte.c +++ b/soh/src/overlays/actors/ovl_En_Butte/z_en_butte.c @@ -180,6 +180,8 @@ void EnButte_Destroy(Actor* thisx, PlayState* play2) { EnButte* this = (EnButte*)thisx; Collider_DestroyJntSph(play, &this->collider); + + ResourceMgr_UnregisterSkeleton(&this->skelAnime); } void func_809CD56C(EnButte* this) { diff --git a/soh/src/overlays/actors/ovl_En_Bw/z_en_bw.c b/soh/src/overlays/actors/ovl_En_Bw/z_en_bw.c index acf4727b4..3b7155b5e 100644 --- a/soh/src/overlays/actors/ovl_En_Bw/z_en_bw.c +++ b/soh/src/overlays/actors/ovl_En_Bw/z_en_bw.c @@ -161,6 +161,8 @@ void EnBw_Destroy(Actor* thisx, PlayState* play) { Collider_DestroyCylinder(play, &this->collider1); Collider_DestroyCylinder(play, &this->collider2); + + ResourceMgr_UnregisterSkeleton(&this->skelAnime); } void func_809CE884(EnBw* this, PlayState* play) { diff --git a/soh/src/overlays/actors/ovl_En_Cow/z_en_cow.c b/soh/src/overlays/actors/ovl_En_Cow/z_en_cow.c index 1c9df2c7c..b9e0136f1 100644 --- a/soh/src/overlays/actors/ovl_En_Cow/z_en_cow.c +++ b/soh/src/overlays/actors/ovl_En_Cow/z_en_cow.c @@ -162,6 +162,8 @@ void EnCow_Destroy(Actor* thisx, PlayState* play) { Collider_DestroyCylinder(play, &this->colliders[0]); Collider_DestroyCylinder(play, &this->colliders[1]); } + + ResourceMgr_UnregisterSkeleton(&this->skelAnime); } void func_809DF494(EnCow* this, PlayState* play) { diff --git a/soh/src/overlays/actors/ovl_En_Crow/z_en_crow.c b/soh/src/overlays/actors/ovl_En_Crow/z_en_crow.c index 40af76a4d..6598406ce 100644 --- a/soh/src/overlays/actors/ovl_En_Crow/z_en_crow.c +++ b/soh/src/overlays/actors/ovl_En_Crow/z_en_crow.c @@ -124,6 +124,8 @@ void EnCrow_Destroy(Actor* thisx, PlayState* play) { EnCrow* this = (EnCrow*)thisx; Collider_DestroyJntSph(play, &this->collider); + + ResourceMgr_UnregisterSkeleton(&this->skelAnime); } // Setup Action functions diff --git a/soh/src/overlays/actors/ovl_En_Cs/z_en_cs.c b/soh/src/overlays/actors/ovl_En_Cs/z_en_cs.c index 497a6d527..a3006daec 100644 --- a/soh/src/overlays/actors/ovl_En_Cs/z_en_cs.c +++ b/soh/src/overlays/actors/ovl_En_Cs/z_en_cs.c @@ -161,6 +161,8 @@ void EnCs_Destroy(Actor* thisx, PlayState* play) { EnCs* this = (EnCs*)thisx; Collider_DestroyCylinder(play, &this->collider); + + ResourceMgr_UnregisterSkeleton(&this->skelAnime); } s32 EnCs_GetTalkState(EnCs* this, PlayState* play) { diff --git a/soh/src/overlays/actors/ovl_En_Daiku/z_en_daiku.c b/soh/src/overlays/actors/ovl_En_Daiku/z_en_daiku.c index 57d2b9055..635f77e41 100644 --- a/soh/src/overlays/actors/ovl_En_Daiku/z_en_daiku.c +++ b/soh/src/overlays/actors/ovl_En_Daiku/z_en_daiku.c @@ -219,6 +219,8 @@ void EnDaiku_Destroy(Actor* thisx, PlayState* play) { EnDaiku* this = (EnDaiku*)thisx; Collider_DestroyCylinder(play, &this->collider); + + ResourceMgr_UnregisterSkeleton(&this->skelAnime); } s32 EnDaiku_UpdateTalking(EnDaiku* this, PlayState* play) { diff --git a/soh/src/overlays/actors/ovl_En_Daiku_Kakariko/z_en_daiku_kakariko.c b/soh/src/overlays/actors/ovl_En_Daiku_Kakariko/z_en_daiku_kakariko.c index a7a107d3a..e098051c7 100644 --- a/soh/src/overlays/actors/ovl_En_Daiku_Kakariko/z_en_daiku_kakariko.c +++ b/soh/src/overlays/actors/ovl_En_Daiku_Kakariko/z_en_daiku_kakariko.c @@ -208,6 +208,8 @@ void EnDaikuKakariko_Destroy(Actor* thisx, PlayState* play) { EnDaikuKakariko* this = (EnDaikuKakariko*)thisx; Collider_DestroyCylinder(play, &this->collider); + + ResourceMgr_UnregisterSkeleton(&this->skelAnime); } s32 EnDaikuKakariko_GetTalkState(EnDaikuKakariko* this, PlayState* play) { diff --git a/soh/src/overlays/actors/ovl_En_Dekubaba/z_en_dekubaba.c b/soh/src/overlays/actors/ovl_En_Dekubaba/z_en_dekubaba.c index 5fa65f49c..9f1658ed9 100644 --- a/soh/src/overlays/actors/ovl_En_Dekubaba/z_en_dekubaba.c +++ b/soh/src/overlays/actors/ovl_En_Dekubaba/z_en_dekubaba.c @@ -278,6 +278,8 @@ void EnDekubaba_Destroy(Actor* thisx, PlayState* play) { EnDekubaba* this = (EnDekubaba*)thisx; Collider_DestroyJntSph(play, &this->collider); + + ResourceMgr_UnregisterSkeleton(&this->skelAnime); } void EnDekubaba_DisableHitboxes(EnDekubaba* this) { diff --git a/soh/src/overlays/actors/ovl_En_Dekunuts/z_en_dekunuts.c b/soh/src/overlays/actors/ovl_En_Dekunuts/z_en_dekunuts.c index 3305700ec..86daf5187 100644 --- a/soh/src/overlays/actors/ovl_En_Dekunuts/z_en_dekunuts.c +++ b/soh/src/overlays/actors/ovl_En_Dekunuts/z_en_dekunuts.c @@ -136,6 +136,8 @@ void EnDekunuts_Destroy(Actor* thisx, PlayState* play) { if (this->actor.params != DEKUNUTS_FLOWER) { Collider_DestroyCylinder(play, &this->collider); + + ResourceMgr_UnregisterSkeleton(&this->skelAnime); } } diff --git a/soh/src/overlays/actors/ovl_En_Dh/z_en_dh.c b/soh/src/overlays/actors/ovl_En_Dh/z_en_dh.c index b41e9cdff..b64d327a9 100644 --- a/soh/src/overlays/actors/ovl_En_Dh/z_en_dh.c +++ b/soh/src/overlays/actors/ovl_En_Dh/z_en_dh.c @@ -164,6 +164,8 @@ void EnDh_Destroy(Actor* thisx, PlayState* play) { func_800F5B58(); Collider_DestroyCylinder(play, &this->collider1); Collider_DestroyJntSph(play, &this->collider2); + + ResourceMgr_UnregisterSkeleton(&this->skelAnime); } void EnDh_SpawnDebris(PlayState* play, EnDh* this, Vec3f* spawnPos, f32 spread, s32 arg4, f32 accelXZ, diff --git a/soh/src/overlays/actors/ovl_En_Dha/z_en_dha.c b/soh/src/overlays/actors/ovl_En_Dha/z_en_dha.c index dda60bfe4..090bac2d2 100644 --- a/soh/src/overlays/actors/ovl_En_Dha/z_en_dha.c +++ b/soh/src/overlays/actors/ovl_En_Dha/z_en_dha.c @@ -177,6 +177,8 @@ void EnDha_Destroy(Actor* thisx, PlayState* play) { EnDha* this = (EnDha*)thisx; Collider_DestroyJntSph(play, &this->collider); + + ResourceMgr_UnregisterSkeleton(&this->skelAnime); } void EnDha_SetupWait(EnDha* this) { diff --git a/soh/src/overlays/actors/ovl_En_Diving_Game/z_en_diving_game.c b/soh/src/overlays/actors/ovl_En_Diving_Game/z_en_diving_game.c index cbd6cee2b..a54b87e7b 100644 --- a/soh/src/overlays/actors/ovl_En_Diving_Game/z_en_diving_game.c +++ b/soh/src/overlays/actors/ovl_En_Diving_Game/z_en_diving_game.c @@ -108,6 +108,8 @@ void EnDivingGame_Destroy(Actor* thisx, PlayState* play) { gSaveContext.timer1State = 0; } Collider_DestroyCylinder(play, &this->collider); + + ResourceMgr_UnregisterSkeleton(&this->skelAnime); } void EnDivingGame_SpawnRuppy(EnDivingGame* this, PlayState* play) { diff --git a/soh/src/overlays/actors/ovl_En_Dns/z_en_dns.c b/soh/src/overlays/actors/ovl_En_Dns/z_en_dns.c index 6df63c4de..3942e3b2f 100644 --- a/soh/src/overlays/actors/ovl_En_Dns/z_en_dns.c +++ b/soh/src/overlays/actors/ovl_En_Dns/z_en_dns.c @@ -200,6 +200,8 @@ void EnDns_Destroy(Actor* thisx, PlayState* play) { EnDns* this = (EnDns*)thisx; Collider_DestroyCylinder(play, &this->collider); + + ResourceMgr_UnregisterSkeleton(&this->skelAnime); } void EnDns_ChangeAnim(EnDns* this, u8 index) { diff --git a/soh/src/overlays/actors/ovl_En_Dnt_Jiji/z_en_dnt_jiji.c b/soh/src/overlays/actors/ovl_En_Dnt_Jiji/z_en_dnt_jiji.c index 215331857..6a0d204bf 100644 --- a/soh/src/overlays/actors/ovl_En_Dnt_Jiji/z_en_dnt_jiji.c +++ b/soh/src/overlays/actors/ovl_En_Dnt_Jiji/z_en_dnt_jiji.c @@ -96,6 +96,8 @@ void EnDntJiji_Destroy(Actor* thisx, PlayState* play) { EnDntJiji* this = (EnDntJiji*)thisx; Collider_DestroyCylinder(play, &this->collider); + + ResourceMgr_UnregisterSkeleton(&this->skelAnime); } void EnDntJiji_SetFlower(EnDntJiji* this, PlayState* play) { diff --git a/soh/src/overlays/actors/ovl_En_Dnt_Nomal/z_en_dnt_nomal.c b/soh/src/overlays/actors/ovl_En_Dnt_Nomal/z_en_dnt_nomal.c index 030735f5b..5f05992ca 100644 --- a/soh/src/overlays/actors/ovl_En_Dnt_Nomal/z_en_dnt_nomal.c +++ b/soh/src/overlays/actors/ovl_En_Dnt_Nomal/z_en_dnt_nomal.c @@ -167,6 +167,8 @@ void EnDntNomal_Destroy(Actor* thisx, PlayState* play) { } else { Collider_DestroyCylinder(play, &this->bodyCyl); } + + ResourceMgr_UnregisterSkeleton(&this->skelAnime); } void EnDntNomal_WaitForObject(EnDntNomal* this, PlayState* play) { diff --git a/soh/src/overlays/actors/ovl_En_Dodojr/z_en_dodojr.c b/soh/src/overlays/actors/ovl_En_Dodojr/z_en_dodojr.c index 3f459e84d..62c83a231 100644 --- a/soh/src/overlays/actors/ovl_En_Dodojr/z_en_dodojr.c +++ b/soh/src/overlays/actors/ovl_En_Dodojr/z_en_dodojr.c @@ -88,6 +88,8 @@ void EnDodojr_Destroy(Actor* thisx, PlayState* play) { EnDodojr* this = (EnDodojr*)thisx; Collider_DestroyCylinder(play, &this->collider); + + ResourceMgr_UnregisterSkeleton(&this->skelAnime); } void func_809F64D0(EnDodojr* this) { diff --git a/soh/src/overlays/actors/ovl_En_Dodongo/z_en_dodongo.c b/soh/src/overlays/actors/ovl_En_Dodongo/z_en_dodongo.c index 0f841389d..f6b0939e1 100644 --- a/soh/src/overlays/actors/ovl_En_Dodongo/z_en_dodongo.c +++ b/soh/src/overlays/actors/ovl_En_Dodongo/z_en_dodongo.c @@ -353,6 +353,8 @@ void EnDodongo_Destroy(Actor* thisx, PlayState* play) { Collider_DestroyTris(play, &this->colliderHard); Collider_DestroyJntSph(play, &this->colliderBody); Collider_DestroyQuad(play, &this->colliderAT); + + ResourceMgr_UnregisterSkeleton(&this->skelAnime); } void EnDodongo_SetupIdle(EnDodongo* this) { diff --git a/soh/src/overlays/actors/ovl_En_Dog/z_en_dog.c b/soh/src/overlays/actors/ovl_En_Dog/z_en_dog.c index e2e63fc03..c860abe26 100644 --- a/soh/src/overlays/actors/ovl_En_Dog/z_en_dog.c +++ b/soh/src/overlays/actors/ovl_En_Dog/z_en_dog.c @@ -300,6 +300,8 @@ void EnDog_Init(Actor* thisx, PlayState* play) { void EnDog_Destroy(Actor* thisx, PlayState* play) { EnDog* this = (EnDog*)thisx; Collider_DestroyCylinder(play, &this->collider); + + ResourceMgr_UnregisterSkeleton(&this->skelAnime); } void EnDog_FollowPath(EnDog* this, PlayState* play) { diff --git a/soh/src/overlays/actors/ovl_En_Door/z_en_door.c b/soh/src/overlays/actors/ovl_En_Door/z_en_door.c index bd60075fd..2e830f250 100644 --- a/soh/src/overlays/actors/ovl_En_Door/z_en_door.c +++ b/soh/src/overlays/actors/ovl_En_Door/z_en_door.c @@ -143,6 +143,8 @@ void EnDoor_Destroy(Actor* thisx, PlayState* play) { if (transitionEntry->id < 0) { transitionEntry->id = -transitionEntry->id; } + + ResourceMgr_UnregisterSkeleton(&this->skelAnime); } void EnDoor_SetupType(EnDoor* this, PlayState* play) { diff --git a/soh/src/overlays/actors/ovl_En_Ds/z_en_ds.c b/soh/src/overlays/actors/ovl_En_Ds/z_en_ds.c index 84b4cd816..cebceaf9e 100644 --- a/soh/src/overlays/actors/ovl_En_Ds/z_en_ds.c +++ b/soh/src/overlays/actors/ovl_En_Ds/z_en_ds.c @@ -50,6 +50,9 @@ void EnDs_Init(Actor* thisx, PlayState* play) { } void EnDs_Destroy(Actor* thisx, PlayState* play) { + EnDs* this = (EnDs*)thisx; + + ResourceMgr_UnregisterSkeleton(&this->skelAnime); } void EnDs_Talk(EnDs* this, PlayState* play) { diff --git a/soh/src/overlays/actors/ovl_En_Eiyer/z_en_eiyer.c b/soh/src/overlays/actors/ovl_En_Eiyer/z_en_eiyer.c index b7bfa42f4..7690dfaf5 100644 --- a/soh/src/overlays/actors/ovl_En_Eiyer/z_en_eiyer.c +++ b/soh/src/overlays/actors/ovl_En_Eiyer/z_en_eiyer.c @@ -171,6 +171,8 @@ void EnEiyer_Init(Actor* thisx, PlayState* play) { void EnEiyer_Destroy(Actor* thisx, PlayState* play) { EnEiyer* this = (EnEiyer*)thisx; Collider_DestroyCylinder(play, &this->collider); + + ResourceMgr_UnregisterSkeleton(&this->skelanime); } void EnEiyer_RotateAroundHome(EnEiyer* this) { diff --git a/soh/src/overlays/actors/ovl_En_Elf/z_en_elf.c b/soh/src/overlays/actors/ovl_En_Elf/z_en_elf.c index fe39cea99..7911cbddd 100644 --- a/soh/src/overlays/actors/ovl_En_Elf/z_en_elf.c +++ b/soh/src/overlays/actors/ovl_En_Elf/z_en_elf.c @@ -437,6 +437,8 @@ void EnElf_Destroy(Actor* thisx, PlayState* play) { LightContext_RemoveLight(play, &play->lightCtx, this->lightNodeGlow); LightContext_RemoveLight(play, &play->lightCtx, this->lightNodeNoGlow); + + ResourceMgr_UnregisterSkeleton(&this->skelAnime); } void func_80A02A20(EnElf* this, PlayState* play) { diff --git a/soh/src/overlays/actors/ovl_En_Fd/z_en_fd.c b/soh/src/overlays/actors/ovl_En_Fd/z_en_fd.c index de2aaf391..39988998f 100644 --- a/soh/src/overlays/actors/ovl_En_Fd/z_en_fd.c +++ b/soh/src/overlays/actors/ovl_En_Fd/z_en_fd.c @@ -472,6 +472,8 @@ void EnFd_Destroy(Actor* thisx, PlayState* play) { EnFd* this = (EnFd*)thisx; Collider_DestroyJntSph(play, &this->collider); + + ResourceMgr_UnregisterSkeleton(&this->skelAnime); } void EnFd_Reappear(EnFd* this, PlayState* play) { diff --git a/soh/src/overlays/actors/ovl_En_Firefly/z_en_firefly.c b/soh/src/overlays/actors/ovl_En_Firefly/z_en_firefly.c index a62dc583c..231629b0b 100644 --- a/soh/src/overlays/actors/ovl_En_Firefly/z_en_firefly.c +++ b/soh/src/overlays/actors/ovl_En_Firefly/z_en_firefly.c @@ -198,6 +198,8 @@ void EnFirefly_Destroy(Actor* thisx, PlayState* play) { EnFirefly* this = (EnFirefly*)thisx; Collider_DestroyJntSph(play, &this->collider); + + ResourceMgr_UnregisterSkeleton(&this->skelAnime); } void EnFirefly_SetupFlyIdle(EnFirefly* this) { diff --git a/soh/src/overlays/actors/ovl_En_Fish/z_en_fish.c b/soh/src/overlays/actors/ovl_En_Fish/z_en_fish.c index be42dc521..4c1a9ba1f 100644 --- a/soh/src/overlays/actors/ovl_En_Fish/z_en_fish.c +++ b/soh/src/overlays/actors/ovl_En_Fish/z_en_fish.c @@ -157,6 +157,8 @@ void EnFish_Destroy(Actor* thisx, PlayState* play2) { EnFish* this = (EnFish*)thisx; Collider_DestroyJntSph(play, &this->collider); + + ResourceMgr_UnregisterSkeleton(&this->skelAnime); } void EnFish_SetYOffset(EnFish* this) { diff --git a/soh/src/overlays/actors/ovl_En_Floormas/z_en_floormas.c b/soh/src/overlays/actors/ovl_En_Floormas/z_en_floormas.c index 068e255f4..84725221a 100644 --- a/soh/src/overlays/actors/ovl_En_Floormas/z_en_floormas.c +++ b/soh/src/overlays/actors/ovl_En_Floormas/z_en_floormas.c @@ -179,6 +179,8 @@ void EnFloormas_Destroy(Actor* thisx, PlayState* play) { EnFloormas* this = (EnFloormas*)thisx; ColliderCylinder* col = &this->collider; Collider_DestroyCylinder(play, col); + + ResourceMgr_UnregisterSkeleton(&this->skelAnime); } void EnFloormas_MakeInvulnerable(EnFloormas* this) { diff --git a/soh/src/overlays/actors/ovl_En_Fr/z_en_fr.c b/soh/src/overlays/actors/ovl_En_Fr/z_en_fr.c index 5c7159da0..fa131fe00 100644 --- a/soh/src/overlays/actors/ovl_En_Fr/z_en_fr.c +++ b/soh/src/overlays/actors/ovl_En_Fr/z_en_fr.c @@ -309,6 +309,9 @@ void EnFr_Destroy(Actor* thisx, PlayState* play) { EnFr* this = (EnFr*)thisx; LightContext_RemoveLight(play, &play->lightCtx, this->lightNode); + + ResourceMgr_UnregisterSkeleton(&this->skelAnime); + ResourceMgr_UnregisterSkeleton(&this->skelAnimeButterfly); } void EnFr_IsDivingIntoWater(EnFr* this, PlayState* play) { diff --git a/soh/src/overlays/actors/ovl_En_Fu/z_en_fu.c b/soh/src/overlays/actors/ovl_En_Fu/z_en_fu.c index 13d32d757..20bc6dff6 100644 --- a/soh/src/overlays/actors/ovl_En_Fu/z_en_fu.c +++ b/soh/src/overlays/actors/ovl_En_Fu/z_en_fu.c @@ -99,6 +99,8 @@ void EnFu_Init(Actor* thisx, PlayState* play) { void EnFu_Destroy(Actor* thisx, PlayState* play) { EnFu* this = (EnFu*)thisx; Collider_DestroyCylinder(play, &this->collider); + + ResourceMgr_UnregisterSkeleton(&this->skelanime); } s32 func_80A1D94C(EnFu* this, PlayState* play, u16 textID, EnFuActionFunc actionFunc) { diff --git a/soh/src/overlays/actors/ovl_En_Fw/z_en_fw.c b/soh/src/overlays/actors/ovl_En_Fw/z_en_fw.c index b1b25bed5..ffac1fa0c 100644 --- a/soh/src/overlays/actors/ovl_En_Fw/z_en_fw.c +++ b/soh/src/overlays/actors/ovl_En_Fw/z_en_fw.c @@ -208,6 +208,8 @@ void EnFw_Init(Actor* thisx, PlayState* play) { void EnFw_Destroy(Actor* thisx, PlayState* play) { EnFw* this = (EnFw*)thisx; Collider_DestroyJntSph(play, &this->collider); + + ResourceMgr_UnregisterSkeleton(&this->skelAnime); } void EnFw_Bounce(EnFw* this, PlayState* play) { diff --git a/soh/src/overlays/actors/ovl_En_Gb/z_en_gb.c b/soh/src/overlays/actors/ovl_En_Gb/z_en_gb.c index 0110c3017..ff4be49e6 100644 --- a/soh/src/overlays/actors/ovl_En_Gb/z_en_gb.c +++ b/soh/src/overlays/actors/ovl_En_Gb/z_en_gb.c @@ -224,6 +224,8 @@ void EnGb_Destroy(Actor* thisx, PlayState* play) { Collider_DestroyCylinder(play, &this->collider); LightContext_RemoveLight(play, &play->lightCtx, this->light); DynaPoly_DeleteBgActor(play, &play->colCtx.dyna, this->dyna.bgId); + + ResourceMgr_UnregisterSkeleton(&this->skelAnime); } void func_80A2F608(EnGb* this) { diff --git a/soh/src/overlays/actors/ovl_En_Ge1/z_en_ge1.c b/soh/src/overlays/actors/ovl_En_Ge1/z_en_ge1.c index c3b56c44e..7e397050f 100644 --- a/soh/src/overlays/actors/ovl_En_Ge1/z_en_ge1.c +++ b/soh/src/overlays/actors/ovl_En_Ge1/z_en_ge1.c @@ -203,6 +203,8 @@ void EnGe1_Destroy(Actor* thisx, PlayState* play) { EnGe1* this = (EnGe1*)thisx; Collider_DestroyCylinder(play, &this->collider); + + ResourceMgr_UnregisterSkeleton(&this->skelAnime); } s32 EnGe1_SetTalkAction(EnGe1* this, PlayState* play, u16 textId, f32 arg3, EnGe1ActionFunc actionFunc) { diff --git a/soh/src/overlays/actors/ovl_En_Ge2/z_en_ge2.c b/soh/src/overlays/actors/ovl_En_Ge2/z_en_ge2.c index ab5a48b7a..9437100d5 100644 --- a/soh/src/overlays/actors/ovl_En_Ge2/z_en_ge2.c +++ b/soh/src/overlays/actors/ovl_En_Ge2/z_en_ge2.c @@ -174,6 +174,8 @@ void EnGe2_Destroy(Actor* thisx, PlayState* play) { EnGe2* this = (EnGe2*)thisx; Collider_DestroyCylinder(play, &this->collider); + + ResourceMgr_UnregisterSkeleton(&this->skelAnime); } // Detection/check functions diff --git a/soh/src/overlays/actors/ovl_En_Ge3/z_en_ge3.c b/soh/src/overlays/actors/ovl_En_Ge3/z_en_ge3.c index fecf3e9ea..4da2b2db7 100644 --- a/soh/src/overlays/actors/ovl_En_Ge3/z_en_ge3.c +++ b/soh/src/overlays/actors/ovl_En_Ge3/z_en_ge3.c @@ -90,6 +90,8 @@ void EnGe3_Destroy(Actor* thisx, PlayState* play) { EnGe3* this = (EnGe3*)thisx; Collider_DestroyCylinder(play, &this->collider); + + ResourceMgr_UnregisterSkeleton(&this->skelAnime); } void EnGe3_TurnToFacePlayer(EnGe3* this, PlayState* play) { diff --git a/soh/src/overlays/actors/ovl_En_GeldB/z_en_geldb.c b/soh/src/overlays/actors/ovl_En_GeldB/z_en_geldb.c index 909aa91b4..040f516ad 100644 --- a/soh/src/overlays/actors/ovl_En_GeldB/z_en_geldb.c +++ b/soh/src/overlays/actors/ovl_En_GeldB/z_en_geldb.c @@ -269,6 +269,8 @@ void EnGeldB_Destroy(Actor* thisx, PlayState* play) { Collider_DestroyTris(play, &this->blockCollider); Collider_DestroyCylinder(play, &this->bodyCollider); Collider_DestroyQuad(play, &this->swordCollider); + + ResourceMgr_UnregisterSkeleton(&this->skelAnime); } s32 EnGeldB_ReactToPlayer(PlayState* play, EnGeldB* this, s16 arg2) { diff --git a/soh/src/overlays/actors/ovl_En_GirlA/z_en_girla.c b/soh/src/overlays/actors/ovl_En_GirlA/z_en_girla.c index ea41a2e69..bd99a9ba1 100644 --- a/soh/src/overlays/actors/ovl_En_GirlA/z_en_girla.c +++ b/soh/src/overlays/actors/ovl_En_GirlA/z_en_girla.c @@ -452,11 +452,6 @@ void EnGirlA_Init(Actor* thisx, PlayState* play) { } void EnGirlA_Destroy(Actor* thisx, PlayState* play) { - EnGirlA* this = (EnGirlA*)thisx; - - if (this->isInitialized) { - SkelAnime_Free(&this->skelAnime, play); - } } s32 EnGirlA_CanBuy_Arrows(PlayState* play, EnGirlA* this) { diff --git a/soh/src/overlays/actors/ovl_En_Gm/z_en_gm.c b/soh/src/overlays/actors/ovl_En_Gm/z_en_gm.c index 2519fcb1e..3f6ae565d 100644 --- a/soh/src/overlays/actors/ovl_En_Gm/z_en_gm.c +++ b/soh/src/overlays/actors/ovl_En_Gm/z_en_gm.c @@ -88,6 +88,8 @@ void EnGm_Destroy(Actor* thisx, PlayState* play) { EnGm* this = (EnGm*)thisx; Collider_DestroyCylinder(play, &this->collider); + + ResourceMgr_UnregisterSkeleton(&this->skelAnime); } s32 func_80A3D7C8(void) { diff --git a/soh/src/overlays/actors/ovl_En_Go2/z_en_go2.c b/soh/src/overlays/actors/ovl_En_Go2/z_en_go2.c index c70a55932..ad7d99686 100644 --- a/soh/src/overlays/actors/ovl_En_Go2/z_en_go2.c +++ b/soh/src/overlays/actors/ovl_En_Go2/z_en_go2.c @@ -1691,6 +1691,9 @@ void EnGo2_Init(Actor* thisx, PlayState* play) { } void EnGo2_Destroy(Actor* thisx, PlayState* play) { + EnGo2* this = (EnGo2*)thisx; + + ResourceMgr_UnregisterSkeleton(&this->skelAnime); } void EnGo2_CurledUp(EnGo2* this, PlayState* play) { diff --git a/soh/src/overlays/actors/ovl_En_Goma/z_en_goma.c b/soh/src/overlays/actors/ovl_En_Goma/z_en_goma.c index decc90f5b..641eb0872 100644 --- a/soh/src/overlays/actors/ovl_En_Goma/z_en_goma.c +++ b/soh/src/overlays/actors/ovl_En_Goma/z_en_goma.c @@ -177,6 +177,8 @@ void EnGoma_Destroy(Actor* thisx, PlayState* play) { if (this->actor.params < 10) { Collider_DestroyCylinder(play, &this->colCyl1); Collider_DestroyCylinder(play, &this->colCyl2); + + ResourceMgr_UnregisterSkeleton(&this->skelanime); } } diff --git a/soh/src/overlays/actors/ovl_En_Guest/z_en_guest.c b/soh/src/overlays/actors/ovl_En_Guest/z_en_guest.c index bdf90381a..69d543d27 100644 --- a/soh/src/overlays/actors/ovl_En_Guest/z_en_guest.c +++ b/soh/src/overlays/actors/ovl_En_Guest/z_en_guest.c @@ -71,6 +71,8 @@ void EnGuest_Destroy(Actor* thisx, PlayState* play) { EnGuest* this = (EnGuest*)thisx; Collider_DestroyCylinder(play, &this->collider); + + ResourceMgr_UnregisterSkeleton(&this->skelAnime); } void EnGuest_Update(Actor* thisx, PlayState* play) { diff --git a/soh/src/overlays/actors/ovl_En_Heishi1/z_en_heishi1.c b/soh/src/overlays/actors/ovl_En_Heishi1/z_en_heishi1.c index 7c06608ff..507d480b2 100644 --- a/soh/src/overlays/actors/ovl_En_Heishi1/z_en_heishi1.c +++ b/soh/src/overlays/actors/ovl_En_Heishi1/z_en_heishi1.c @@ -142,6 +142,9 @@ void EnHeishi1_Init(Actor* thisx, PlayState* play) { } void EnHeishi1_Destroy(Actor* thisx, PlayState* play) { + EnHeishi1* this = (EnHeishi1*)thisx; + + ResourceMgr_UnregisterSkeleton(&this->skelAnime); } void EnHeishi1_SetupWalk(EnHeishi1* this, PlayState* play) { diff --git a/soh/src/overlays/actors/ovl_En_Heishi2/z_en_heishi2.c b/soh/src/overlays/actors/ovl_En_Heishi2/z_en_heishi2.c index 89ea0e499..35c351827 100644 --- a/soh/src/overlays/actors/ovl_En_Heishi2/z_en_heishi2.c +++ b/soh/src/overlays/actors/ovl_En_Heishi2/z_en_heishi2.c @@ -166,6 +166,8 @@ void EnHeishi2_Destroy(Actor* thisx, PlayState* play) { if ((this->collider.dim.radius != 0) || (this->collider.dim.height != 0)) { Collider_DestroyCylinder(play, &this->collider); } + + ResourceMgr_UnregisterSkeleton(&this->skelAnime); } void EnHeishi2_DoNothing1(EnHeishi2* this, PlayState* play) { diff --git a/soh/src/overlays/actors/ovl_En_Heishi3/z_en_heishi3.c b/soh/src/overlays/actors/ovl_En_Heishi3/z_en_heishi3.c index 06407227f..83dae5d81 100644 --- a/soh/src/overlays/actors/ovl_En_Heishi3/z_en_heishi3.c +++ b/soh/src/overlays/actors/ovl_En_Heishi3/z_en_heishi3.c @@ -90,6 +90,8 @@ void EnHeishi3_Destroy(Actor* thisx, PlayState* play) { EnHeishi3* this = (EnHeishi3*)thisx; Collider_DestroyCylinder(play, &this->collider); + + ResourceMgr_UnregisterSkeleton(&this->skelAnime); } void EnHeishi3_SetupGuardType(EnHeishi3* this, PlayState* play) { diff --git a/soh/src/overlays/actors/ovl_En_Heishi4/z_en_heishi4.c b/soh/src/overlays/actors/ovl_En_Heishi4/z_en_heishi4.c index f3a9ddc3a..f684f4ae5 100644 --- a/soh/src/overlays/actors/ovl_En_Heishi4/z_en_heishi4.c +++ b/soh/src/overlays/actors/ovl_En_Heishi4/z_en_heishi4.c @@ -107,6 +107,8 @@ void EnHeishi4_Destroy(Actor* thisx, PlayState* play) { EnHeishi4* this = (EnHeishi4*)thisx; Collider_DestroyCylinder(play, &this->collider); + + ResourceMgr_UnregisterSkeleton(&this->skelAnime); } void func_80A56328(EnHeishi4* this, PlayState* play) { diff --git a/soh/src/overlays/actors/ovl_En_Hintnuts/z_en_hintnuts.c b/soh/src/overlays/actors/ovl_En_Hintnuts/z_en_hintnuts.c index 8f6cc3ec6..f21966d93 100644 --- a/soh/src/overlays/actors/ovl_En_Hintnuts/z_en_hintnuts.c +++ b/soh/src/overlays/actors/ovl_En_Hintnuts/z_en_hintnuts.c @@ -105,6 +105,8 @@ void EnHintnuts_Destroy(Actor* thisx, PlayState* play) { if (this->actor.params != 0xA) { Collider_DestroyCylinder(play, &this->collider); + + ResourceMgr_UnregisterSkeleton(&this->skelAnime); } } diff --git a/soh/src/overlays/actors/ovl_En_Hs/z_en_hs.c b/soh/src/overlays/actors/ovl_En_Hs/z_en_hs.c index 17efce312..b730915e6 100644 --- a/soh/src/overlays/actors/ovl_En_Hs/z_en_hs.c +++ b/soh/src/overlays/actors/ovl_En_Hs/z_en_hs.c @@ -116,6 +116,8 @@ void EnHs_Destroy(Actor* thisx, PlayState* play) { EnHs* this = (EnHs*)thisx; Collider_DestroyCylinder(play, &this->collider); + + ResourceMgr_UnregisterSkeleton(&this->skelAnime); } s32 func_80A6E53C(EnHs* this, PlayState* play, u16 textId, EnHsActionFunc actionFunc) { diff --git a/soh/src/overlays/actors/ovl_En_Hs2/z_en_hs2.c b/soh/src/overlays/actors/ovl_En_Hs2/z_en_hs2.c index 6e0e58913..01deb32fb 100644 --- a/soh/src/overlays/actors/ovl_En_Hs2/z_en_hs2.c +++ b/soh/src/overlays/actors/ovl_En_Hs2/z_en_hs2.c @@ -71,6 +71,8 @@ void EnHs2_Destroy(Actor* thisx, PlayState* play) { EnHs2* this = (EnHs2*)thisx; Collider_DestroyCylinder(play, &this->collider); + + ResourceMgr_UnregisterSkeleton(&this->skelAnime); } s32 func_80A6F0B4(EnHs2* this, PlayState* play, u16 textId, EnHs2ActionFunc actionFunc) { diff --git a/soh/src/overlays/actors/ovl_En_Hy/z_en_hy.c b/soh/src/overlays/actors/ovl_En_Hy/z_en_hy.c index b7eb3a301..248094843 100644 --- a/soh/src/overlays/actors/ovl_En_Hy/z_en_hy.c +++ b/soh/src/overlays/actors/ovl_En_Hy/z_en_hy.c @@ -906,6 +906,8 @@ void EnHy_Destroy(Actor* thisx, PlayState* play) { EnHy* this = (EnHy*)thisx; Collider_DestroyCylinder(play, &this->collider); + + ResourceMgr_UnregisterSkeleton(&this->skelAnime); } void EnHy_InitImpl(EnHy* this, PlayState* play) { diff --git a/soh/src/overlays/actors/ovl_En_Ik/z_en_ik.c b/soh/src/overlays/actors/ovl_En_Ik/z_en_ik.c index 17825746d..d57ec0d45 100644 --- a/soh/src/overlays/actors/ovl_En_Ik/z_en_ik.c +++ b/soh/src/overlays/actors/ovl_En_Ik/z_en_ik.c @@ -175,6 +175,8 @@ void EnIk_Destroy(Actor* thisx, PlayState* play) { Collider_DestroyTris(play, &this->shieldCollider); Collider_DestroyCylinder(play, &this->bodyCollider); Collider_DestroyQuad(play, &this->axeCollider); + + ResourceMgr_UnregisterSkeleton(&this->skelAnime); } void EnIk_SetupAction(EnIk* this, EnIkActionFunc actionFunc) { diff --git a/soh/src/overlays/actors/ovl_En_In/z_en_in.c b/soh/src/overlays/actors/ovl_En_In/z_en_in.c index 720f371b4..246a0f3ac 100644 --- a/soh/src/overlays/actors/ovl_En_In/z_en_in.c +++ b/soh/src/overlays/actors/ovl_En_In/z_en_in.c @@ -503,6 +503,8 @@ void EnIn_Destroy(Actor* thisx, PlayState* play) { if (this->actionFunc != NULL && this->actionFunc != func_80A79FB0) { Collider_DestroyCylinder(play, &this->collider); + + ResourceMgr_UnregisterSkeleton(&this->skelAnime); } } diff --git a/soh/src/overlays/actors/ovl_En_Insect/z_en_insect.c b/soh/src/overlays/actors/ovl_En_Insect/z_en_insect.c index c6da75926..8e1604e9a 100644 --- a/soh/src/overlays/actors/ovl_En_Insect/z_en_insect.c +++ b/soh/src/overlays/actors/ovl_En_Insect/z_en_insect.c @@ -234,6 +234,8 @@ void EnInsect_Destroy(Actor* thisx, PlayState* play) { if ((temp_v0 == 2 || temp_v0 == 3) && D_80A7DEB8 > 0) { D_80A7DEB8--; } + + ResourceMgr_UnregisterSkeleton(&this->skelAnime); } void func_80A7C3A0(EnInsect* this) { diff --git a/soh/src/overlays/actors/ovl_En_Jj/z_en_jj.c b/soh/src/overlays/actors/ovl_En_Jj/z_en_jj.c index 9b729d260..fbea8b130 100644 --- a/soh/src/overlays/actors/ovl_En_Jj/z_en_jj.c +++ b/soh/src/overlays/actors/ovl_En_Jj/z_en_jj.c @@ -143,6 +143,8 @@ void EnJj_Destroy(Actor* thisx, PlayState* play) { case JABUJABU_MAIN: DynaPoly_DeleteBgActor(play, &play->colCtx.dyna, this->dyna.bgId); Collider_DestroyCylinder(play, &this->collider); + + ResourceMgr_UnregisterSkeleton(&this->skelAnime); break; case JABUJABU_COLLISION: diff --git a/soh/src/overlays/actors/ovl_En_Js/z_en_js.c b/soh/src/overlays/actors/ovl_En_Js/z_en_js.c index 3248ad33f..12f443ec3 100644 --- a/soh/src/overlays/actors/ovl_En_Js/z_en_js.c +++ b/soh/src/overlays/actors/ovl_En_Js/z_en_js.c @@ -76,6 +76,8 @@ void EnJs_Destroy(Actor* thisx, PlayState* play) { EnJs* this = (EnJs*)thisx; Collider_DestroyCylinder(play, &this->collider); + + ResourceMgr_UnregisterSkeleton(&this->skelAnime); } u8 func_80A88F64(EnJs* this, PlayState* play, u16 textId) { diff --git a/soh/src/overlays/actors/ovl_En_Kakasi/z_en_kakasi.c b/soh/src/overlays/actors/ovl_En_Kakasi/z_en_kakasi.c index 2d2e2c6a6..10baf4b03 100644 --- a/soh/src/overlays/actors/ovl_En_Kakasi/z_en_kakasi.c +++ b/soh/src/overlays/actors/ovl_En_Kakasi/z_en_kakasi.c @@ -59,8 +59,7 @@ void EnKakasi_Destroy(Actor* thisx, PlayState* play) { EnKakasi* this = (EnKakasi*)thisx; Collider_DestroyCylinder(play, &this->collider); - SkelAnime_Free(&this->skelanime, play); // OTR - Fixed this memory leak - //! @bug SkelAnime_Free is not called + SkelAnime_Free(&this->skelanime, play); } void EnKakasi_Init(Actor* thisx, PlayState* play) { diff --git a/soh/src/overlays/actors/ovl_En_Kakasi2/z_en_kakasi2.c b/soh/src/overlays/actors/ovl_En_Kakasi2/z_en_kakasi2.c index efb05975e..a61323f38 100644 --- a/soh/src/overlays/actors/ovl_En_Kakasi2/z_en_kakasi2.c +++ b/soh/src/overlays/actors/ovl_En_Kakasi2/z_en_kakasi2.c @@ -109,8 +109,7 @@ void EnKakasi2_Destroy(Actor* thisx, PlayState* play) { EnKakasi2* this = (EnKakasi2*)thisx; Collider_DestroyCylinder(play, &this->collider); - SkelAnime_Free(&this->skelAnime, play); // OTR - Fixed this memory leak - //! @bug SkelAnime_Free is not called + SkelAnime_Free(&this->skelAnime, play); } void func_80A90264(EnKakasi2* this, PlayState* play) { diff --git a/soh/src/overlays/actors/ovl_En_Kakasi3/z_en_kakasi3.c b/soh/src/overlays/actors/ovl_En_Kakasi3/z_en_kakasi3.c index 95b0e405f..6d000d6fe 100644 --- a/soh/src/overlays/actors/ovl_En_Kakasi3/z_en_kakasi3.c +++ b/soh/src/overlays/actors/ovl_En_Kakasi3/z_en_kakasi3.c @@ -63,8 +63,7 @@ void EnKakasi3_Destroy(Actor* thisx, PlayState* play) { EnKakasi3* this = (EnKakasi3*)thisx; Collider_DestroyCylinder(play, &this->collider); - SkelAnime_Free(&this->skelAnime, play); //OTR - Fixed this memory leak - //! @bug SkelAnime_Free is not called + SkelAnime_Free(&this->skelAnime, play); } void EnKakasi3_Init(Actor* thisx, PlayState* play) { diff --git a/soh/src/overlays/actors/ovl_En_Karebaba/z_en_karebaba.c b/soh/src/overlays/actors/ovl_En_Karebaba/z_en_karebaba.c index de209a242..6632b949b 100644 --- a/soh/src/overlays/actors/ovl_En_Karebaba/z_en_karebaba.c +++ b/soh/src/overlays/actors/ovl_En_Karebaba/z_en_karebaba.c @@ -119,6 +119,8 @@ void EnKarebaba_Destroy(Actor* thisx, PlayState* play) { Collider_DestroyCylinder(play, &this->bodyCollider); Collider_DestroyCylinder(play, &this->headCollider); + + ResourceMgr_UnregisterSkeleton(&this->skelAnime); } void EnKarebaba_ResetCollider(EnKarebaba* this) { diff --git a/soh/src/overlays/actors/ovl_En_Ko/z_en_ko.c b/soh/src/overlays/actors/ovl_En_Ko/z_en_ko.c index 5c8a41671..6dec44fe6 100644 --- a/soh/src/overlays/actors/ovl_En_Ko/z_en_ko.c +++ b/soh/src/overlays/actors/ovl_En_Ko/z_en_ko.c @@ -1145,6 +1145,8 @@ void EnKo_Init(Actor* thisx, PlayState* play) { void EnKo_Destroy(Actor* thisx, PlayState* play) { EnKo* this = (EnKo*)thisx; Collider_DestroyCylinder(play, &this->collider); + + ResourceMgr_UnregisterSkeleton(&this->skelAnime); } void func_80A99048(EnKo* this, PlayState* play) { diff --git a/soh/src/overlays/actors/ovl_En_Kz/z_en_kz.c b/soh/src/overlays/actors/ovl_En_Kz/z_en_kz.c index c9f3f6112..30a8d39b9 100644 --- a/soh/src/overlays/actors/ovl_En_Kz/z_en_kz.c +++ b/soh/src/overlays/actors/ovl_En_Kz/z_en_kz.c @@ -385,6 +385,8 @@ void EnKz_Destroy(Actor* thisx, PlayState* play) { EnKz* this = (EnKz*)thisx; Collider_DestroyCylinder(play, &this->collider); + + ResourceMgr_UnregisterSkeleton(&this->skelanime); } void EnKz_PreMweepWait(EnKz* this, PlayState* play) { diff --git a/soh/src/overlays/actors/ovl_En_Mb/z_en_mb.c b/soh/src/overlays/actors/ovl_En_Mb/z_en_mb.c index f932e9aa5..b8c7bfc19 100644 --- a/soh/src/overlays/actors/ovl_En_Mb/z_en_mb.c +++ b/soh/src/overlays/actors/ovl_En_Mb/z_en_mb.c @@ -336,6 +336,8 @@ void EnMb_Destroy(Actor* thisx, PlayState* play) { Collider_DestroyTris(play, &this->frontShielding); Collider_DestroyCylinder(play, &this->hitbox); Collider_DestroyQuad(play, &this->attackCollider); + + ResourceMgr_UnregisterSkeleton(&this->skelAnime); } void EnMb_FaceWaypoint(EnMb* this, PlayState* play) { diff --git a/soh/src/overlays/actors/ovl_En_Md/z_en_md.c b/soh/src/overlays/actors/ovl_En_Md/z_en_md.c index b4b4a7b07..72ecf32be 100644 --- a/soh/src/overlays/actors/ovl_En_Md/z_en_md.c +++ b/soh/src/overlays/actors/ovl_En_Md/z_en_md.c @@ -699,6 +699,8 @@ void EnMd_Init(Actor* thisx, PlayState* play) { void EnMd_Destroy(Actor* thisx, PlayState* play) { EnMd* this = (EnMd*)thisx; Collider_DestroyCylinder(play, &this->collider); + + ResourceMgr_UnregisterSkeleton(&this->skelAnime); } void func_80AAB874(EnMd* this, PlayState* play) { diff --git a/soh/src/overlays/actors/ovl_En_Mk/z_en_mk.c b/soh/src/overlays/actors/ovl_En_Mk/z_en_mk.c index 1ffca5429..67399b3d6 100644 --- a/soh/src/overlays/actors/ovl_En_Mk/z_en_mk.c +++ b/soh/src/overlays/actors/ovl_En_Mk/z_en_mk.c @@ -79,6 +79,8 @@ void EnMk_Destroy(Actor* thisx, PlayState* play) { EnMk* this = (EnMk*)thisx; Collider_DestroyCylinder(play, &this->collider); + + ResourceMgr_UnregisterSkeleton(&this->skelAnime); } void func_80AACA40(EnMk* this, PlayState* play) { diff --git a/soh/src/overlays/actors/ovl_En_Mm/z_en_mm.c b/soh/src/overlays/actors/ovl_En_Mm/z_en_mm.c index ce31cf2c7..bd5f3107a 100644 --- a/soh/src/overlays/actors/ovl_En_Mm/z_en_mm.c +++ b/soh/src/overlays/actors/ovl_En_Mm/z_en_mm.c @@ -198,6 +198,8 @@ void EnMm_Destroy(Actor* thisx, PlayState* play) { EnMm* this = (EnMm*)thisx; Collider_DestroyCylinder(play, &this->collider); + + ResourceMgr_UnregisterSkeleton(&this->skelAnime); } s32 func_80AADA70(void) { diff --git a/soh/src/overlays/actors/ovl_En_Mm2/z_en_mm2.c b/soh/src/overlays/actors/ovl_En_Mm2/z_en_mm2.c index d48517704..f567727c5 100644 --- a/soh/src/overlays/actors/ovl_En_Mm2/z_en_mm2.c +++ b/soh/src/overlays/actors/ovl_En_Mm2/z_en_mm2.c @@ -161,6 +161,8 @@ void EnMm2_Destroy(Actor* thisx, PlayState* play) { EnMm2* this = (EnMm2*)thisx; Collider_DestroyCylinder(play, &this->collider); + + ResourceMgr_UnregisterSkeleton(&this->skelAnime); } s32 func_80AAF224(EnMm2* this, PlayState* play, EnMm2ActionFunc actionFunc) { diff --git a/soh/src/overlays/actors/ovl_En_Ms/z_en_ms.c b/soh/src/overlays/actors/ovl_En_Ms/z_en_ms.c index 3e0321b49..923cb5011 100644 --- a/soh/src/overlays/actors/ovl_En_Ms/z_en_ms.c +++ b/soh/src/overlays/actors/ovl_En_Ms/z_en_ms.c @@ -99,6 +99,8 @@ void EnMs_Destroy(Actor* thisx, PlayState* play) { EnMs* this = (EnMs*)thisx; Collider_DestroyCylinder(play, &this->collider); + + ResourceMgr_UnregisterSkeleton(&this->skelAnime); } void EnMs_Wait(EnMs* this, PlayState* play) { diff --git a/soh/src/overlays/actors/ovl_En_Nb/z_en_nb.c b/soh/src/overlays/actors/ovl_En_Nb/z_en_nb.c index 06dc6b868..560cb6cfa 100644 --- a/soh/src/overlays/actors/ovl_En_Nb/z_en_nb.c +++ b/soh/src/overlays/actors/ovl_En_Nb/z_en_nb.c @@ -147,6 +147,8 @@ void EnNb_Destroy(Actor* thisx, PlayState* play) { D_80AB4318 = 0; Collider_DestroyCylinder(play, &this->collider); + + ResourceMgr_UnregisterSkeleton(&this->skelAnime); } void func_80AB0FBC(EnNb* this, PlayState* play) { diff --git a/soh/src/overlays/actors/ovl_En_Niw/z_en_niw.c b/soh/src/overlays/actors/ovl_En_Niw/z_en_niw.c index 6659350ff..e36296654 100644 --- a/soh/src/overlays/actors/ovl_En_Niw/z_en_niw.c +++ b/soh/src/overlays/actors/ovl_En_Niw/z_en_niw.c @@ -246,6 +246,8 @@ void EnNiw_Destroy(Actor* thisx, PlayState* play) { EnNiw* this = (EnNiw*)thisx; Collider_DestroyCylinder(play, &this->collider); + + ResourceMgr_UnregisterSkeleton(&this->skelAnime); } void func_80AB5BF8(EnNiw* this, PlayState* play, s16 arg2) { diff --git a/soh/src/overlays/actors/ovl_En_Niw_Girl/z_en_niw_girl.c b/soh/src/overlays/actors/ovl_En_Niw_Girl/z_en_niw_girl.c index d6013fc79..db41089e8 100644 --- a/soh/src/overlays/actors/ovl_En_Niw_Girl/z_en_niw_girl.c +++ b/soh/src/overlays/actors/ovl_En_Niw_Girl/z_en_niw_girl.c @@ -93,6 +93,9 @@ void EnNiwGirl_Init(Actor* thisx, PlayState* play) { } void EnNiwGirl_Destroy(Actor* thisx, PlayState* play) { + EnNiwGirl* this = (EnNiwGirl*)thisx; + + ResourceMgr_UnregisterSkeleton(&this->skelAnime); } void EnNiwGirl_Jump(EnNiwGirl* this, PlayState* play) { diff --git a/soh/src/overlays/actors/ovl_En_Niw_Lady/z_en_niw_lady.c b/soh/src/overlays/actors/ovl_En_Niw_Lady/z_en_niw_lady.c index 63522dc64..088381c70 100644 --- a/soh/src/overlays/actors/ovl_En_Niw_Lady/z_en_niw_lady.c +++ b/soh/src/overlays/actors/ovl_En_Niw_Lady/z_en_niw_lady.c @@ -96,6 +96,8 @@ void EnNiwLady_Destroy(Actor* thisx, PlayState* play) { EnNiwLady* this = (EnNiwLady*)thisx; Collider_DestroyCylinder(play, &this->collider); + + ResourceMgr_UnregisterSkeleton(&this->skelAnime); } void EnNiwLady_ChoseAnimation(EnNiwLady* this, PlayState* play, s32 arg2) { diff --git a/soh/src/overlays/actors/ovl_En_Okuta/z_en_okuta.c b/soh/src/overlays/actors/ovl_En_Okuta/z_en_okuta.c index 92a49fc57..9d958d0c5 100644 --- a/soh/src/overlays/actors/ovl_En_Okuta/z_en_okuta.c +++ b/soh/src/overlays/actors/ovl_En_Okuta/z_en_okuta.c @@ -163,6 +163,10 @@ void EnOkuta_Destroy(Actor* thisx, PlayState* play) { EnOkuta* this = (EnOkuta*)thisx; Collider_DestroyCylinder(play, &this->collider); + + if (thisx->params == 0) { + ResourceMgr_UnregisterSkeleton(&this->skelAnime); + } } void EnOkuta_SpawnBubbles(EnOkuta* this, PlayState* play) { diff --git a/soh/src/overlays/actors/ovl_En_Owl/z_en_owl.c b/soh/src/overlays/actors/ovl_En_Owl/z_en_owl.c index 20c5373c1..eb7932aed 100644 --- a/soh/src/overlays/actors/ovl_En_Owl/z_en_owl.c +++ b/soh/src/overlays/actors/ovl_En_Owl/z_en_owl.c @@ -240,6 +240,9 @@ void EnOwl_Destroy(Actor* thisx, PlayState* play) { EnOwl* this = (EnOwl*)thisx; Collider_DestroyCylinder(play, &this->collider); + + ResourceMgr_UnregisterSkeleton(&this->skelAnime); + ResourceMgr_UnregisterSkeleton(&this->skelAnime2); } /** diff --git a/soh/src/overlays/actors/ovl_En_Partner/z_en_partner.c b/soh/src/overlays/actors/ovl_En_Partner/z_en_partner.c index c284af550..e3c5028e5 100644 --- a/soh/src/overlays/actors/ovl_En_Partner/z_en_partner.c +++ b/soh/src/overlays/actors/ovl_En_Partner/z_en_partner.c @@ -124,6 +124,8 @@ void EnPartner_Destroy(Actor* thisx, PlayState* play) { LightContext_RemoveLight(play, &play->lightCtx, this->lightNodeNoGlow); Collider_DestroyCylinder(play, &this->collider); + + ResourceMgr_UnregisterSkeleton(&this->skelAnime); } void EnPartner_UpdateLights(EnPartner* this, PlayState* play) { diff --git a/soh/src/overlays/actors/ovl_En_Peehat/z_en_peehat.c b/soh/src/overlays/actors/ovl_En_Peehat/z_en_peehat.c index 318fe2b8b..ec1bcd28b 100644 --- a/soh/src/overlays/actors/ovl_En_Peehat/z_en_peehat.c +++ b/soh/src/overlays/actors/ovl_En_Peehat/z_en_peehat.c @@ -256,6 +256,8 @@ void EnPeehat_Destroy(Actor* thisx, PlayState* play) { parent->unk_2FA--; } } + + ResourceMgr_UnregisterSkeleton(&this->skelAnime); } void EnPeehat_SpawnDust(PlayState* play, EnPeehat* this, Vec3f* pos, f32 arg3, s32 arg4, f32 arg5, f32 arg6) { diff --git a/soh/src/overlays/actors/ovl_En_Po_Desert/z_en_po_desert.c b/soh/src/overlays/actors/ovl_En_Po_Desert/z_en_po_desert.c index b5771c87d..8c2e6d569 100644 --- a/soh/src/overlays/actors/ovl_En_Po_Desert/z_en_po_desert.c +++ b/soh/src/overlays/actors/ovl_En_Po_Desert/z_en_po_desert.c @@ -86,6 +86,8 @@ void EnPoDesert_Destroy(Actor* thisx, PlayState* play) { LightContext_RemoveLight(play, &play->lightCtx, this->lightNode); Collider_DestroyCylinder(play, &this->collider); + + ResourceMgr_UnregisterSkeleton(&this->skelAnime); } void EnPoDesert_SetNextPathPoint(EnPoDesert* this, PlayState* play) { diff --git a/soh/src/overlays/actors/ovl_En_Po_Field/z_en_po_field.c b/soh/src/overlays/actors/ovl_En_Po_Field/z_en_po_field.c index c7e8bf877..882ea5baf 100644 --- a/soh/src/overlays/actors/ovl_En_Po_Field/z_en_po_field.c +++ b/soh/src/overlays/actors/ovl_En_Po_Field/z_en_po_field.c @@ -188,6 +188,8 @@ void EnPoField_Destroy(Actor* thisx, PlayState* play) { Collider_DestroyCylinder(play, &this->flameCollider); Collider_DestroyCylinder(play, &this->collider); } + + ResourceMgr_UnregisterSkeleton(&this->skelAnime); } void EnPoField_SetupWaitForSpawn(EnPoField* this, PlayState* play) { diff --git a/soh/src/overlays/actors/ovl_En_Po_Relay/z_en_po_relay.c b/soh/src/overlays/actors/ovl_En_Po_Relay/z_en_po_relay.c index e271da21a..7db0fcc16 100644 --- a/soh/src/overlays/actors/ovl_En_Po_Relay/z_en_po_relay.c +++ b/soh/src/overlays/actors/ovl_En_Po_Relay/z_en_po_relay.c @@ -117,6 +117,8 @@ void EnPoRelay_Destroy(Actor* thisx, PlayState* play) { D_80AD8D24 = 0; LightContext_RemoveLight(play, &play->lightCtx, this->lightNode); Collider_DestroyCylinder(play, &this->collider); + + ResourceMgr_UnregisterSkeleton(&this->skelAnime); } void EnPoRelay_SetupIdle(EnPoRelay* this) { diff --git a/soh/src/overlays/actors/ovl_En_Po_Sisters/z_en_po_sisters.c b/soh/src/overlays/actors/ovl_En_Po_Sisters/z_en_po_sisters.c index 77dc6cbb0..def561831 100644 --- a/soh/src/overlays/actors/ovl_En_Po_Sisters/z_en_po_sisters.c +++ b/soh/src/overlays/actors/ovl_En_Po_Sisters/z_en_po_sisters.c @@ -238,6 +238,8 @@ void EnPoSisters_Destroy(Actor* thisx, PlayState* play) { func_800F5B58(); } Collider_DestroyCylinder(play, &this->collider); + + ResourceMgr_UnregisterSkeleton(&this->skelAnime); } void func_80AD9240(EnPoSisters* this, s32 arg1, Vec3f* arg2) { diff --git a/soh/src/overlays/actors/ovl_En_Poh/z_en_poh.c b/soh/src/overlays/actors/ovl_En_Poh/z_en_poh.c index 117752a8e..dcf933967 100644 --- a/soh/src/overlays/actors/ovl_En_Poh/z_en_poh.c +++ b/soh/src/overlays/actors/ovl_En_Poh/z_en_poh.c @@ -254,6 +254,8 @@ void EnPoh_Destroy(Actor* thisx, PlayState* play) { if (this->actor.params == EN_POH_RUPEE) { D_80AE1A50--; } + + ResourceMgr_UnregisterSkeleton(&this->skelAnime); } void func_80ADE114(EnPoh* this) { diff --git a/soh/src/overlays/actors/ovl_En_Rd/z_en_rd.c b/soh/src/overlays/actors/ovl_En_Rd/z_en_rd.c index cde57a6bf..bccf64b30 100644 --- a/soh/src/overlays/actors/ovl_En_Rd/z_en_rd.c +++ b/soh/src/overlays/actors/ovl_En_Rd/z_en_rd.c @@ -175,6 +175,8 @@ void EnRd_Destroy(Actor* thisx, PlayState* play) { gSaveContext.sunsSongState = SUNSSONG_INACTIVE; } Collider_DestroyCylinder(play, &this->collider); + + ResourceMgr_UnregisterSkeleton(&this->skelAnime); } void func_80AE2630(PlayState* play, Actor* thisx, s32 arg2) { diff --git a/soh/src/overlays/actors/ovl_En_Reeba/z_en_reeba.c b/soh/src/overlays/actors/ovl_En_Reeba/z_en_reeba.c index 9015f3407..a12149145 100644 --- a/soh/src/overlays/actors/ovl_En_Reeba/z_en_reeba.c +++ b/soh/src/overlays/actors/ovl_En_Reeba/z_en_reeba.c @@ -161,6 +161,8 @@ void EnReeba_Destroy(Actor* thisx, PlayState* play) { } } } + + ResourceMgr_UnregisterSkeleton(&this->skelanime); } void func_80AE4F40(EnReeba* this, PlayState* play) { diff --git a/soh/src/overlays/actors/ovl_En_Ru1/z_en_ru1.c b/soh/src/overlays/actors/ovl_En_Ru1/z_en_ru1.c index 6894a1c9c..557be5e40 100644 --- a/soh/src/overlays/actors/ovl_En_Ru1/z_en_ru1.c +++ b/soh/src/overlays/actors/ovl_En_Ru1/z_en_ru1.c @@ -205,6 +205,8 @@ void EnRu1_Destroy(Actor* thisx, PlayState* play) { D_80AF1938 = 0; EnRu1_DestroyColliders(this, play); + + ResourceMgr_UnregisterSkeleton(&this->skelAnime); } void EnRu1_UpdateEyes(EnRu1* this) { diff --git a/soh/src/overlays/actors/ovl_En_Ru2/z_en_ru2.c b/soh/src/overlays/actors/ovl_En_Ru2/z_en_ru2.c index 697871524..7e06207f0 100644 --- a/soh/src/overlays/actors/ovl_En_Ru2/z_en_ru2.c +++ b/soh/src/overlays/actors/ovl_En_Ru2/z_en_ru2.c @@ -108,6 +108,8 @@ void EnRu2_Destroy(Actor* thisx, PlayState* play) { EnRu2* this = (EnRu2*)thisx; D_80AF4118 = 0; Collider_DestroyCylinder(play, &this->collider); + + ResourceMgr_UnregisterSkeleton(&this->skelAnime); } void func_80AF2608(EnRu2* this) { diff --git a/soh/src/overlays/actors/ovl_En_Sa/z_en_sa.c b/soh/src/overlays/actors/ovl_En_Sa/z_en_sa.c index bb700f82b..3e1bc7fe5 100644 --- a/soh/src/overlays/actors/ovl_En_Sa/z_en_sa.c +++ b/soh/src/overlays/actors/ovl_En_Sa/z_en_sa.c @@ -532,6 +532,8 @@ void EnSa_Destroy(Actor* thisx, PlayState* play) { EnSa* this = (EnSa*)thisx; Collider_DestroyCylinder(play, &this->collider); + + ResourceMgr_UnregisterSkeleton(&this->skelAnime); } void func_80AF6448(EnSa* this, PlayState* play) { diff --git a/soh/src/overlays/actors/ovl_En_Shopnuts/z_en_shopnuts.c b/soh/src/overlays/actors/ovl_En_Shopnuts/z_en_shopnuts.c index 582e66a97..23a530fd7 100644 --- a/soh/src/overlays/actors/ovl_En_Shopnuts/z_en_shopnuts.c +++ b/soh/src/overlays/actors/ovl_En_Shopnuts/z_en_shopnuts.c @@ -91,6 +91,8 @@ void EnShopnuts_Destroy(Actor* thisx, PlayState* play) { EnShopnuts* this = (EnShopnuts*)thisx; Collider_DestroyCylinder(play, &this->collider); + + ResourceMgr_UnregisterSkeleton(&this->skelAnime); } void EnShopnuts_SetupWait(EnShopnuts* this) { diff --git a/soh/src/overlays/actors/ovl_En_Skb/z_en_skb.c b/soh/src/overlays/actors/ovl_En_Skb/z_en_skb.c index 185eb265f..ef88637bf 100644 --- a/soh/src/overlays/actors/ovl_En_Skb/z_en_skb.c +++ b/soh/src/overlays/actors/ovl_En_Skb/z_en_skb.c @@ -185,6 +185,8 @@ void EnSkb_Destroy(Actor* thisx, PlayState* play) { } } Collider_DestroyJntSph(play, &this->collider); + + ResourceMgr_UnregisterSkeleton(&this->skelAnime); } void func_80AFCD60(EnSkb* this) { diff --git a/soh/src/overlays/actors/ovl_En_Skj/z_en_skj.c b/soh/src/overlays/actors/ovl_En_Skj/z_en_skj.c index a93e30107..a5ba6ef0a 100644 --- a/soh/src/overlays/actors/ovl_En_Skj/z_en_skj.c +++ b/soh/src/overlays/actors/ovl_En_Skj/z_en_skj.c @@ -464,6 +464,8 @@ void EnSkj_Destroy(Actor* thisx, PlayState* play) { EnSkj* this = (EnSkj*)thisx; Collider_DestroyCylinder(play, &this->collider); + + ResourceMgr_UnregisterSkeleton(&this->skelAnime); } s32 EnSkj_RangeCheck(Player* player, EnSkj* this) { diff --git a/soh/src/overlays/actors/ovl_En_Ssh/z_en_ssh.c b/soh/src/overlays/actors/ovl_En_Ssh/z_en_ssh.c index 3a05dbfd9..45768898c 100644 --- a/soh/src/overlays/actors/ovl_En_Ssh/z_en_ssh.c +++ b/soh/src/overlays/actors/ovl_En_Ssh/z_en_ssh.c @@ -643,6 +643,8 @@ void EnSsh_Destroy(Actor* thisx, PlayState* play) { Collider_DestroyCylinder(play, &this->colCylinder[i]); } Collider_DestroyJntSph(play, &this->colSph); + + ResourceMgr_UnregisterSkeleton(&this->skelAnime); } void EnSsh_Wait(EnSsh* this, PlayState* play) { diff --git a/soh/src/overlays/actors/ovl_En_St/z_en_st.c b/soh/src/overlays/actors/ovl_En_St/z_en_st.c index c9de2bb59..66eb44abc 100644 --- a/soh/src/overlays/actors/ovl_En_St/z_en_st.c +++ b/soh/src/overlays/actors/ovl_En_St/z_en_st.c @@ -815,6 +815,8 @@ void EnSt_Destroy(Actor* thisx, PlayState* play) { Collider_DestroyCylinder(play, &this->colCylinder[i]); } Collider_DestroyJntSph(play, &this->colSph); + + ResourceMgr_UnregisterSkeleton(&this->skelAnime); } void EnSt_WaitOnCeiling(EnSt* this, PlayState* play) { diff --git a/soh/src/overlays/actors/ovl_En_Sth/z_en_sth.c b/soh/src/overlays/actors/ovl_En_Sth/z_en_sth.c index 064de26fe..b45560a04 100644 --- a/soh/src/overlays/actors/ovl_En_Sth/z_en_sth.c +++ b/soh/src/overlays/actors/ovl_En_Sth/z_en_sth.c @@ -173,6 +173,8 @@ void EnSth_Destroy(Actor* thisx, PlayState* play) { EnSth* this = (EnSth*)thisx; Collider_DestroyCylinder(play, &this->collider); + + ResourceMgr_UnregisterSkeleton(&this->skelAnime); } void EnSth_WaitForObjectLoaded(EnSth* this, PlayState* play) { diff --git a/soh/src/overlays/actors/ovl_En_Sw/z_en_sw.c b/soh/src/overlays/actors/ovl_En_Sw/z_en_sw.c index da860cb21..7c5473055 100644 --- a/soh/src/overlays/actors/ovl_En_Sw/z_en_sw.c +++ b/soh/src/overlays/actors/ovl_En_Sw/z_en_sw.c @@ -328,6 +328,8 @@ void EnSw_Destroy(Actor* thisx, PlayState* play) { EnSw* this = (EnSw*)thisx; Collider_DestroyJntSph(play, &this->collider); + + ResourceMgr_UnregisterSkeleton(&this->skelAnime); } s32 func_80B0C9F0(EnSw* this, PlayState* play) { diff --git a/soh/src/overlays/actors/ovl_En_Syateki_Man/z_en_syateki_man.c b/soh/src/overlays/actors/ovl_En_Syateki_Man/z_en_syateki_man.c index d3371f345..279ac1af3 100644 --- a/soh/src/overlays/actors/ovl_En_Syateki_Man/z_en_syateki_man.c +++ b/soh/src/overlays/actors/ovl_En_Syateki_Man/z_en_syateki_man.c @@ -183,6 +183,9 @@ void EnSyatekiMan_Init(Actor* thisx, PlayState* play) { } void EnSyatekiMan_Destroy(Actor* thisx, PlayState* play) { + EnSyatekiMan* this = (EnSyatekiMan*)thisx; + + ResourceMgr_UnregisterSkeleton(&this->skelAnime); } void EnSyatekiMan_Start(EnSyatekiMan* this, PlayState* play) { diff --git a/soh/src/overlays/actors/ovl_En_Syateki_Niw/z_en_syateki_niw.c b/soh/src/overlays/actors/ovl_En_Syateki_Niw/z_en_syateki_niw.c index cfbe70085..8c231a09c 100644 --- a/soh/src/overlays/actors/ovl_En_Syateki_Niw/z_en_syateki_niw.c +++ b/soh/src/overlays/actors/ovl_En_Syateki_Niw/z_en_syateki_niw.c @@ -103,6 +103,8 @@ void EnSyatekiNiw_Destroy(Actor* thisx, PlayState* play) { EnSyatekiNiw* this = (EnSyatekiNiw*)thisx; Collider_DestroyCylinder(play, &this->collider); + + ResourceMgr_UnregisterSkeleton(&this->skelAnime); } void func_80B11A94(EnSyatekiNiw* this, PlayState* play, s16 arg2) { diff --git a/soh/src/overlays/actors/ovl_En_Ta/z_en_ta.c b/soh/src/overlays/actors/ovl_En_Ta/z_en_ta.c index d3fb9ddba..3fa33d6b4 100644 --- a/soh/src/overlays/actors/ovl_En_Ta/z_en_ta.c +++ b/soh/src/overlays/actors/ovl_En_Ta/z_en_ta.c @@ -243,6 +243,8 @@ void EnTa_Destroy(Actor* thisx, PlayState* play) { if (this->unk_2E0 & 0x200) { func_800F5B58(); } + + ResourceMgr_UnregisterSkeleton(&this->skelAnime); } s32 func_80B142F4(EnTa* this, PlayState* play, u16 textId) { diff --git a/soh/src/overlays/actors/ovl_En_Takara_Man/z_en_takara_man.c b/soh/src/overlays/actors/ovl_En_Takara_Man/z_en_takara_man.c index 975bb3f14..8a208f104 100644 --- a/soh/src/overlays/actors/ovl_En_Takara_Man/z_en_takara_man.c +++ b/soh/src/overlays/actors/ovl_En_Takara_Man/z_en_takara_man.c @@ -14,6 +14,7 @@ void EnTakaraMan_Init(Actor* thisx, PlayState* play); void EnTakaraMan_Reset(Actor* thisx, PlayState* play); void EnTakaraMan_Update(Actor* thisx, PlayState* play); void EnTakaraMan_Draw(Actor* thisx, PlayState* play); +void EnTakaraMan_Destroy(Actor* thisx, PlayState* play); void func_80B176E0(EnTakaraMan* this, PlayState* play); void func_80B1778C(EnTakaraMan* this, PlayState* play); @@ -29,7 +30,7 @@ const ActorInit En_Takara_Man_InitVars = { OBJECT_TS, sizeof(EnTakaraMan), (ActorFunc)EnTakaraMan_Init, - NULL, + (ActorFunc)EnTakaraMan_Destroy, (ActorFunc)EnTakaraMan_Update, (ActorFunc)EnTakaraMan_Draw, (ActorResetFunc)EnTakaraMan_Reset, @@ -72,6 +73,12 @@ void EnTakaraMan_Init(Actor* thisx, PlayState* play) { this->actionFunc = func_80B176E0; } +void EnTakaraMan_Destroy(Actor* thisx, PlayState* play) { + EnTakaraMan* this = (EnTakaraMan*)thisx; + + ResourceMgr_UnregisterSkeleton(&this->skelAnime); +} + void func_80B176E0(EnTakaraMan* this, PlayState* play) { f32 frameCount = Animation_GetLastFrame(&object_ts_Anim_000498); diff --git a/soh/src/overlays/actors/ovl_En_Test/z_en_test.c b/soh/src/overlays/actors/ovl_En_Test/z_en_test.c index e30f94c48..be9033e96 100644 --- a/soh/src/overlays/actors/ovl_En_Test/z_en_test.c +++ b/soh/src/overlays/actors/ovl_En_Test/z_en_test.c @@ -321,6 +321,9 @@ void EnTest_Destroy(Actor* thisx, PlayState* play) { Collider_DestroyCylinder(play, &this->shieldCollider); Collider_DestroyCylinder(play, &this->bodyCollider); Collider_DestroyQuad(play, &this->swordCollider); + + ResourceMgr_UnregisterSkeleton(&this->skelAnime); + ResourceMgr_UnregisterSkeleton(&this->upperSkelanime); } /** diff --git a/soh/src/overlays/actors/ovl_En_Tite/z_en_tite.c b/soh/src/overlays/actors/ovl_En_Tite/z_en_tite.c index f64d81163..c4741204b 100644 --- a/soh/src/overlays/actors/ovl_En_Tite/z_en_tite.c +++ b/soh/src/overlays/actors/ovl_En_Tite/z_en_tite.c @@ -217,6 +217,8 @@ void EnTite_Destroy(Actor* thisx, PlayState* play) { osSyncPrintf("\n\n"); } Collider_DestroyJntSph(play, &this->collider); + + ResourceMgr_UnregisterSkeleton(&this->skelAnime); } void EnTite_SetupIdle(EnTite* this) { diff --git a/soh/src/overlays/actors/ovl_En_Tk/z_en_tk.c b/soh/src/overlays/actors/ovl_En_Tk/z_en_tk.c index 60c78d04f..5bcbe6e22 100644 --- a/soh/src/overlays/actors/ovl_En_Tk/z_en_tk.c +++ b/soh/src/overlays/actors/ovl_En_Tk/z_en_tk.c @@ -526,6 +526,8 @@ void EnTk_Destroy(Actor* thisx, PlayState* play) { EnTk* this = (EnTk*)thisx; Collider_DestroyCylinder(play, &this->collider); + + ResourceMgr_UnregisterSkeleton(&this->skelAnime); } void EnTk_Rest(EnTk* this, PlayState* play) { diff --git a/soh/src/overlays/actors/ovl_En_Toryo/z_en_toryo.c b/soh/src/overlays/actors/ovl_En_Toryo/z_en_toryo.c index 7ec5db7e5..704790caf 100644 --- a/soh/src/overlays/actors/ovl_En_Toryo/z_en_toryo.c +++ b/soh/src/overlays/actors/ovl_En_Toryo/z_en_toryo.c @@ -138,6 +138,8 @@ void EnToryo_Destroy(Actor* thisx, PlayState* play) { EnToryo* this = (EnToryo*)thisx; Collider_DestroyCylinder(play, &this->collider); + + ResourceMgr_UnregisterSkeleton(&this->skelAnime); } s32 func_80B203D8(EnToryo* this, PlayState* play) { diff --git a/soh/src/overlays/actors/ovl_En_Tr/z_en_tr.c b/soh/src/overlays/actors/ovl_En_Tr/z_en_tr.c index e9b2f6db4..6cc2e90c9 100644 --- a/soh/src/overlays/actors/ovl_En_Tr/z_en_tr.c +++ b/soh/src/overlays/actors/ovl_En_Tr/z_en_tr.c @@ -120,6 +120,9 @@ void EnTr_Init(Actor* thisx, PlayState* play) { } void EnTr_Destroy(Actor* thisx, PlayState* play) { + EnTr* this = (EnTr*)thisx; + + ResourceMgr_UnregisterSkeleton(&this->skelAnime); } void EnTr_CrySpellcast(EnTr* this, PlayState* play) { diff --git a/soh/src/overlays/actors/ovl_En_Vali/z_en_vali.c b/soh/src/overlays/actors/ovl_En_Vali/z_en_vali.c index 90254a29e..5ca56ead7 100644 --- a/soh/src/overlays/actors/ovl_En_Vali/z_en_vali.c +++ b/soh/src/overlays/actors/ovl_En_Vali/z_en_vali.c @@ -171,6 +171,8 @@ void EnVali_Destroy(Actor* thisx, PlayState* play) { Collider_DestroyQuad(play, &this->leftArmCollider); Collider_DestroyQuad(play, &this->rightArmCollider); Collider_DestroyCylinder(play, &this->bodyCollider); + + ResourceMgr_UnregisterSkeleton(&this->skelAnime); } void EnVali_SetupLurk(EnVali* this) { diff --git a/soh/src/overlays/actors/ovl_En_Vm/z_en_vm.c b/soh/src/overlays/actors/ovl_En_Vm/z_en_vm.c index 2e88d20ad..4eac68129 100644 --- a/soh/src/overlays/actors/ovl_En_Vm/z_en_vm.c +++ b/soh/src/overlays/actors/ovl_En_Vm/z_en_vm.c @@ -162,6 +162,8 @@ void EnVm_Destroy(Actor* thisx, PlayState* play) { EnVm* this = (EnVm*)thisx; Collider_DestroyCylinder(play, &this->colliderCylinder); + + ResourceMgr_UnregisterSkeleton(&this->skelAnime); } void EnVm_SetupWait(EnVm* this) { diff --git a/soh/src/overlays/actors/ovl_En_Wallmas/z_en_wallmas.c b/soh/src/overlays/actors/ovl_En_Wallmas/z_en_wallmas.c index 07ee9fe23..c015413db 100644 --- a/soh/src/overlays/actors/ovl_En_Wallmas/z_en_wallmas.c +++ b/soh/src/overlays/actors/ovl_En_Wallmas/z_en_wallmas.c @@ -148,6 +148,8 @@ void EnWallmas_Destroy(Actor* thisx, PlayState* play) { EnWallmas* this = (EnWallmas*)thisx; Collider_DestroyCylinder(play, &this->collider); + + ResourceMgr_UnregisterSkeleton(&this->skelAnime); } void EnWallmas_TimerInit(EnWallmas* this, PlayState* play) { diff --git a/soh/src/overlays/actors/ovl_En_Weiyer/z_en_weiyer.c b/soh/src/overlays/actors/ovl_En_Weiyer/z_en_weiyer.c index 31ceb7071..f7a6d3638 100644 --- a/soh/src/overlays/actors/ovl_En_Weiyer/z_en_weiyer.c +++ b/soh/src/overlays/actors/ovl_En_Weiyer/z_en_weiyer.c @@ -118,6 +118,8 @@ void EnWeiyer_Destroy(Actor* thisx, PlayState* play) { EnWeiyer* this = (EnWeiyer*)thisx; Collider_DestroyCylinder(play, &this->collider); + + ResourceMgr_UnregisterSkeleton(&this->skelAnime); } void func_80B32384(EnWeiyer* this) { diff --git a/soh/src/overlays/actors/ovl_En_Wf/z_en_wf.c b/soh/src/overlays/actors/ovl_En_Wf/z_en_wf.c index 6ec5e00cc..522034446 100644 --- a/soh/src/overlays/actors/ovl_En_Wf/z_en_wf.c +++ b/soh/src/overlays/actors/ovl_En_Wf/z_en_wf.c @@ -279,6 +279,8 @@ void EnWf_Destroy(Actor* thisx, PlayState* play) { osSyncPrintf("\n\n"); } } + + ResourceMgr_UnregisterSkeleton(&this->skelAnime); } s32 EnWf_ChangeAction(PlayState* play, EnWf* this, s16 mustChoose) { diff --git a/soh/src/overlays/actors/ovl_En_Xc/z_en_xc.c b/soh/src/overlays/actors/ovl_En_Xc/z_en_xc.c index 89c478cb5..83d58e069 100644 --- a/soh/src/overlays/actors/ovl_En_Xc/z_en_xc.c +++ b/soh/src/overlays/actors/ovl_En_Xc/z_en_xc.c @@ -74,6 +74,8 @@ void EnXc_Destroy(Actor* thisx, PlayState* play) { EnXc* this = (EnXc*)thisx; Collider_DestroyCylinder(play, &this->collider); + + ResourceMgr_UnregisterSkeleton(&this->skelAnime); } void EnXc_CalculateHeadTurn(EnXc* this, PlayState* play) { diff --git a/soh/src/overlays/actors/ovl_En_Zf/z_en_zf.c b/soh/src/overlays/actors/ovl_En_Zf/z_en_zf.c index eeb88ea39..254d2a757 100644 --- a/soh/src/overlays/actors/ovl_En_Zf/z_en_zf.c +++ b/soh/src/overlays/actors/ovl_En_Zf/z_en_zf.c @@ -364,6 +364,8 @@ void EnZf_Destroy(Actor* thisx, PlayState* play) { Effect_Delete(play, this->blureIndex); Collider_DestroyCylinder(play, &this->bodyCollider); Collider_DestroyQuad(play, &this->swordCollider); + + ResourceMgr_UnregisterSkeleton(&this->skelAnime); } /** diff --git a/soh/src/overlays/actors/ovl_En_Zl3/z_en_zl3.c b/soh/src/overlays/actors/ovl_En_Zl3/z_en_zl3.c index a5d09cfbd..832fac222 100644 --- a/soh/src/overlays/actors/ovl_En_Zl3/z_en_zl3.c +++ b/soh/src/overlays/actors/ovl_En_Zl3/z_en_zl3.c @@ -84,6 +84,8 @@ void EnZl3_Destroy(Actor* thisx, PlayState* play) { EnZl3* this = (EnZl3*)thisx; Collider_DestroyCylinder(play, &this->collider); + + ResourceMgr_UnregisterSkeleton(&this->skelAnime); } void func_80B53468(void) { diff --git a/soh/src/overlays/actors/ovl_En_Zl4/z_en_zl4.c b/soh/src/overlays/actors/ovl_En_Zl4/z_en_zl4.c index 3d32a0524..2f6f05a63 100644 --- a/soh/src/overlays/actors/ovl_En_Zl4/z_en_zl4.c +++ b/soh/src/overlays/actors/ovl_En_Zl4/z_en_zl4.c @@ -422,6 +422,8 @@ void EnZl4_Destroy(Actor* thisx, PlayState* play) { EnZl4* this = (EnZl4*)thisx; Collider_DestroyCylinder(play, &this->collider); + + ResourceMgr_UnregisterSkeleton(&this->skelAnime); } s32 EnZl4_SetNextAnim(EnZl4* this, s32 nextAnim) { diff --git a/soh/src/overlays/actors/ovl_En_Zo/z_en_zo.c b/soh/src/overlays/actors/ovl_En_Zo/z_en_zo.c index 885c58708..8a413d1c3 100644 --- a/soh/src/overlays/actors/ovl_En_Zo/z_en_zo.c +++ b/soh/src/overlays/actors/ovl_En_Zo/z_en_zo.c @@ -615,6 +615,9 @@ void EnZo_Init(Actor* thisx, PlayState* play) { } void EnZo_Destroy(Actor* thisx, PlayState* play) { + EnZo* this = (EnZo*)thisx; + + ResourceMgr_UnregisterSkeleton(&this->skelAnime); } void EnZo_Standing(EnZo* this, PlayState* play) { diff --git a/soh/src/overlays/actors/ovl_player_actor/z_player.c b/soh/src/overlays/actors/ovl_player_actor/z_player.c index 58147efd5..bd41af963 100644 --- a/soh/src/overlays/actors/ovl_player_actor/z_player.c +++ b/soh/src/overlays/actors/ovl_player_actor/z_player.c @@ -11335,6 +11335,9 @@ void Player_Destroy(Actor* thisx, PlayState* play) { func_800876C8(play); gSaveContext.linkAge = play->linkAgeOnLoad; + + ResourceMgr_UnregisterSkeleton(&this->skelAnime); + ResourceMgr_UnregisterSkeleton(&this->skelAnime2); } //first person manipulate player actor diff --git a/soh/src/overlays/misc/ovl_kaleido_scope/z_kaleido_item.c b/soh/src/overlays/misc/ovl_kaleido_scope/z_kaleido_item.c index aae19180e..53bec6c7a 100644 --- a/soh/src/overlays/misc/ovl_kaleido_scope/z_kaleido_item.c +++ b/soh/src/overlays/misc/ovl_kaleido_scope/z_kaleido_item.c @@ -344,12 +344,6 @@ void KaleidoScope_DrawItemSelect(PlayState* play) { pauseCtx->cursorItem[PAUSE_ITEM] = cursorItem; pauseCtx->cursorSlot[PAUSE_ITEM] = cursorSlot; - gSlotAgeReqs[SLOT_TRADE_CHILD] = gItemAgeReqs[ITEM_MASK_BUNNY] = - ((CVarGetInteger("gMMBunnyHood", 0) || CVarGetInteger("gTimelessEquipment", 0)) && - INV_CONTENT(ITEM_TRADE_CHILD) == ITEM_MASK_BUNNY) - ? 9 - : 1; - if (!CHECK_SLOT_AGE(cursorSlot)) { pauseCtx->nameColorSet = 1; } @@ -405,6 +399,12 @@ void KaleidoScope_DrawItemSelect(PlayState* play) { } } gSelectingMask = cursorSlot == SLOT_TRADE_CHILD; + + gSlotAgeReqs[SLOT_TRADE_CHILD] = gItemAgeReqs[ITEM_MASK_BUNNY] = + ((CVarGetInteger("gMMBunnyHood", 0) || CVarGetInteger("gTimelessEquipment", 0)) && + INV_CONTENT(ITEM_TRADE_CHILD) == ITEM_MASK_BUNNY) + ? 9 + : 1; } if (gSaveContext.n64ddFlag && Randomizer_GetSettingValue(RSK_SHUFFLE_ADULT_TRADE) && cursorSlot == SLOT_TRADE_ADULT && CHECK_BTN_ALL(input->press.button, BTN_A)) { diff --git a/soh/src/overlays/misc/ovl_kaleido_scope/z_kaleido_scope.h b/soh/src/overlays/misc/ovl_kaleido_scope/z_kaleido_scope.h index ad53fdd3a..a3c97d9b2 100644 --- a/soh/src/overlays/misc/ovl_kaleido_scope/z_kaleido_scope.h +++ b/soh/src/overlays/misc/ovl_kaleido_scope/z_kaleido_scope.h @@ -7,7 +7,6 @@ extern u8 gAmmoItems[]; extern s16 D_8082AAEC[]; extern s16 D_8082AB2C[]; -extern u8 gSlotAgeReqs[]; extern u8 gEquipAgeReqs[][4]; extern u8 gAreaGsFlags[]; extern bool gSelectingMask;