diff --git a/BUILDING.md b/BUILDING.md index d37d7c0d6f..726b8a99c1 100644 --- a/BUILDING.md +++ b/BUILDING.md @@ -2,9 +2,9 @@ ## Windows - 1. Install [Python](https://www.python.org/ftp/python/3.10.2/python-3.10.2-amd64.exe) + 1. Requires [Python](https://www.python.org/downloads/) >= 3.6. 2. Install [Visual Studio 2022 Community Edition](https://visualstudio.microsoft.com/vs/community/) - 2b. In the Visual Studio Installer, install `MSVC v142 - VS 2019 C++`. + 3. In the Visual Studio Installer, install `MSVC v142 - VS 2019 C++`. 4. Clone the Ship of Harkinian repository. 5. Place one or more [compatible](#compatible-roms) roms in the `OTRExporter` directory with namings of your choice. 6. Run `OTRExporter/OTRExporter.sln`. @@ -41,9 +41,9 @@ cp /usr/local/lib/libGLEW.a external cd soh # Extract the assets/Compile the exporter/Run the exporter -make setup -j$(nproc) +make setup -j$(nproc) OPTFLAGS=-O0 DEBUG=0 # Compile the code -make -j $(nproc) +make -j $(nproc) OPTFLAGS=-O0 DEBUG=0 ``` # Compatible Roms @@ -51,3 +51,16 @@ make -j $(nproc) OOT_PAL_GC checksum 0x09465AC3 OOT_PAL_GC_DBG1 checksum 0x871E1C92 (debug non-master quest) ``` + +# OTRExporter Usage + +The OTRExporter exports an `oot.otr` archive file which Ship of Harkinian requires to play. + +Use the `extract_assets.py` script file to run the exporter using any of the following methods: +1) Double click on the script after placing one or more roms in the directory. +2) Drag & Drop a rom onto the script. +3) In a terminal run `python3 extract_assets.py` after placing one or more roms in the directory. +4) In a terminal run `python3 extract_assets.py ` + +If the script finds multiple roms the user is prompted which to use. Selection is done using the number keys and then pressing the carriage return key. + diff --git a/OTRGui/src/game/game.cpp b/OTRGui/src/game/game.cpp index e995b339c0..022a7788ee 100644 --- a/OTRGui/src/game/game.cpp +++ b/OTRGui/src/game/game.cpp @@ -188,7 +188,7 @@ void OTRGame::draw() { sohFolder = path; } - if (UIUtils::GuiIconButton("Cartridge", "Open\nOoT Rom", 32, 50, currentStep != NULLSTR, "Select an Ocarina of Time\nMaster Quest or Vanilla Debug Rom\n\nYou can dump it or lend one from Nintendo")) { + if (UIUtils::GuiIconButton("Cartridge", "Open\nOoT Rom", 32, 50, currentStep != NULLSTR, "Select an Ocarina of Time\nGameCube PAL or Vanilla Debug Rom\n\nYou can dump it or lend one from Nintendo")) { const std::string path = NativeFS->LaunchFileExplorer(LaunchType::FILE); if (path != NULLSTR) { const std::string patched_n64 = std::string(patched_rom); diff --git a/README.md b/README.md index 3ce3fb21bf..92fca0615b 100644 --- a/README.md +++ b/README.md @@ -6,7 +6,7 @@ The Ship does not include assets and as such requires a prior copy of the game t ## Quick Start -1) Download The Ship of Harkinian from Discord. +1) Download The Ship of Harkinian from [Discord](https://discord.com/invite/BtBmd55HVH). 2) Requires a supported copy of the game (See supported games below). 3) Use the OTRGui to generate an `oot.otr` archive file. 4) Launch `soh.exe` @@ -18,9 +18,9 @@ Build team: `zelda@srd022j` Build date: `03-02-21 00:49:18` (year-month-day) sha1: cee6bc3c2a634b41728f2af8da54d9bf8cc14099 ``` -Ocarina of Time Pal Gamecube +Ocarina of Time PAL GameCube ``` -sha1: d0c95b2cb3c6682a171db267932af7af8cf5fa82 +sha1: 0227d7c0074f2d0ac935631990da8ec5914597b4 ``` Congratulations, you are now sailing with the Ship of Harkinian! Have fun! @@ -64,42 +64,51 @@ Refer to the [building instructions](BUILDING.md) to compile SoH. - Affirm that you have an `/assets` folder filled with XMLs in the same directory as OTRGui.exe - Affirm that `zapd.exe` exists in the `/assets/extractor` folder +## Nightly Builds +Nightly builds of Ship of Harkinian are available at [https://builds.shipofharkinian.com/job/SoH_Multibranch/job/develop] + ## The Harbour Masters Are... - Kenix | Lead Developer/Public Relations - Resource Management Programmer, Audio System Programmer, and General Programmer - Jack Walker | Lead Developer - OTR Format Programmer, Resource Load Programmer, and General Programmer - Louist103 | Developer - Save System Programmer and General Programmer - Emil | Developer - Fast3D Programmer - m4xw | Developer - Shipwright, Throwing Baguettes, and General Programmer - MelonSpeedruns | Developer - General Programmer - Rozlette | Developer - General Programmer - JoshDuMan | Developer - General Programmer - KiritoDev/Lywx | Developer - General Programmer - Theo3 | Developer - General Programmer - Random06457 | Developer - Linux Build + Kenix | Lead Developer/Public Relations - Resource Management Programmer, Audio System Programmer, and General Programmer + Jack Walker | Lead Developer - OTR Format Programmer, Resource Load Programmer, and General Programmer + Louist103 | Developer - Save System Programmer and General Programmer + Emil | Developer - Fast3D Programmer + m4xw | Developer - Shipwright, Throwing Baguettes, and General Programmer + MelonSpeedruns | Developer - General Programmer + Rozlette | Developer - General Programmer + JoshDuMan | Developer - General Programmer + KiritoDev/Lywx | Developer - General Programmer + Theo3 | Developer - General Programmer + Random06457 | Developer - Linux Build ## Special Thanks - Decomp & ZAPD | Made this project even possible in the first place! - MNGoldenEagle | Patiently explained audio data formats, encouragement, and founding ZSO which was the first source of the game's code and resource format documentation. - Rrrrry123 | Speedbunner, encouragement, and community moderation - Fierce deity | Encouragement and community moderation - mzxrules | For his contributions to decomp - zel. | For his contributions to decomp - Aloxado | Developer - General Programmer - MegaMech | Developer - General Programmer - Revo | Tester - GCC support and General Testing - zfg | Tester - General Testing - Horseless Headman | Tester - General Testing - Steven Pritchett | Tester - General Testing - Trenton May | Tester - General Testing - Zeldaboy14 | Tester - General Testing, encouragement, and community moderation - Koby Howell | Tester - General Testing - Logg | Tester - General Testing - Taylor Daley | Graphic Design - Can't Sleep | Graphic Design - MicTheMicrophone | Voice actor for the King - Amphibibro | Voice actor for Link - -Lemons + Decomp & ZAPD | Made this project even possible in the first place! + MNGoldenEagle | Patiently explained audio data formats, encouragement, and founding ZSO which was the first source of the game's code and resource format documentation. + Rrrrry123 | Speedbunner, encouragement, and community moderation + Fierce deity | Encouragement and community moderation + mzxrules | For his contributions to decomp + zel. | For his contributions to decomp + Aloxado | Developer - General Programmer + MegaMech | Developer - General Programmer + Revo | Tester - GCC support and General Testing + zfg | Tester - General Testing + Horseless Headman | Tester - General Testing + Steven Pritchett | Tester - General Testing + Trenton May | Tester - General Testing + Zeldaboy14 | Tester - General Testing, encouragement, and community moderation + Koby Howell | Tester - General Testing + Logg | Tester - General Testing + Taylor Daley | Graphic Design + Can't Sleep | Graphic Design + +## Video Credits + Kenix | Producer / Writer + rainbow_fash | Executive Producer + TheLegendOfXela | Editor + MicTheMicrophone | Gwonam / The King + Amphibibro | Link + AceHeart | Zelda + +###### Lemons \ No newline at end of file diff --git a/libultraship/libultraship/Archive.cpp b/libultraship/libultraship/Archive.cpp index 1e8f8d5973..e4646c3056 100644 --- a/libultraship/libultraship/Archive.cpp +++ b/libultraship/libultraship/Archive.cpp @@ -204,7 +204,7 @@ namespace Ship { return true; } - std::vector Archive::ListFiles(const std::string& searchMask) { + std::vector Archive::ListFiles(const std::string& searchMask) const { auto fileList = std::vector(); SFILE_FIND_DATA findContext; HANDLE hFind; @@ -248,7 +248,7 @@ namespace Ship { return fileList; } - bool Archive::HasFile(const std::string& filename) { + bool Archive::HasFile(const std::string& filename) const { bool result = false; auto start = std::chrono::steady_clock::now(); @@ -267,8 +267,9 @@ namespace Ship { return result; } - std::string Archive::HashToString(uint64_t hash) { - return hashes[hash]; + const std::string* Archive::HashToString(uint64_t hash) const { + auto it = hashes.find(hash); + return it != hashes.end() ? &it->second : nullptr; } bool Archive::Load(bool enableWriting, bool genCRCMap) { diff --git a/libultraship/libultraship/Archive.h b/libultraship/libultraship/Archive.h index f8fe3e0fd4..b21ad9c735 100644 --- a/libultraship/libultraship/Archive.h +++ b/libultraship/libultraship/Archive.h @@ -34,9 +34,9 @@ namespace Ship bool AddFile(const std::string& path, uintptr_t fileData, DWORD dwFileSize); bool RemoveFile(const std::string& path); bool RenameFile(const std::string& oldPath, const std::string& newPath); - std::vector ListFiles(const std::string& searchMask); - bool HasFile(const std::string& searchMask); - std::string HashToString(uint64_t hash); + std::vector ListFiles(const std::string& searchMask) const; + bool HasFile(const std::string& searchMask) const; + const std::string* HashToString(uint64_t hash) const; protected: bool Load(bool enableWriting, bool genCRCMap); bool Unload(); diff --git a/libultraship/libultraship/ConfigFile.cpp b/libultraship/libultraship/ConfigFile.cpp index 2e9a8cc945..8a07946438 100644 --- a/libultraship/libultraship/ConfigFile.cpp +++ b/libultraship/libultraship/ConfigFile.cpp @@ -72,7 +72,7 @@ namespace Ship { (*this)["WINDOW"]["FULLSCREEN WIDTH"] = std::to_string(1920); (*this)["WINDOW"]["FULLSCREEN HEIGHT"] = std::to_string(1080); (*this)["WINDOW"]["FULLSCREEN"] = std::to_string(false); - (*this)["WINDOW"]["GFX BACKEND"] = "sdl"; + (*this)["WINDOW"]["GFX BACKEND"] = ""; (*this)["KEYBOARD CONTROLLER BINDING 1"][STR(BTN_CRIGHT)] = std::to_string(0x14D); (*this)["KEYBOARD CONTROLLER BINDING 1"][STR(BTN_CLEFT)] = std::to_string(0x14B); diff --git a/libultraship/libultraship/GameOverlay.cpp b/libultraship/libultraship/GameOverlay.cpp index 02e089366f..042ff980c0 100644 --- a/libultraship/libultraship/GameOverlay.cpp +++ b/libultraship/libultraship/GameOverlay.cpp @@ -8,6 +8,7 @@ #include "SohImGuiImpl.h" #include "TextureMod.h" #include "Lib/ImGui/imgui_internal.h" +#include "Utils/StringHelper.h" void Ship::GameOverlay::LoadFont(const std::string& name, const std::string& path, float fontSize) { ImGuiIO& io = ImGui::GetIO(); @@ -50,10 +51,21 @@ void Ship::GameOverlay::TextDrawNotification(float duration, bool shadow, const vsnprintf(buf, IM_ARRAYSIZE(buf), fmt, args); buf[IM_ARRAYSIZE(buf) - 1] = 0; va_end(args); - - this->RegisteredOverlays[fmt] = new Overlay({ OverlayType::NOTIFICATION, ImStrdup(buf), duration, duration }); + this->RegisteredOverlays[StringHelper::Sprintf("NotificationID:%d%d", rand(), this->RegisteredOverlays.size())] = new Overlay({ OverlayType::NOTIFICATION, ImStrdup(buf), duration, duration }); + NeedsCleanup = true; } +void Ship::GameOverlay::CleanupNotifications() { + if(!NeedsCleanup) return; + for (auto it = this->RegisteredOverlays.begin(); it != this->RegisteredOverlays.end(); ) { + if (it->second->type == OverlayType::NOTIFICATION && it->second->duration <= 0.0f) { + it = this->RegisteredOverlays.erase(it); + } else { + ++it; + } + } + NeedsCleanup = false; +} float Ship::GameOverlay::GetScreenWidth() { const ImGuiViewport* viewport = ImGui::GetMainViewport(); @@ -137,6 +149,8 @@ void Ship::GameOverlay::Draw() { ImGui::Begin("SoHOverlay", nullptr, ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoBackground | ImGuiWindowFlags_NoScrollbar | ImGuiWindowFlags_NoSavedSettings | ImGuiWindowFlags_NoInputs); + this->CleanupNotifications(); + float textY = 50; float notY = 0; diff --git a/libultraship/libultraship/GameOverlay.h b/libultraship/libultraship/GameOverlay.h index e3161bca39..605cd5898f 100644 --- a/libultraship/libultraship/GameOverlay.h +++ b/libultraship/libultraship/GameOverlay.h @@ -33,6 +33,8 @@ namespace Ship { void TextDraw(float x, float y, bool shadow, ImVec4 color, const char* text, ...); void TextDrawNotification(float duration, bool shadow, const char* fmt, ...); private: + bool NeedsCleanup = false; + void CleanupNotifications(); void LoadFont(const std::string& name, const std::string& path, float fontSize); }; diff --git a/libultraship/libultraship/Lib/Fast3D/gfx_pc.cpp b/libultraship/libultraship/Lib/Fast3D/gfx_pc.cpp index da39c0a4dc..6738f28fa8 100644 --- a/libultraship/libultraship/Lib/Fast3D/gfx_pc.cpp +++ b/libultraship/libultraship/Lib/Fast3D/gfx_pc.cpp @@ -36,7 +36,7 @@ // OTRTODO: fix header files for these extern "C" { - char* ResourceMgr_GetNameByCRC(uint64_t crc, char* alloc); + const char* ResourceMgr_GetNameByCRC(uint64_t crc); int32_t* ResourceMgr_LoadMtxByCRC(uint64_t crc); Vtx* ResourceMgr_LoadVtxByCRC(uint64_t crc); Gfx* ResourceMgr_LoadGfxByCRC(uint64_t crc); @@ -88,7 +88,7 @@ struct LoadedVertex { static struct { TextureCacheMap map; - list lru; + list lru; vector free_texture_ids; } gfx_texture_cache; @@ -134,14 +134,14 @@ static struct RDP { const uint8_t *addr; uint8_t siz; uint32_t width; - char* otr_path; + const char* otr_path; } texture_to_load; struct { const uint8_t *addr; uint32_t size_bytes; uint32_t full_image_line_size_bytes; uint32_t line_size_bytes; - char* otr_path; + const char* otr_path; } loaded_texture[2]; struct { uint8_t fmt; @@ -190,8 +190,12 @@ static int game_framebuffer_msaa_resolved; uint32_t gfx_msaa_level = 1; +static bool has_drawn_imgui_menu; + static bool dropped_frame; +static const std::unordered_map *current_mtx_replacements; + static float buf_vbo[MAX_BUFFERED * (32 * 3)]; // 3 vertices in a triangle and 32 floats per vtx static size_t buf_vbo_len; static size_t buf_vbo_num_tris; @@ -523,18 +527,18 @@ static bool gfx_texture_cache_lookup(int i, int tile) { key = { orig_addr, { }, fmt, siz, palette_index }; } - auto it = gfx_texture_cache.map.find(key); + TextureCacheMap::iterator it = gfx_texture_cache.map.find(key); if (it != gfx_texture_cache.map.end()) { gfx_rapi->select_texture(i, it->second.texture_id); *n = &*it; - gfx_texture_cache.lru.splice(gfx_texture_cache.lru.end(), gfx_texture_cache.lru, *(list::iterator*)&it->second.lru_location); // move to back + gfx_texture_cache.lru.splice(gfx_texture_cache.lru.end(), gfx_texture_cache.lru, it->second.lru_location); // move to back return true; } if (gfx_texture_cache.map.size() >= TEXTURE_CACHE_MAX_SIZE) { // Remove the texture that was least recently used - it = gfx_texture_cache.lru.front(); + it = gfx_texture_cache.lru.front().it; gfx_texture_cache.free_texture_ids.push_back(it->second.texture_id); gfx_texture_cache.map.erase(it); gfx_texture_cache.lru.pop_front(); @@ -551,7 +555,7 @@ static bool gfx_texture_cache_lookup(int i, int tile) { it = gfx_texture_cache.map.insert(make_pair(key, TextureCacheValue())).first; TextureCacheNode* node = &*it; node->second.texture_id = texture_id; - *(list::iterator*)&node->second.lru_location = gfx_texture_cache.lru.insert(gfx_texture_cache.lru.end(), it); + node->second.lru_location = gfx_texture_cache.lru.insert(gfx_texture_cache.lru.end(), { it }); gfx_rapi->select_texture(i, texture_id); gfx_rapi->set_sampler_parameters(i, false, 0, 0); @@ -567,7 +571,7 @@ static void gfx_texture_cache_delete(const uint8_t* orig_addr) bool again = false; for (auto it = gfx_texture_cache.map.begin(bucket); it != gfx_texture_cache.map.end(bucket); ++it) { if (it->first.texture_addr == orig_addr) { - gfx_texture_cache.lru.erase(*(list::iterator*)&it->second.lru_location); + gfx_texture_cache.lru.erase(it->second.lru_location); gfx_texture_cache.free_texture_ids.push_back(it->second.texture_id); gfx_texture_cache.map.erase(it->first); again = true; @@ -916,20 +920,31 @@ static void gfx_matrix_mul(float res[4][4], const float a[4][4], const float b[4 static void gfx_sp_matrix(uint8_t parameters, const int32_t *addr) { float matrix[4][4]; -#ifndef GBI_FLOATS - // Original GBI where fixed point matrices are used - for (int i = 0; i < 4; i++) { - for (int j = 0; j < 4; j += 2) { - int32_t int_part = addr[i * 2 + j / 2]; - uint32_t frac_part = addr[8 + i * 2 + j / 2]; - matrix[i][j] = (int32_t)((int_part & 0xffff0000) | (frac_part >> 16)) / 65536.0f; - matrix[i][j + 1] = (int32_t)((int_part << 16) | (frac_part & 0xffff)) / 65536.0f; + + if (auto it = current_mtx_replacements->find((Mtx *)addr); it != current_mtx_replacements->end()) { + for (int i = 0; i < 4; i++) { + for (int j = 0; j < 4; j++) { + float v = it->second.mf[i][j]; + int as_int = (int)(v * 65536.0f); + matrix[i][j] = as_int * (1.0f / 65536.0f); + } + } + } else { +#ifndef GBI_FLOATS + // Original GBI where fixed point matrices are used + for (int i = 0; i < 4; i++) { + for (int j = 0; j < 4; j += 2) { + int32_t int_part = addr[i * 2 + j / 2]; + uint32_t frac_part = addr[8 + i * 2 + j / 2]; + matrix[i][j] = (int32_t)((int_part & 0xffff0000) | (frac_part >> 16)) / 65536.0f; + matrix[i][j + 1] = (int32_t)((int_part << 16) | (frac_part & 0xffff)) / 65536.0f; + } } - } #else - // For a modified GBI where fixed point values are replaced with floats - memcpy(matrix, addr, sizeof(matrix)); + // For a modified GBI where fixed point values are replaced with floats + memcpy(matrix, addr, sizeof(matrix)); #endif + } if (parameters & G_MTX_PROJECTION) { if (parameters & G_MTX_LOAD) { @@ -1621,11 +1636,11 @@ static void gfx_dp_set_scissor(uint32_t mode, uint32_t ulx, uint32_t uly, uint32 rdp.viewport_or_scissor_changed = true; } -static void gfx_dp_set_texture_image(uint32_t format, uint32_t size, uint32_t width, const void* addr, char* otr_path) { +static void gfx_dp_set_texture_image(uint32_t format, uint32_t size, uint32_t width, const void* addr, const char* otr_path) { rdp.texture_to_load.addr = (const uint8_t*)addr; rdp.texture_to_load.siz = size; rdp.texture_to_load.width = width; - if ( otr_path != nullptr && !strncmp(otr_path, "__OTR__", 7)) otr_path = otr_path + 7; + if (otr_path != nullptr && !strncmp(otr_path, "__OTR__", 7)) otr_path = otr_path + 7; rdp.texture_to_load.otr_path = otr_path; } @@ -2099,7 +2114,7 @@ static void gfx_run_dl(Gfx* cmd) { //puts("dl"); int dummy = 0; char dlName[128]; - char fileName[128]; + const char* fileName; Gfx* dListStart = cmd; uint64_t ourHash = -1; @@ -2422,7 +2437,7 @@ static void gfx_run_dl(Gfx* cmd) { uintptr_t addr = cmd->words.w1; cmd++; uint64_t hash = ((uint64_t)cmd->words.w0 << 32) + (uint64_t)cmd->words.w1; - ResourceMgr_GetNameByCRC(hash, fileName); + fileName = ResourceMgr_GetNameByCRC(hash); #if _DEBUG && 0 char* tex = ResourceMgr_LoadTexByCRC(hash); ResourceMgr_GetNameByCRC(hash, fileName); @@ -2708,6 +2723,7 @@ void gfx_start_frame(void) { gfx_wapi->handle_events(); gfx_wapi->get_dimensions(&gfx_current_window_dimensions.width, &gfx_current_window_dimensions.height); SohImGui::DrawMainMenuAndCalculateGameSize(); + has_drawn_imgui_menu = true; if (gfx_current_dimensions.height == 0) { // Avoid division by zero gfx_current_dimensions.height = 1; @@ -2746,7 +2762,7 @@ void gfx_start_frame(void) { fbActive = 0; } -void gfx_run(Gfx *commands) { +void gfx_run(Gfx *commands, const std::unordered_map& mtx_replacements) { gfx_sp_reset(); //puts("New frame"); @@ -2755,12 +2771,21 @@ void gfx_run(Gfx *commands) { if (!gfx_wapi->start_frame()) { dropped_frame = true; - SohImGui::DrawFramebufferAndGameInput(); - SohImGui::CancelFrame(); + if (has_drawn_imgui_menu) { + SohImGui::DrawFramebufferAndGameInput(); + SohImGui::CancelFrame(); + has_drawn_imgui_menu = false; + } return; } dropped_frame = false; + if (!has_drawn_imgui_menu) { + SohImGui::DrawMainMenuAndCalculateGameSize(); + } + + current_mtx_replacements = &mtx_replacements; + double t0 = gfx_wapi->get_time(); gfx_rapi->update_framebuffer_parameters(0, gfx_current_window_dimensions.width, gfx_current_window_dimensions.height, 1, false, true, true, !game_renders_to_framebuffer); gfx_rapi->start_frame(); @@ -2792,6 +2817,7 @@ void gfx_run(Gfx *commands) { //printf("Process %f %f\n", t1, t1 - t0); gfx_rapi->end_frame(); gfx_wapi->swap_buffers_begin(); + has_drawn_imgui_menu = false; } void gfx_end_frame(void) { diff --git a/libultraship/libultraship/Lib/Fast3D/gfx_pc.h b/libultraship/libultraship/Lib/Fast3D/gfx_pc.h index d9cd9be8de..2ecb68898e 100644 --- a/libultraship/libultraship/Lib/Fast3D/gfx_pc.h +++ b/libultraship/libultraship/Lib/Fast3D/gfx_pc.h @@ -6,6 +6,8 @@ #include #include +#include "U64/PR/ultra64/types.h" + struct GfxRenderingAPI; struct GfxWindowManagerAPI; @@ -44,34 +46,31 @@ struct TextureCacheValue { uint8_t cms, cmt; bool linear_filter; - // Old versions of libstdc++ fail to compile this -#ifdef _MSC_VER - std::list::iterator lru_location; -#else - std::list::iterator lru_location; -#endif + std::list::iterator lru_location; +}; + +struct TextureCacheMapIter { + TextureCacheMap::iterator it; }; -#ifdef __cplusplus extern "C" { -#endif + extern struct GfxDimensions gfx_current_window_dimensions; // The dimensions of the window extern struct GfxDimensions gfx_current_dimensions; // The dimensions of the draw area the game draws to, before scaling (if applicable) extern struct XYWidthHeight gfx_current_game_window_viewport; // The area of the window the game is drawn to, (0, 0) is top-left corner extern uint32_t gfx_msaa_level; +} + void gfx_init(struct GfxWindowManagerAPI* wapi, struct GfxRenderingAPI* rapi, const char* game_name, bool start_in_fullscreen); struct GfxRenderingAPI* gfx_get_current_rendering_api(void); void gfx_start_frame(void); -void gfx_run(Gfx* commands); +void gfx_run(Gfx* commands, const std::unordered_map& mtx_replacements); void gfx_end_frame(void); void gfx_set_framedivisor(int); void gfx_texture_cache_clear(); -int gfx_create_framebuffer(uint32_t width, uint32_t height); +extern "C" int gfx_create_framebuffer(uint32_t width, uint32_t height); void gfx_get_pixel_depth_prepare(float x, float y); uint16_t gfx_get_pixel_depth(float x, float y); -#ifdef __cplusplus -} -#endif #endif diff --git a/libultraship/libultraship/Resource.cpp b/libultraship/libultraship/Resource.cpp index 04fc2bfbbe..66a1f41991 100644 --- a/libultraship/libultraship/Resource.cpp +++ b/libultraship/libultraship/Resource.cpp @@ -40,8 +40,11 @@ namespace Ship for (size_t i = 0; i < patches.size(); i++) { - std::string hashStr = resMgr->HashToString(patches[i].crc); - auto resShared = resMgr->GetCachedFile(hashStr); + const std::string* hashStr = resMgr->HashToString(patches[i].crc); + if (hashStr == nullptr) + continue; + + auto resShared = resMgr->GetCachedFile(hashStr->c_str()); if (resShared != nullptr) { auto res = (Ship::DisplayList*)resShared.get(); diff --git a/libultraship/libultraship/ResourceMgr.cpp b/libultraship/libultraship/ResourceMgr.cpp index de0296d125..0033ef4423 100644 --- a/libultraship/libultraship/ResourceMgr.cpp +++ b/libultraship/libultraship/ResourceMgr.cpp @@ -215,7 +215,7 @@ namespace Ship { return ToLoad; } - std::shared_ptr ResourceMgr::GetCachedFile(std::string FilePath) { + std::shared_ptr ResourceMgr::GetCachedFile(const char* FilePath) const { auto resCacheFind = ResourceCache.find(FilePath); if (resCacheFind != ResourceCache.end() && @@ -227,8 +227,13 @@ namespace Ship { return nullptr; } - std::shared_ptr ResourceMgr::LoadResource(std::string FilePath) { - auto Promise = LoadResourceAsync(FilePath); + std::shared_ptr ResourceMgr::LoadResource(const char* FilePath) { + auto Res = LoadResourceAsync(FilePath); + + if (std::holds_alternative>(Res)) + return std::get>(Res); + + auto& Promise = std::get>(Res); if (!Promise->bHasResourceLoaded) { @@ -241,21 +246,18 @@ namespace Ship { return Promise->resource; } - std::shared_ptr ResourceMgr::LoadResourceAsync(std::string FilePath) { - StringHelper::ReplaceOriginal(FilePath, "\\", "/"); - - if (StringHelper::StartsWith(FilePath, "__OTR__")) - FilePath = StringHelper::Split(FilePath, "__OTR__")[1]; - - std::shared_ptr Promise = std::make_shared(); + std::variant, std::shared_ptr> ResourceMgr::LoadResourceAsync(const char* FilePath) { + if (FilePath[0] == '_' && FilePath[1] == '_' && FilePath[2] == 'O' && FilePath[3] == 'T' && FilePath[4] == 'R' && FilePath[5] == '_' && FilePath[6] == '_') + FilePath += 7; const std::lock_guard ResLock(ResourceLoadMutex); auto resCacheFind = ResourceCache.find(FilePath); if (resCacheFind == ResourceCache.end() || resCacheFind->second->isDirty/* || !FileData->bIsLoaded*/) { if (resCacheFind == ResourceCache.end()) { - SPDLOG_TRACE("Cache miss on Resource load: {}", FilePath.c_str()); + SPDLOG_TRACE("Cache miss on Resource load: {}", FilePath); } + std::shared_ptr Promise = std::make_shared(); std::shared_ptr FileData = LoadFile(FilePath); Promise->file = FileData; @@ -269,12 +271,13 @@ namespace Ship { ResourceLoadQueue.push(Promise); ResourceLoadNotifier.notify_all(); } - } else { - Promise->bHasResourceLoaded = true; - Promise->resource = resCacheFind->second; - } - return Promise; + return Promise; + } + else + { + return resCacheFind->second; + } } std::shared_ptr>> ResourceMgr::CacheDirectoryAsync(std::string SearchMask) { @@ -282,10 +285,15 @@ namespace Ship { auto fileList = OTR->ListFiles(SearchMask); for (DWORD i = 0; i < fileList.size(); i++) { - auto file = LoadResourceAsync(fileList.operator[](i).cFileName); - if (file != nullptr) { - loadedList->push_back(file); + auto resource = LoadResourceAsync(fileList.operator[](i).cFileName); + if (std::holds_alternative>(resource)) + { + auto promise = std::make_shared(); + promise->bHasResourceLoaded = true; + promise->resource = std::get>(resource); + resource = promise; } + loadedList->push_back(std::get>(resource)); } return loadedList; @@ -335,7 +343,7 @@ namespace Ship { ResourceCache.clear(); } - std::string ResourceMgr::HashToString(uint64_t Hash) { + const std::string* ResourceMgr::HashToString(uint64_t Hash) const { return OTR->HashToString(Hash); } } \ No newline at end of file diff --git a/libultraship/libultraship/ResourceMgr.h b/libultraship/libultraship/ResourceMgr.h index 28e77e537c..e76d5b5219 100644 --- a/libultraship/libultraship/ResourceMgr.h +++ b/libultraship/libultraship/ResourceMgr.h @@ -4,6 +4,7 @@ #include #include #include +#include #include "Resource.h" #include "GlobalCtx2.h" @@ -25,7 +26,7 @@ namespace Ship std::shared_ptr GetArchive() { return OTR; } std::shared_ptr GetContext() { return Context.lock(); } - std::string HashToString(uint64_t Hash); + const std::string* HashToString(uint64_t Hash) const; void InvalidateResourceCache(); @@ -33,9 +34,10 @@ namespace Ship void SetGameVersion(uint32_t newGameVersion); std::shared_ptr LoadFileAsync(std::string FilePath); std::shared_ptr LoadFile(std::string FilePath); - std::shared_ptr GetCachedFile(std::string FilePath); - std::shared_ptr LoadResource(std::string FilePath); - std::shared_ptr LoadResourceAsync(std::string FilePath); + std::shared_ptr GetCachedFile(const char* FilePath) const; + std::shared_ptr LoadResource(const char* FilePath); + std::shared_ptr LoadResource(const std::string& FilePath) { return LoadResource(FilePath.c_str()); } + std::variant, std::shared_ptr> LoadResourceAsync(const char* FilePath); std::shared_ptr>> CacheDirectory(std::string SearchMask); std::shared_ptr>> CacheDirectoryAsync(std::string SearchMask); std::shared_ptr>> DirtyDirectory(std::string SearchMask); @@ -50,7 +52,7 @@ namespace Ship std::weak_ptr Context; volatile bool bIsRunning; std::map> FileCache; - std::map> ResourceCache; + std::map, std::less<>> ResourceCache; std::queue> FileLoadQueue; std::queue> ResourceLoadQueue; std::shared_ptr OTR; diff --git a/libultraship/libultraship/SohImGuiImpl.cpp b/libultraship/libultraship/SohImGuiImpl.cpp index 72d9cee1ae..d17d6ccee2 100644 --- a/libultraship/libultraship/SohImGuiImpl.cpp +++ b/libultraship/libultraship/SohImGuiImpl.cpp @@ -363,7 +363,7 @@ namespace SohImGui { ModInternal::registerHookListener({ CONTROLLER_READ, [](const HookEvent ev) { pads = static_cast(ev->baseArgs["cont_pad"]); - }}); + } }); Game::InitSettings(); } @@ -402,7 +402,7 @@ namespace SohImGui { EnhancementRadioButton("French", "gLanguages", 2); */ int val = CVar_GetS32(cvarName.c_str(), 0); - if (ImGui::RadioButton(text.c_str(), id==val)) { + if (ImGui::RadioButton(text.c_str(), id == val)) { CVar_SetS32(cvarName.c_str(), (int)id); needs_save = true; } @@ -505,7 +505,12 @@ namespace SohImGui { } } - void DrawMainMenuAndCalculateGameSize() { + void Tooltip(std::string text) { + if (ImGui::IsItemHovered()) + ImGui::SetTooltip("%s", text.c_str()); + } + + void DrawMainMenuAndCalculateGameSize() { console->Update(); ImGuiBackendNewFrame(); ImGuiWMNewFrame(); @@ -603,15 +608,17 @@ namespace SohImGui { } EnhancementCheckbox("Show Inputs", "gInputEnabled"); + Tooltip("Shows currently pressed inputs on the bottom right of the screen"); EnhancementCheckbox("Rumble Enabled", "gRumbleEnabled"); EnhancementSliderFloat("Input Scale: %.1f", "##Input", "gInputScale", 1.0f, 3.0f, "", 1.0f, false); + Tooltip("Sets the on screen size of the displayed inputs from Show Inputs"); ImGui::Separator(); - EnhancementCheckbox("Dpad Support on Pause and File Select", "gDpadPauseName"); - EnhancementCheckbox("DPad Support in Ocarina and Text Choice", "gDpadOcarinaText"); - EnhancementCheckbox("DPad Support for Browsing Shop Items", "gDpadShop"); + EnhancementCheckbox("D-pad Support on Pause and File Select", "gDpadPauseName"); + EnhancementCheckbox("D-pad Support in Ocarina and Text Choice", "gDpadOcarinaText"); + EnhancementCheckbox("D-pad Support for Browsing Shop Items", "gDpadShop"); ImGui::EndMenu(); } @@ -619,8 +626,10 @@ namespace SohImGui { if (ImGui::BeginMenu("Graphics")) { EnhancementSliderInt("Internal Resolution: %dx", "##IMul", "gInternalResolution", 1, 8, ""); + Tooltip("Increases the render resolution of the game, up to 8x your output resolution,\nas a more intensive but effective form of anti-aliasing"); gfx_current_dimensions.internal_mul = CVar_GetS32("gInternalResolution", 1); EnhancementSliderInt("MSAA: %d", "##IMSAA", "gMSAAValue", 1, 8, ""); + Tooltip("Activates anti-aliasing when above 1, up to 8x for 8 samples for every pixel"); gfx_msaa_level = CVar_GetS32("gMSAAValue", 1); EXPERIMENTAL(); @@ -632,7 +641,7 @@ namespace SohImGui { INFO("New Filter: %s", filters[fId]); gapi->set_texture_filter((FilteringMode)fId); - CVar_SetS32("gTextureFilter", (int) fId); + CVar_SetS32("gTextureFilter", (int)fId); needs_save = true; } @@ -652,36 +661,59 @@ namespace SohImGui { if (ImGui::BeginMenu("Enhancements")) { - ImGui::Text("Gameplay"); - ImGui::Separator(); + if (ImGui::BeginMenu("Gameplay")) + { + EnhancementSliderInt("Text Speed: %dx", "##TEXTSPEED", "gTextSpeed", 1, 5, ""); + EnhancementSliderInt("King Zora Speed: %dx", "##WEEPSPEED", "gMweepSpeed", 1, 5, ""); - EnhancementSliderInt("Text Speed: %dx", "##TEXTSPEED", "gTextSpeed", 1, 5, ""); - EnhancementSliderInt("King Zora Speed: %dx", "##WEEPSPEED", "gMweepSpeed", 1, 5, ""); + EnhancementCheckbox("Skip Text", "gSkipText"); + Tooltip("Holding down B skips text"); + EnhancementCheckbox("Mute Low HP Alarm", "gLowHpAlarm"); + Tooltip("Disable the low HP beeping sound"); + EnhancementCheckbox("Minimal UI", "gMinimalUI"); + Tooltip("Hides most of the UI when not needed"); + EnhancementCheckbox("Visual Stone of Agony", "gVisualAgony"); + Tooltip("Displays an icon and plays a sound when Stone of Agony should be activated, for those without rumble"); + EnhancementCheckbox("Faster Block Push", "gFasterBlockPush"); + EnhancementCheckbox("Assignable Tunics and Boots", "gAssignableTunicsAndBoots"); + Tooltip("Allows equiping the tunic and boots to c-buttons"); + EnhancementCheckbox("MM Bunny Hood", "gMMBunnyHood"); + Tooltip("Wearing the Bunny Hood grants a speed increase like in Majora's Mask"); - EnhancementCheckbox("Skip Text", "gSkipText"); - EnhancementCheckbox("Minimal UI", "gMinimalUI"); - EnhancementCheckbox("MM Bunny Hood", "gMMBunnyHood"); - EnhancementCheckbox("Visual Stone of Agony", "gVisualAgony"); + ImGui::EndMenu(); + } - ImGui::Text("Graphics"); - ImGui::Separator(); + if (ImGui::BeginMenu("Graphics")) + { + EnhancementCheckbox("N64 Mode", "gN64Mode"); + Tooltip("Sets aspect ratio to 4:3 and lowers resolution to 240p, the N64's native resolution"); + EnhancementCheckbox("Animated Link in Pause Menu", "gPauseLiveLink"); + EnhancementCheckbox("Enable 3D Dropped items", "gNewDrops"); + EnhancementCheckbox("Dynamic Wallet Icon", "gDynamicWalletIcon"); + Tooltip("Changes the rupee in the wallet icon to match the wallet size you currently have"); + EnhancementCheckbox("Always show dungeon entrances", "gAlwaysShowDungeonMinimapIcon"); + Tooltip("Always shows dungeon entrance icons on the minimap"); - EnhancementCheckbox("N64 Mode", "gN64Mode"); + ImGui::EndMenu(); + } - EnhancementCheckbox("Animated Link in Pause Menu", "gPauseLiveLink"); - EnhancementCheckbox("Enable 3D Dropped items", "gNewDrops"); - EnhancementCheckbox("Faster Block Push", "gFasterBlockPush"); - EnhancementCheckbox("Dynamic Wallet Icon", "gDynamicWalletIcon"); - EnhancementCheckbox("Always show dungeon entrances", "gAlwaysShowDungeonMinimapIcon"); + if (ImGui::BeginMenu("Fixes")) + { + EnhancementCheckbox("Fix L&R Pause menu", "gUniformLR"); + Tooltip("Makes the L and R buttons in the pause menu the same color"); + EnhancementCheckbox("Fix Dungeon entrances", "gFixDungeonMinimapIcon"); + Tooltip("Show dungeon entrances icon only when it should be"); + EnhancementCheckbox("Fix Two Handed idle animations", "gTwoHandedIdle"); + Tooltip("Makes two handed idle animation play, a seemingly finished animation that was disabled on accident in the original game"); - ImGui::Text("Fixes"); - ImGui::Separator(); - EnhancementCheckbox("Fix L&R Pause menu", "gUniformLR"); - EnhancementCheckbox("Fix Dungeon entrances", "gFixDungeonMinimapIcon"); + ImGui::EndMenu(); + } EXPERIMENTAL(); + EnhancementCheckbox("60FPS Interpolation", "g60FPS"); EnhancementCheckbox("Disable LOD", "gDisableLOD"); + Tooltip("Turns off the level of detail setting, making models always use their higher poly variants"); ImGui::EndMenu(); } @@ -723,12 +755,19 @@ namespace SohImGui { } EnhancementCheckbox("No Clip", "gNoClip"); + Tooltip("Allows you to walk through walls"); EnhancementCheckbox("Climb Everything", "gClimbEverything"); + Tooltip("Makes every surface in the game climbable"); EnhancementCheckbox("Moon Jump on L", "gMoonJumpOnL"); + Tooltip("Holding L makes you float into the air"); EnhancementCheckbox("Super Tunic", "gSuperTunic"); + Tooltip("Makes every tunic have the effects of every other tunic"); EnhancementCheckbox("Easy ISG", "gEzISG"); + Tooltip("Automatically activates the Infinite Sword glitch, making you constantly swing your sword"); EnhancementCheckbox("Unrestricted Items", "gNoRestrictItems"); + Tooltip("Allows you to use any item at any location"); EnhancementCheckbox("Freeze Time", "gFreezeTime"); + Tooltip("Freezes the time of day"); ImGui::EndMenu(); } @@ -736,9 +775,12 @@ namespace SohImGui { if (ImGui::BeginMenu("Developer Tools")) { EnhancementCheckbox("OoT Debug Mode", "gDebugEnabled"); + Tooltip("Enables Debug Mode, allowing you to select maps with L + R + Z, noclip with L + D-pad Right,\nand open the debug menu with L on the pause screen"); ImGui::Separator(); EnhancementCheckbox("Stats", "gStatsEnabled"); + Tooltip("Shows the stats window, with your FPS and frametimes, and the OS you're playing on"); EnhancementCheckbox("Console", "gConsoleEnabled"); + Tooltip("Enables the console window, allowing you to input commands, type help for some examples"); console->opened = CVar_GetS32("gConsoleEnabled", 0); ImGui::EndMenu(); @@ -748,7 +790,7 @@ namespace SohImGui { if (ImGui::BeginMenu(category.first.c_str())) { for (const std::string& name : category.second) { std::string varName(name); - varName.erase(std::ranges::remove_if(varName, isspace).begin(), varName.end()); + varName.erase(std::ranges::remove_if(varName, isspace).begin(), varName.end()); std::string toggleName = "g" + varName + "Enabled"; EnhancementCheckbox(name, toggleName); diff --git a/libultraship/libultraship/Window.cpp b/libultraship/libultraship/Window.cpp index 35a95f0de8..bbd906c76e 100644 --- a/libultraship/libultraship/Window.cpp +++ b/libultraship/libultraship/Window.cpp @@ -119,17 +119,16 @@ extern "C" { ModInternal::callBindHook(0); } - char* ResourceMgr_GetNameByCRC(uint64_t crc, char* alloc) { - std::string hashStr = Ship::GlobalCtx2::GetInstance()->GetResourceManager()->HashToString(crc); - strcpy(alloc, hashStr.c_str()); - return (char*)hashStr.c_str(); + const char* ResourceMgr_GetNameByCRC(uint64_t crc) { + const std::string* hashStr = Ship::GlobalCtx2::GetInstance()->GetResourceManager()->HashToString(crc); + return hashStr != nullptr ? hashStr->c_str() : nullptr; } Vtx* ResourceMgr_LoadVtxByCRC(uint64_t crc) { - std::string hashStr = Ship::GlobalCtx2::GetInstance()->GetResourceManager()->HashToString(crc); + const std::string* hashStr = Ship::GlobalCtx2::GetInstance()->GetResourceManager()->HashToString(crc); - if (hashStr != "") { - auto res = std::static_pointer_cast(Ship::GlobalCtx2::GetInstance()->GetResourceManager()->LoadResource(hashStr)); + if (hashStr != nullptr) { + auto res = std::static_pointer_cast(Ship::GlobalCtx2::GetInstance()->GetResourceManager()->LoadResource(hashStr->c_str())); //if (res != nullptr) return (Vtx*)res->vertices.data(); @@ -142,10 +141,10 @@ extern "C" { } int32_t* ResourceMgr_LoadMtxByCRC(uint64_t crc) { - std::string hashStr = Ship::GlobalCtx2::GetInstance()->GetResourceManager()->HashToString(crc); + const std::string* hashStr = Ship::GlobalCtx2::GetInstance()->GetResourceManager()->HashToString(crc); - if (hashStr != "") { - auto res = std::static_pointer_cast(Ship::GlobalCtx2::GetInstance()->GetResourceManager()->LoadResource(hashStr)); + if (hashStr != nullptr) { + auto res = std::static_pointer_cast(Ship::GlobalCtx2::GetInstance()->GetResourceManager()->LoadResource(hashStr->c_str())); return (int32_t*)res->mtx.data(); } else { return nullptr; @@ -153,10 +152,10 @@ extern "C" { } Gfx* ResourceMgr_LoadGfxByCRC(uint64_t crc) { - std::string hashStr = Ship::GlobalCtx2::GetInstance()->GetResourceManager()->HashToString(crc); + const std::string* hashStr = Ship::GlobalCtx2::GetInstance()->GetResourceManager()->HashToString(crc); - if (hashStr != "") { - auto res = std::static_pointer_cast(Ship::GlobalCtx2::GetInstance()->GetResourceManager()->LoadResource(hashStr)); + if (hashStr != nullptr) { + auto res = std::static_pointer_cast(Ship::GlobalCtx2::GetInstance()->GetResourceManager()->LoadResource(hashStr->c_str())); return (Gfx*)&res->instructions[0]; } else { return nullptr; @@ -164,14 +163,14 @@ extern "C" { } char* ResourceMgr_LoadTexByCRC(uint64_t crc) { - const std::string hashStr = Ship::GlobalCtx2::GetInstance()->GetResourceManager()->HashToString(crc); + const std::string* hashStr = Ship::GlobalCtx2::GetInstance()->GetResourceManager()->HashToString(crc); - if (!hashStr.empty()) { - const auto res = static_cast(Ship::GlobalCtx2::GetInstance()->GetResourceManager()->LoadResource(hashStr).get()); + if (hashStr != nullptr) { + const auto res = static_cast(Ship::GlobalCtx2::GetInstance()->GetResourceManager()->LoadResource(hashStr->c_str()).get()); ModInternal::bindHook(LOAD_TEXTURE); ModInternal::initBindHook(2, - HookParameter({.name = "path", .parameter = (void*)hashStr.c_str() }), + HookParameter({.name = "path", .parameter = (void*)hashStr->c_str() }), HookParameter({.name = "texture", .parameter = static_cast(&res->imageData) }) ); ModInternal::callBindHook(0); @@ -184,11 +183,11 @@ extern "C" { void ResourceMgr_RegisterResourcePatch(uint64_t hash, uint32_t instrIndex, uintptr_t origData) { - std::string hashStr = Ship::GlobalCtx2::GetInstance()->GetResourceManager()->HashToString(hash); + const std::string* hashStr = Ship::GlobalCtx2::GetInstance()->GetResourceManager()->HashToString(hash); - if (hashStr != "") + if (hashStr != nullptr) { - auto res = (Ship::Texture*)Ship::GlobalCtx2::GetInstance()->GetResourceManager()->LoadResource(hashStr).get(); + auto res = (Ship::Texture*)Ship::GlobalCtx2::GetInstance()->GetResourceManager()->LoadResource(hashStr->c_str()).get(); Ship::Patch patch; patch.crc = hash; @@ -282,8 +281,12 @@ namespace Ship { gfx_start_frame(); } - void Window::RunCommands(Gfx* Commands) { - gfx_run(Commands); + void Window::RunCommands(Gfx* Commands, const std::vector>& mtx_replacements) { + for (const auto& m : mtx_replacements) { + gfx_run(Commands, m); + gfx_end_frame(); + } + gfx_run(Commands, {}); gfx_end_frame(); } diff --git a/libultraship/libultraship/Window.h b/libultraship/libultraship/Window.h index b075e496fa..04886c53e8 100644 --- a/libultraship/libultraship/Window.h +++ b/libultraship/libultraship/Window.h @@ -19,7 +19,7 @@ namespace Ship { void MainLoop(void (*MainFunction)(void)); void Init(); void StartFrame(); - void RunCommands(Gfx* Commands); + void RunCommands(Gfx* Commands, const std::vector>& mtx_replacements); void SetFrameDivisor(int divisor); void GetPixelDepthPrepare(float x, float y); uint16_t GetPixelDepth(float x, float y); diff --git a/soh/assets/xml/GC_NMQ_D/textures/map_48x85_static.xml b/soh/assets/xml/GC_NMQ_D/textures/map_48x85_static.xml index b5b1d1a859..2788498a84 100644 --- a/soh/assets/xml/GC_NMQ_D/textures/map_48x85_static.xml +++ b/soh/assets/xml/GC_NMQ_D/textures/map_48x85_static.xml @@ -1,72 +1,72 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/soh/assets/xml/GC_NMQ_PAL_F/textures/map_48x85_static.xml b/soh/assets/xml/GC_NMQ_PAL_F/textures/map_48x85_static.xml index b5b1d1a859..b2ea46340a 100644 --- a/soh/assets/xml/GC_NMQ_PAL_F/textures/map_48x85_static.xml +++ b/soh/assets/xml/GC_NMQ_PAL_F/textures/map_48x85_static.xml @@ -1,72 +1,72 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - + \ No newline at end of file diff --git a/soh/include/macros.h b/soh/include/macros.h index a621a65f8d..7392274d9b 100644 --- a/soh/include/macros.h +++ b/soh/include/macros.h @@ -138,6 +138,8 @@ extern GraphicsContext* __gfxCtx; #ifndef NDEBUG #define OPEN_DISPS(gfxCtx, file, line) \ { \ + void FrameInterpolation_RecordOpenChild(const void* a, int b); \ + FrameInterpolation_RecordOpenChild(file, line); \ GraphicsContext* __gfxCtx; \ Gfx* dispRefs[4]; \ __gfxCtx = gfxCtx; \ @@ -146,6 +148,8 @@ extern GraphicsContext* __gfxCtx; #else #define OPEN_DISPS(gfxCtx, file, line) \ { \ + void FrameInterpolation_RecordOpenChild(const void* a, int b); \ + FrameInterpolation_RecordOpenChild(file, line); \ GraphicsContext* __gfxCtx; \ __gfxCtx = gfxCtx; \ (void)__gfxCtx; @@ -153,11 +157,15 @@ extern GraphicsContext* __gfxCtx; #ifndef NDEBUG #define CLOSE_DISPS(gfxCtx, file, line) \ + {void FrameInterpolation_RecordCloseChild(void); \ + FrameInterpolation_RecordCloseChild();} \ Graph_CloseDisps(dispRefs, gfxCtx, file, line); \ } \ (void)0 #else #define CLOSE_DISPS(gfxCtx, file, line) \ + {void FrameInterpolation_RecordCloseChild(void); \ + FrameInterpolation_RecordCloseChild();} \ (void)0; \ } \ (void)0 diff --git a/soh/include/z64effect.h b/soh/include/z64effect.h index c85c9d6173..1b35d46b61 100644 --- a/soh/include/z64effect.h +++ b/soh/include/z64effect.h @@ -225,6 +225,7 @@ typedef struct EffectSs { /* 0x5C */ s16 life; // -1 means this entry is free /* 0x5E */ u8 priority; // Lower value means higher priority /* 0x5F */ u8 type; + u32 epoch; } EffectSs; // size = 0x60 typedef struct { diff --git a/soh/include/z64item.h b/soh/include/z64item.h index 05b2a24b2a..6cb5ec2a4e 100644 --- a/soh/include/z64item.h +++ b/soh/include/z64item.h @@ -78,6 +78,12 @@ typedef enum { /* 0x15 */ SLOT_BOTTLE_4, /* 0x16 */ SLOT_TRADE_ADULT, /* 0x17 */ SLOT_TRADE_CHILD, + /* 0x18 */ SLOT_TUNIC_KOKIRI, + /* 0x19 */ SLOT_TUNIC_GORON, + /* 0x1A */ SLOT_TUNIC_ZORA, + /* 0x1B */ SLOT_BOOTS_KOKIRI, + /* 0x1C */ SLOT_BOOTS_IRON, + /* 0x1D */ SLOT_BOOTS_HOVER, /* 0xFF */ SLOT_NONE = 0xFF } InventorySlot; diff --git a/soh/include/z64player.h b/soh/include/z64player.h index 6771ef5c6c..9bce60ffe4 100644 --- a/soh/include/z64player.h +++ b/soh/include/z64player.h @@ -128,7 +128,16 @@ typedef enum { /* 0x40 */ PLAYER_AP_MASK_GERUDO, /* 0x41 */ PLAYER_AP_MASK_TRUTH, /* 0x42 */ PLAYER_AP_LENS, - /* 0x43 */ PLAYER_AP_MAX + /* 0x43 */ PLAYER_AP_SHIELD_DEKU, + /* 0x44 */ PLAYER_AP_SHIELD_HYLIAN, + /* 0x45 */ PLAYER_AP_SHIELD_MIRROR, + /* 0x46 */ PLAYER_AP_TUNIC_KOKIRI, + /* 0x47 */ PLAYER_AP_TUNIC_GORON, + /* 0x48 */ PLAYER_AP_TUNIC_ZORA, + /* 0x49 */ PLAYER_AP_BOOTS_KOKIRI, + /* 0x4A */ PLAYER_AP_BOOTS_IRON, + /* 0x4B */ PLAYER_AP_BOOTS_HOVER, + /* 0x4C */ PLAYER_AP_MAX } PlayerActionParam; typedef enum { diff --git a/soh/soh.vcxproj b/soh/soh.vcxproj index a395a22ce0..0a2006122e 100644 --- a/soh/soh.vcxproj +++ b/soh/soh.vcxproj @@ -173,6 +173,7 @@ + @@ -878,6 +879,7 @@ + diff --git a/soh/soh.vcxproj.filters b/soh/soh.vcxproj.filters index b9d68b7046..04f6030a85 100644 --- a/soh/soh.vcxproj.filters +++ b/soh/soh.vcxproj.filters @@ -2186,6 +2186,8 @@ Source Files\soh\Enhancements\debugger + + Source Files\soh Source Files @@ -3748,6 +3750,8 @@ Header Files\soh\Enhancements\debugger + + Header Files\soh Source Files\soh\Enhancements diff --git a/soh/soh/Enhancements/bootcommands.c b/soh/soh/Enhancements/bootcommands.c index 03742dae97..71bd00cacc 100644 --- a/soh/soh/Enhancements/bootcommands.c +++ b/soh/soh/Enhancements/bootcommands.c @@ -27,6 +27,7 @@ void BootCommands_Init() CVar_RegisterS32("gMinimalUI", 0); CVar_RegisterS32("gRumbleEnabled", 0); CVar_RegisterS32("gUniformLR", 1); + CVar_RegisterS32("gTwoHandedIdle", 0); CVar_RegisterS32("gNewDrops", 0); CVar_RegisterS32("gVisualAgony", 0); CVar_RegisterS32("gLanguages", 0); //0 = English / 1 = German / 2 = French diff --git a/soh/soh/Enhancements/debugger/colViewer.cpp b/soh/soh/Enhancements/debugger/colViewer.cpp index ed0fe0b567..f5c283aac9 100644 --- a/soh/soh/Enhancements/debugger/colViewer.cpp +++ b/soh/soh/Enhancements/debugger/colViewer.cpp @@ -1,6 +1,7 @@ #include "colViewer.h" #include "../libultraship/SohImGuiImpl.h" #include "ImGuiHelpers.h" +#include "../../frame_interpolation.h" #include #include diff --git a/soh/soh/OTRGlobals.cpp b/soh/soh/OTRGlobals.cpp index 7456ebf7ef..e8e1b31c19 100644 --- a/soh/soh/OTRGlobals.cpp +++ b/soh/soh/OTRGlobals.cpp @@ -29,6 +29,7 @@ #include "AudioPlayer.h" #include "Enhancements/debugconsole.h" #include "Enhancements/debugger/debugger.h" +#include "soh/frame_interpolation.h" #include "Utils/BitConverter.h" #include "variables.h" #include "macros.h" @@ -174,7 +175,7 @@ extern "C" void Graph_StartFrame() { // C->C++ Bridge extern "C" void Graph_ProcessGfxCommands(Gfx* commands) { - OTRGlobals::Instance->context->GetWindow()->SetFrameDivisor(R_UPDATE_RATE); + OTRGlobals::Instance->context->GetWindow()->SetFrameDivisor(CVar_GetS32("g60FPS", 0) == 0 ? R_UPDATE_RATE : 1); if (!audio.initialized) { audio.initialized = true; @@ -224,7 +225,15 @@ extern "C" void Graph_ProcessGfxCommands(Gfx* commands) { } audio.cv_to_thread.notify_one(); - OTRGlobals::Instance->context->GetWindow()->RunCommands(commands); + std::vector> mtx_replacements; + if (CVar_GetS32("g60FPS", 0) != 0) { + int to = R_UPDATE_RATE; + for (int i = 1; i < to; i++) { + mtx_replacements.push_back(FrameInterpolation_Interpolate(i / (float)to)); + } + } + + OTRGlobals::Instance->context->GetWindow()->RunCommands(commands, mtx_replacements); { std::unique_lock Lock(audio.mutex); diff --git a/soh/soh/frame_interpolation.cpp b/soh/soh/frame_interpolation.cpp new file mode 100644 index 0000000000..bd85d41ce2 --- /dev/null +++ b/soh/soh/frame_interpolation.cpp @@ -0,0 +1,732 @@ +#include "Cvar.h" + +#include +#include +#include +#include + +#include "frame_interpolation.h" + +/* +Frame interpolation. + +The idea of this code is to interpolate all matrices. + +The code contains two approaches. The first is to interpolate +all inputs in transformations, such as angles, scale and distances, +and then perform the same transformations with the interpolated values. +After evaluation for some reason some animations such rolling look strange. + +The second approach is to simply interpolate the final matrices. This will +more or less simply interpolate the world coordinates for movements. +This will however make rotations ~180 degrees get the "paper effect". +The mitigation is to identify this case for actors and interpolate the +matrix but in model coordinates instead, by "removing" the rotation- +translation before interpolating, create a rotation matrix with the +interpolated angle which is then applied to the matrix. + +Currently the code contains both methods but only the second one is currently +used. + +Both approaches build a tree of instructions, containing matrices +at leaves. Every node is built from OPEN_DISPS/CLOSE_DISPS and manually +inserted FrameInterpolation_OpenChild/FrameInterpolation_Close child calls. +These nodes contain information that should suffice to identify the matrix, +so we can find it in an adjacent frame. + +We can interpolate an arbitrary amount of frames between two original frames, +given a specific interpolation factor (0=old frame, 0.5=average of frames, +1.0=new frame). +*/ + +extern "C" { + +void Matrix_Init(struct GameState* gameState); +void Matrix_Push(void); +void Matrix_Pop(void); +void Matrix_Get(MtxF* dest); +void Matrix_Put(MtxF* src); +void Matrix_Mult(MtxF* mf, u8 mode); +void Matrix_Translate(f32 x, f32 y, f32 z, u8 mode); +void Matrix_Scale(f32 x, f32 y, f32 z, u8 mode); +void Matrix_RotateX(f32 x, u8 mode); +void Matrix_RotateY(f32 y, u8 mode); +void Matrix_RotateZ(f32 z, u8 mode); +void Matrix_RotateZYX(s16 x, s16 y, s16 z, u8 mode); +void Matrix_TranslateRotateZYX(Vec3f* translation, Vec3s* rotation); +void Matrix_SetTranslateRotateYXZ(f32 translateX, f32 translateY, f32 translateZ, Vec3s* rot); +Mtx* Matrix_MtxFToMtx(MtxF* src, Mtx* dest); +Mtx* Matrix_ToMtx(Mtx* dest, char* file, s32 line); +Mtx* Matrix_NewMtx(struct GraphicsContext* gfxCtx, char* file, s32 line); +Mtx* Matrix_MtxFToNewMtx(MtxF* src, struct GraphicsContext* gfxCtx); +void Matrix_MultVec3f(Vec3f* src, Vec3f* dest); +void Matrix_MtxFCopy(MtxF* dest, MtxF* src); +void Matrix_MtxToMtxF(Mtx* src, MtxF* dest); +void Matrix_MultVec3fExt(Vec3f* src, Vec3f* dest, MtxF* mf); +void Matrix_Transpose(MtxF* mf); +void Matrix_ReplaceRotation(MtxF* mf); +void Matrix_MtxFToYXZRotS(MtxF* mf, Vec3s* rotDest, s32 flag); +void Matrix_MtxFToZYXRotS(MtxF* mf, Vec3s* rotDest, s32 flag); +void Matrix_RotateAxis(f32 angle, Vec3f* axis, u8 mode); +MtxF* Matrix_CheckFloats(MtxF* mf, char* file, s32 line); +void Matrix_SetTranslateScaleMtx2(Mtx* mtx, f32 scaleX, f32 scaleY, f32 scaleZ, f32 translateX, f32 translateY, + f32 translateZ); + +MtxF* Matrix_GetCurrent(void); + +void SkinMatrix_MtxFMtxFMult(MtxF* mfA, MtxF* mfB, MtxF* dest); + +} + +static bool invert_matrix(const float m[16], float invOut[16]); + +using namespace std; + +namespace { + + enum class Op { + OpenChild, + CloseChild, + + MatrixPush, + MatrixPop, + MatrixPut, + MatrixMult, + MatrixTranslate, + MatrixScale, + MatrixRotate1Coord, + MatrixRotateZYX, + MatrixTranslateRotateZYX, + MatrixSetTranslateRotateYXZ, + MatrixMtxFToMtx, + MatrixToMtx, + MatrixReplaceRotation, + MatrixRotateAxis, + SkinMatrixMtxFToMtx + }; + + typedef pair label; + + union Data { + Data() { + } + + struct { + MtxF src; + } matrix_put; + + struct { + MtxF mf; + u8 mode; + } matrix_mult; + + struct { + f32 x, y, z; + u8 mode; + } matrix_translate, matrix_scale; + + struct { + u32 coord; + f32 value; + u8 mode; + } matrix_rotate_1_coord; + + struct { + s16 x, y, z; + u8 mode; + } matrix_rotate_zyx; + + struct { + Vec3f translation; + Vec3s rotation; + } matrix_translate_rotate_zyx; + + struct { + f32 translateX, translateY, translateZ; + Vec3s rot; + //MtxF mtx; + bool has_mtx; + } matrix_set_translate_rotate_yxz; + + struct { + MtxF src; + Mtx* dest; + } matrix_mtxf_to_mtx; + + struct { + Mtx* dest; + MtxF src; + bool has_adjusted; + } matrix_to_mtx; + + struct { + MtxF mf; + } matrix_replace_rotation; + + struct { + f32 angle; + Vec3f axis; + u8 mode; + } matrix_rotate_axis; + + struct { + label key; + size_t idx; + } open_child; + }; + + struct Path { + map> children; + map> ops; + vector> items; + }; + + struct Recording { + Path root_path; + }; + + bool is_recording; + vector current_path; + uint32_t camera_epoch; + uint32_t previous_camera_epoch; + Recording current_recording; + Recording previous_recording; + + bool next_is_actor_pos_rot_matrix; + bool has_inv_actor_mtx; + MtxF inv_actor_mtx; + size_t inv_actor_mtx_path_index; + + Data& append(Op op) { + auto& m = current_path.back()->ops[op]; + current_path.back()->items.emplace_back(op, m.size()); + return m.emplace_back(); + } + + struct InterpolateCtx { + float step; + float w; + unordered_map mtx_replacements; + MtxF tmp_mtxf, tmp_mtxf2; + Vec3f tmp_vec3f; + Vec3s tmp_vec3s; + MtxF actor_mtx; + + MtxF* new_replacement(Mtx* addr) { + return &mtx_replacements[addr]; + } + + void interpolate_mtxf(MtxF* res, MtxF* o, MtxF* n) { + for (size_t i = 0; i < 4; i++) { + for (size_t j = 0; j < 4; j++) { + res->mf[i][j] = w * o->mf[i][j] + step * n->mf[i][j]; + } + } + } + + float lerp(f32 o, f32 n) { + return w * o + step * n; + } + + void lerp_vec3f(Vec3f* res, Vec3f* o, Vec3f* n) { + res->x = lerp(o->x, n->x); + res->y = lerp(o->y, n->y); + res->z = lerp(o->z, n->z); + } + + float interpolate_angle(f32 o, f32 n) { + if (o == n) + return n; + o = fmodf(o, 2 * M_PI); + if (o < 0.0f) { + o += 2 * M_PI; + } + n = fmodf(n, 2 * M_PI); + if (n < 0.0f) { + n += 2 * M_PI; + } + if (fabsf(o - n) > M_PI) { + if (o < n) { + o += 2 * M_PI; + } else { + n += 2 * M_PI; + } + } + if (fabsf(o - n) > M_PI / 2) { + //return n; + } + return lerp(o, n); + } + + s16 interpolate_angle(s16 os, s16 ns) { + if (os == ns) + return ns; + int o = (u16)os; + int n = (u16)ns; + u16 res; + int diff = o - n; + if (-0x8000 <= diff && diff <= 0x8000) { + if (diff < -0x4000 || diff > 0x4000) { + return ns; + } + res = (u16)(w * o + step * n); + } else { + if (o < n) { + o += 0x10000; + } else { + n += 0x10000; + } + diff = o - n; + if (diff < -0x4000 || diff > 0x4000) { + return ns; + } + res = (u16)(w * o + step * n); + } + if (os / 327 == ns / 327 && (s16)res / 327 != os / 327) { + int bp = 0; + } + return res; + } + + void interpolate_angles(Vec3s* res, Vec3s* o, Vec3s* n) { + res->x = interpolate_angle(o->x, n->x); + res->y = interpolate_angle(o->y, n->y); + res->z = interpolate_angle(o->z, n->z); + } + + void interpolate_branch(Path* old_path, Path *new_path) { + for (auto& item : new_path->items) { + Data& new_op = new_path->ops[item.first][item.second]; + + if (item.first == Op::OpenChild) { + if (auto it = old_path->children.find(new_op.open_child.key); + it != old_path->children.end() && new_op.open_child.idx < it->second.size()) { + interpolate_branch(&it->second[new_op.open_child.idx], + &new_path->children.find(new_op.open_child.key)->second[new_op.open_child.idx]); + } else { + interpolate_branch( + &new_path->children.find(new_op.open_child.key)->second[new_op.open_child.idx], + &new_path->children.find(new_op.open_child.key)->second[new_op.open_child.idx]); + } + continue; + } + + if (auto it = old_path->ops.find(item.first); it != old_path->ops.end()) { + if (item.second < it->second.size()) { + Data& old_op = it->second[item.second]; + switch (item.first) { + case Op::OpenChild: + break; + case Op::CloseChild: + break; + + case Op::MatrixPush: + Matrix_Push(); + break; + + case Op::MatrixPop: + Matrix_Pop(); + break; + + case Op::MatrixPut: + interpolate_mtxf(&tmp_mtxf, &old_op.matrix_put.src, &new_op.matrix_put.src); + Matrix_Put(&tmp_mtxf); + break; + + case Op::MatrixMult: + interpolate_mtxf(&tmp_mtxf, &old_op.matrix_mult.mf, &new_op.matrix_mult.mf); + Matrix_Mult(&tmp_mtxf, new_op.matrix_mult.mode); + break; + + case Op::MatrixTranslate: + Matrix_Translate(lerp(old_op.matrix_translate.x, new_op.matrix_translate.x), + lerp(old_op.matrix_translate.y, new_op.matrix_translate.y), + lerp(old_op.matrix_translate.z, new_op.matrix_translate.z), + new_op.matrix_translate.mode); + break; + + case Op::MatrixScale: + Matrix_Scale(lerp(old_op.matrix_scale.x, new_op.matrix_scale.x), + lerp(old_op.matrix_scale.y, new_op.matrix_scale.y), + lerp(old_op.matrix_scale.z, new_op.matrix_scale.z), + new_op.matrix_scale.mode); + break; + + case Op::MatrixRotate1Coord: { + float v = interpolate_angle(old_op.matrix_rotate_1_coord.value, new_op.matrix_rotate_1_coord.value); + u8 mode = new_op.matrix_rotate_1_coord.mode; + switch (new_op.matrix_rotate_1_coord.coord) { + case 0: + Matrix_RotateX(v, mode); + break; + + case 1: + Matrix_RotateY(v, mode); + break; + + case 2: + Matrix_RotateZ(v, mode); + break; + } + break; + } + + case Op::MatrixRotateZYX: + Matrix_RotateZYX(interpolate_angle(old_op.matrix_rotate_zyx.x, new_op.matrix_rotate_zyx.x), + interpolate_angle(old_op.matrix_rotate_zyx.y, new_op.matrix_rotate_zyx.y), + interpolate_angle(old_op.matrix_rotate_zyx.z, new_op.matrix_rotate_zyx.z), + new_op.matrix_rotate_zyx.mode); + break; + + case Op::MatrixTranslateRotateZYX: + lerp_vec3f(&tmp_vec3f, &old_op.matrix_translate_rotate_zyx.translation, &new_op.matrix_translate_rotate_zyx.translation); + interpolate_angles(&tmp_vec3s, &old_op.matrix_translate_rotate_zyx.rotation, &new_op.matrix_translate_rotate_zyx.rotation); + Matrix_TranslateRotateZYX(&tmp_vec3f, &tmp_vec3s); + break; + + case Op::MatrixSetTranslateRotateYXZ: + interpolate_angles(&tmp_vec3s, &old_op.matrix_set_translate_rotate_yxz.rot, + &new_op.matrix_set_translate_rotate_yxz.rot); + Matrix_SetTranslateRotateYXZ(lerp(old_op.matrix_set_translate_rotate_yxz.translateX, + new_op.matrix_set_translate_rotate_yxz.translateX), + lerp(old_op.matrix_set_translate_rotate_yxz.translateY, + new_op.matrix_set_translate_rotate_yxz.translateY), + lerp(old_op.matrix_set_translate_rotate_yxz.translateZ, + new_op.matrix_set_translate_rotate_yxz.translateZ), + &tmp_vec3s); + if (new_op.matrix_set_translate_rotate_yxz.has_mtx && old_op.matrix_set_translate_rotate_yxz.has_mtx) { + actor_mtx = *Matrix_GetCurrent(); + } + break; + + case Op::MatrixMtxFToMtx: + interpolate_mtxf(new_replacement(new_op.matrix_mtxf_to_mtx.dest), + &old_op.matrix_mtxf_to_mtx.src, &new_op.matrix_mtxf_to_mtx.src); + break; + + case Op::MatrixToMtx: { + //*new_replacement(new_op.matrix_to_mtx.dest) = *Matrix_GetCurrent(); + if (old_op.matrix_to_mtx.has_adjusted && new_op.matrix_to_mtx.has_adjusted) { + interpolate_mtxf(&tmp_mtxf, &old_op.matrix_to_mtx.src, &new_op.matrix_to_mtx.src); + SkinMatrix_MtxFMtxFMult(&actor_mtx, &tmp_mtxf, new_replacement(new_op.matrix_to_mtx.dest)); + } else { + interpolate_mtxf(new_replacement(new_op.matrix_to_mtx.dest), + &old_op.matrix_to_mtx.src, &new_op.matrix_to_mtx.src); + } + break; + } + + case Op::MatrixReplaceRotation: + interpolate_mtxf(&tmp_mtxf, &old_op.matrix_replace_rotation.mf, &new_op.matrix_replace_rotation.mf); + Matrix_ReplaceRotation(&tmp_mtxf); + break; + + case Op::MatrixRotateAxis: + lerp_vec3f(&tmp_vec3f, &old_op.matrix_rotate_axis.axis, &new_op.matrix_rotate_axis.axis); + Matrix_RotateAxis(interpolate_angle(old_op.matrix_rotate_axis.angle, new_op.matrix_rotate_axis.angle), + &tmp_vec3f, new_op.matrix_rotate_axis.mode); + break; + + case Op::SkinMatrixMtxFToMtx: + break; + } + } + } + } + } + }; + +} // anonymous namespace + +unordered_map FrameInterpolation_Interpolate(float step) { + InterpolateCtx ctx; + ctx.step = step; + ctx.w = 1.0f - step; + ctx.interpolate_branch(&previous_recording.root_path, ¤t_recording.root_path); + return ctx.mtx_replacements; +} + +void FrameInterpolation_StartRecord(void) { + previous_recording = move(current_recording); + current_recording = {}; + current_path.clear(); + current_path.push_back(¤t_recording.root_path); + if (CVar_GetS32("g60FPS", 0) != 0) { + is_recording = true; + } +} + +void FrameInterpolation_StopRecord(void) { + previous_camera_epoch = camera_epoch; + is_recording = false; +} + +void FrameInterpolation_RecordOpenChild(const void* a, int b) { + if (!is_recording) + return; + label key = { a, b }; + auto& m = current_path.back()->children[key]; + append(Op::OpenChild).open_child = { key, m.size() }; + current_path.push_back(&m.emplace_back()); +} + +void FrameInterpolation_RecordCloseChild(void) { + if (!is_recording) + return; + //append(Op::CloseChild); + if (has_inv_actor_mtx && current_path.size() == inv_actor_mtx_path_index) { + has_inv_actor_mtx = false; + } + current_path.pop_back(); +} + +void FrameInterpolation_DontInterpolateCamera(void) { + camera_epoch = previous_camera_epoch + 1; +} + +int FrameInterpolation_GetCameraEpoch(void) { + return (int)camera_epoch; +} + +void FrameInterpolation_RecordActorPosRotMatrix(void) { + if (!is_recording) + return; + next_is_actor_pos_rot_matrix = true; +} + +void FrameInterpolation_RecordMatrixPush(void) { + if (!is_recording) + return; + append(Op::MatrixPush); +} + +void FrameInterpolation_RecordMatrixPop(void) { + if (!is_recording) + return; + append(Op::MatrixPop); +} + +void FrameInterpolation_RecordMatrixPut(MtxF* src) { + if (!is_recording) + return; + append(Op::MatrixPut).matrix_put = { *src }; +} + +void FrameInterpolation_RecordMatrixMult(MtxF* mf, u8 mode) { + if (!is_recording) + return; + append(Op::MatrixMult).matrix_mult = { *mf, mode }; +} + +void FrameInterpolation_RecordMatrixTranslate(f32 x, f32 y, f32 z, u8 mode) { + if (!is_recording) + return; + append(Op::MatrixTranslate).matrix_translate = { x, y, z, mode }; +} + +void FrameInterpolation_RecordMatrixScale(f32 x, f32 y, f32 z, u8 mode) { + if (!is_recording) + return; + append(Op::MatrixScale).matrix_scale = { x, y, z, mode }; +} + +void FrameInterpolation_RecordMatrixRotate1Coord(u32 coord, f32 value, u8 mode) { + if (!is_recording) + return; + append(Op::MatrixRotate1Coord).matrix_rotate_1_coord = { coord, value, mode }; +} + +void FrameInterpolation_RecordMatrixRotateZYX(s16 x, s16 y, s16 z, u8 mode) { + if (!is_recording) + return; + append(Op::MatrixRotateZYX).matrix_rotate_zyx = { x, y, z, mode }; +} + +void FrameInterpolation_RecordMatrixTranslateRotateZYX(Vec3f* translation, Vec3s* rotation) { + if (!is_recording) + return; + append(Op::MatrixTranslateRotateZYX).matrix_translate_rotate_zyx = { *translation, *rotation }; +} + +void FrameInterpolation_RecordMatrixSetTranslateRotateYXZ(f32 translateX, f32 translateY, f32 translateZ, Vec3s* rot) { + if (!is_recording) + return; + auto& d = append(Op::MatrixSetTranslateRotateYXZ).matrix_set_translate_rotate_yxz = { translateX, translateY, translateZ, + *rot }; + if (next_is_actor_pos_rot_matrix) { + d.has_mtx = true; + //d.mtx = *Matrix_GetCurrent(); + invert_matrix((const float *)Matrix_GetCurrent()->mf, (float *)inv_actor_mtx.mf); + next_is_actor_pos_rot_matrix = false; + has_inv_actor_mtx = true; + inv_actor_mtx_path_index = current_path.size(); + } +} + +void FrameInterpolation_RecordMatrixMtxFToMtx(MtxF* src, Mtx* dest) { + if (!is_recording) + return; + append(Op::MatrixMtxFToMtx).matrix_mtxf_to_mtx = { *src, dest }; +} + +void FrameInterpolation_RecordMatrixToMtx(Mtx* dest, char* file, s32 line) { + if (!is_recording) + return; + auto& d = append(Op::MatrixToMtx).matrix_to_mtx = { dest }; + if (has_inv_actor_mtx) { + d.has_adjusted = true; + SkinMatrix_MtxFMtxFMult(&inv_actor_mtx, Matrix_GetCurrent(), &d.src); + } else { + d.src = *Matrix_GetCurrent(); + } +} + +void FrameInterpolation_RecordMatrixReplaceRotation(MtxF* mf) { + if (!is_recording) + return; + append(Op::MatrixReplaceRotation).matrix_replace_rotation = { *mf }; +} + +void FrameInterpolation_RecordMatrixRotateAxis(f32 angle, Vec3f* axis, u8 mode) { + if (!is_recording) + return; + append(Op::MatrixRotateAxis).matrix_rotate_axis = { angle, *axis, mode }; +} + +void FrameInterpolation_RecordSkinMatrixMtxFToMtx(MtxF* src, Mtx* dest) { + if (!is_recording) + return; + FrameInterpolation_RecordMatrixMtxFToMtx(src, dest); +} + +// https://stackoverflow.com/questions/1148309/inverting-a-4x4-matrix +static bool invert_matrix(const float m[16], float invOut[16]) { + float inv[16], det; + int i; + + inv[0] = m[5] * m[10] * m[15] - + m[5] * m[11] * m[14] - + m[9] * m[6] * m[15] + + m[9] * m[7] * m[14] + + m[13] * m[6] * m[11] - + m[13] * m[7] * m[10]; + + inv[4] = -m[4] * m[10] * m[15] + + m[4] * m[11] * m[14] + + m[8] * m[6] * m[15] - + m[8] * m[7] * m[14] - + m[12] * m[6] * m[11] + + m[12] * m[7] * m[10]; + + inv[8] = m[4] * m[9] * m[15] - + m[4] * m[11] * m[13] - + m[8] * m[5] * m[15] + + m[8] * m[7] * m[13] + + m[12] * m[5] * m[11] - + m[12] * m[7] * m[9]; + + inv[12] = -m[4] * m[9] * m[14] + + m[4] * m[10] * m[13] + + m[8] * m[5] * m[14] - + m[8] * m[6] * m[13] - + m[12] * m[5] * m[10] + + m[12] * m[6] * m[9]; + + inv[1] = -m[1] * m[10] * m[15] + + m[1] * m[11] * m[14] + + m[9] * m[2] * m[15] - + m[9] * m[3] * m[14] - + m[13] * m[2] * m[11] + + m[13] * m[3] * m[10]; + + inv[5] = m[0] * m[10] * m[15] - + m[0] * m[11] * m[14] - + m[8] * m[2] * m[15] + + m[8] * m[3] * m[14] + + m[12] * m[2] * m[11] - + m[12] * m[3] * m[10]; + + inv[9] = -m[0] * m[9] * m[15] + + m[0] * m[11] * m[13] + + m[8] * m[1] * m[15] - + m[8] * m[3] * m[13] - + m[12] * m[1] * m[11] + + m[12] * m[3] * m[9]; + + inv[13] = m[0] * m[9] * m[14] - + m[0] * m[10] * m[13] - + m[8] * m[1] * m[14] + + m[8] * m[2] * m[13] + + m[12] * m[1] * m[10] - + m[12] * m[2] * m[9]; + + inv[2] = m[1] * m[6] * m[15] - + m[1] * m[7] * m[14] - + m[5] * m[2] * m[15] + + m[5] * m[3] * m[14] + + m[13] * m[2] * m[7] - + m[13] * m[3] * m[6]; + + inv[6] = -m[0] * m[6] * m[15] + + m[0] * m[7] * m[14] + + m[4] * m[2] * m[15] - + m[4] * m[3] * m[14] - + m[12] * m[2] * m[7] + + m[12] * m[3] * m[6]; + + inv[10] = m[0] * m[5] * m[15] - + m[0] * m[7] * m[13] - + m[4] * m[1] * m[15] + + m[4] * m[3] * m[13] + + m[12] * m[1] * m[7] - + m[12] * m[3] * m[5]; + + inv[14] = -m[0] * m[5] * m[14] + + m[0] * m[6] * m[13] + + m[4] * m[1] * m[14] - + m[4] * m[2] * m[13] - + m[12] * m[1] * m[6] + + m[12] * m[2] * m[5]; + + inv[3] = -m[1] * m[6] * m[11] + + m[1] * m[7] * m[10] + + m[5] * m[2] * m[11] - + m[5] * m[3] * m[10] - + m[9] * m[2] * m[7] + + m[9] * m[3] * m[6]; + + inv[7] = m[0] * m[6] * m[11] - + m[0] * m[7] * m[10] - + m[4] * m[2] * m[11] + + m[4] * m[3] * m[10] + + m[8] * m[2] * m[7] - + m[8] * m[3] * m[6]; + + inv[11] = -m[0] * m[5] * m[11] + + m[0] * m[7] * m[9] + + m[4] * m[1] * m[11] - + m[4] * m[3] * m[9] - + m[8] * m[1] * m[7] + + m[8] * m[3] * m[5]; + + inv[15] = m[0] * m[5] * m[10] - + m[0] * m[6] * m[9] - + m[4] * m[1] * m[10] + + m[4] * m[2] * m[9] + + m[8] * m[1] * m[6] - + m[8] * m[2] * m[5]; + + det = m[0] * inv[0] + m[1] * inv[4] + m[2] * inv[8] + m[3] * inv[12]; + + if (det == 0) { + return false; + } + + det = 1.0 / det; + + for (i = 0; i < 16; i++) { + invOut[i] = inv[i] * det; + } + + return true; +} diff --git a/soh/soh/frame_interpolation.h b/soh/soh/frame_interpolation.h new file mode 100644 index 0000000000..64c7197cd3 --- /dev/null +++ b/soh/soh/frame_interpolation.h @@ -0,0 +1,61 @@ +#pragma once + +#include "include/z64math.h" + +#ifdef __cplusplus + +#include + +std::unordered_map FrameInterpolation_Interpolate(float step); + +extern "C" { + +#endif + +void FrameInterpolation_StartRecord(void); + +void FrameInterpolation_StopRecord(void); + +void FrameInterpolation_RecordOpenChild(const void* a, int b); + +void FrameInterpolation_RecordCloseChild(void); + +void FrameInterpolation_DontInterpolateCamera(void); + +int FrameInterpolation_GetCameraEpoch(void); + +void FrameInterpolation_RecordActorPosRotMatrix(void); + +void FrameInterpolation_RecordMatrixPush(void); + +void FrameInterpolation_RecordMatrixPop(void); + +void FrameInterpolation_RecordMatrixPut(MtxF* src); + +void FrameInterpolation_RecordMatrixMult(MtxF* mf, u8 mode); + +void FrameInterpolation_RecordMatrixTranslate(f32 x, f32 y, f32 z, u8 mode); + +void FrameInterpolation_RecordMatrixScale(f32 x, f32 y, f32 z, u8 mode); + +void FrameInterpolation_RecordMatrixRotate1Coord(u32 coord, f32 value, u8 mode); + +void FrameInterpolation_RecordMatrixRotateZYX(s16 x, s16 y, s16 z, u8 mode); + +void FrameInterpolation_RecordMatrixTranslateRotateZYX(Vec3f* translation, Vec3s* rotation); + +void FrameInterpolation_RecordMatrixSetTranslateRotateYXZ(f32 translateX, f32 translateY, f32 translateZ, Vec3s* rot); + +void FrameInterpolation_RecordMatrixMtxFToMtx(MtxF* src, Mtx* dest); + +void FrameInterpolation_RecordMatrixToMtx(Mtx* dest, char* file, s32 line); + +void FrameInterpolation_RecordMatrixReplaceRotation(MtxF* mf); + +void FrameInterpolation_RecordMatrixRotateAxis(f32 angle, Vec3f* axis, u8 mode); + +void FrameInterpolation_RecordSkinMatrixMtxFToMtx(MtxF* src, Mtx* dest); + +#ifdef __cplusplus +} +#endif diff --git a/soh/src/boot/build.c b/soh/src/boot/build.c index f603804674..94d142020e 100644 --- a/soh/src/boot/build.c +++ b/soh/src/boot/build.c @@ -1,4 +1,4 @@ -const char gBuildVersion[] = "DECKARD ALFA (1.0.0)"; +const char gBuildVersion[] = "ROY ALFA (2.0.0)"; const char gBuildTeam[] = "github.com/harbourmasters"; const char gBuildDate[] = __DATE__ " " __TIME__; const char gBuildMakeOption[] = ""; diff --git a/soh/src/code/graph.c b/soh/src/code/graph.c index 4b1887c18e..651d405d57 100644 --- a/soh/src/code/graph.c +++ b/soh/src/code/graph.c @@ -478,7 +478,10 @@ static void RunFrame() Graph_StartFrame(); - PadMgr_ThreadEntry(&gPadMgr); + // TODO: Workaround for rumble being too long. Implement os thread functions. + for (int i = 0; i < 3; i++) { + PadMgr_ThreadEntry(&gPadMgr); + } Graph_Update(&runFrameContext.gfxCtx, runFrameContext.gameState); ticksB = GetPerfCounter(); diff --git a/soh/src/code/sys_matrix.c b/soh/src/code/sys_matrix.c index 9d7c73967c..bc90bf669d 100644 --- a/soh/src/code/sys_matrix.c +++ b/soh/src/code/sys_matrix.c @@ -1,5 +1,7 @@ #include "global.h" +#include "soh/frame_interpolation.h" + // clang-format off Mtx gMtxClear = { 65536, 0, 1, 0, @@ -25,11 +27,13 @@ void Matrix_Init(GameState* gameState) { } void Matrix_Push(void) { + FrameInterpolation_RecordMatrixPush(); Matrix_MtxFCopy(sCurrentMatrix + 1, sCurrentMatrix); sCurrentMatrix++; } void Matrix_Pop(void) { + FrameInterpolation_RecordMatrixPop(); sCurrentMatrix--; ASSERT(sCurrentMatrix >= sMatrixStack, "Matrix_now >= Matrix_stack", "../sys_matrix.c", 176); } @@ -39,6 +43,7 @@ void Matrix_Get(MtxF* dest) { } void Matrix_Put(MtxF* src) { + FrameInterpolation_RecordMatrixPut(src); Matrix_MtxFCopy(sCurrentMatrix, src); } @@ -47,6 +52,7 @@ MtxF* Matrix_GetCurrent(void) { } void Matrix_Mult(MtxF* mf, u8 mode) { + FrameInterpolation_RecordMatrixMult(mf, mode); MtxF* cmf = Matrix_GetCurrent(); if (mode == MTXMODE_APPLY) { @@ -57,6 +63,7 @@ void Matrix_Mult(MtxF* mf, u8 mode) { } void Matrix_Translate(f32 x, f32 y, f32 z, u8 mode) { + FrameInterpolation_RecordMatrixTranslate(x, y, z, mode); MtxF* cmf = sCurrentMatrix; f32 tx; f32 ty; @@ -80,6 +87,7 @@ void Matrix_Translate(f32 x, f32 y, f32 z, u8 mode) { } void Matrix_Scale(f32 x, f32 y, f32 z, u8 mode) { + FrameInterpolation_RecordMatrixScale(x, y, z, mode); MtxF* cmf = sCurrentMatrix; if (mode == MTXMODE_APPLY) { @@ -101,6 +109,7 @@ void Matrix_Scale(f32 x, f32 y, f32 z, u8 mode) { } void Matrix_RotateX(f32 x, u8 mode) { + FrameInterpolation_RecordMatrixRotate1Coord(0, x, mode); MtxF* cmf; f32 sin; f32 cos; @@ -165,6 +174,7 @@ void Matrix_RotateX(f32 x, u8 mode) { } void Matrix_RotateY(f32 y, u8 mode) { + FrameInterpolation_RecordMatrixRotate1Coord(1, y, mode); MtxF* cmf; f32 sin; f32 cos; @@ -229,6 +239,7 @@ void Matrix_RotateY(f32 y, u8 mode) { } void Matrix_RotateZ(f32 z, u8 mode) { + FrameInterpolation_RecordMatrixRotate1Coord(2, z, mode); MtxF* cmf; f32 sin; f32 cos; @@ -299,6 +310,7 @@ void Matrix_RotateZ(f32 z, u8 mode) { * Original Name: Matrix_RotateXYZ, changed to reflect rotation order. */ void Matrix_RotateZYX(s16 x, s16 y, s16 z, u8 mode) { + FrameInterpolation_RecordMatrixRotateZYX(x, y, z, mode); MtxF* cmf = sCurrentMatrix; f32 temp1; f32 temp2; @@ -389,6 +401,7 @@ void Matrix_RotateZYX(s16 x, s16 y, s16 z, u8 mode) { * transformed according to whatever the matrix was previously. */ void Matrix_TranslateRotateZYX(Vec3f* translation, Vec3s* rotation) { + FrameInterpolation_RecordMatrixTranslateRotateZYX(translation, rotation); MtxF* cmf = sCurrentMatrix; f32 sin = Math_SinS(rotation->z); f32 cos = Math_CosS(rotation->z); @@ -530,15 +543,20 @@ void Matrix_SetTranslateRotateYXZ(f32 translateX, f32 translateY, f32 translateZ } else { cmf->yx = 0.0f; } + FrameInterpolation_RecordMatrixSetTranslateRotateYXZ(translateX, translateY, translateZ, rot); } Mtx* Matrix_MtxFToMtx(MtxF* src, Mtx* dest) { + FrameInterpolation_RecordMatrixMtxFToMtx(src, dest); guMtxF2L(src, dest); return dest; } Mtx* Matrix_ToMtx(Mtx* dest, char* file, s32 line) { - return Matrix_MtxFToMtx(Matrix_CheckFloats(sCurrentMatrix, file, line), dest); + FrameInterpolation_RecordMatrixToMtx(dest, file, line); + guMtxF2L(Matrix_CheckFloats(sCurrentMatrix, file, line), dest); + return dest; + //return Matrix_MtxFToMtx(Matrix_CheckFloats(sCurrentMatrix, file, line), dest); } Mtx* Matrix_NewMtx(GraphicsContext* gfxCtx, char* file, s32 line) { @@ -627,6 +645,7 @@ void Matrix_Transpose(MtxF* mf) { * seen as replacing the R rotation with `mf`, hence the function name. */ void Matrix_ReplaceRotation(MtxF* mf) { + FrameInterpolation_RecordMatrixReplaceRotation(mf); MtxF* cmf = sCurrentMatrix; f32 acc; f32 temp; @@ -779,6 +798,7 @@ void Matrix_MtxFToZYXRotS(MtxF* mf, Vec3s* rotDest, s32 flag) { * NB: `axis` is assumed to be a unit vector. */ void Matrix_RotateAxis(f32 angle, Vec3f* axis, u8 mode) { + FrameInterpolation_RecordMatrixRotateAxis(angle, axis, mode); MtxF* cmf; f32 sin; f32 cos; diff --git a/soh/src/code/z_actor.c b/soh/src/code/z_actor.c index a9f50f6876..caa16a7ecd 100644 --- a/soh/src/code/z_actor.c +++ b/soh/src/code/z_actor.c @@ -6,6 +6,7 @@ #include "objects/gameplay_keep/gameplay_keep.h" #include "objects/gameplay_dangeon_keep/gameplay_dangeon_keep.h" #include "objects/object_bdoor/object_bdoor.h" +#include "soh/frame_interpolation.h" #if defined(_MSC_VER) || defined(__GNUC__) #include @@ -410,6 +411,7 @@ void func_8002C124(TargetContext* targetCtx, GlobalContext* globalCtx) { f32 var2; s32 i; + FrameInterpolation_RecordOpenChild(actor, 0); player = GET_PLAYER(globalCtx); spCE = 0xFF; @@ -486,10 +488,12 @@ void func_8002C124(TargetContext* targetCtx, GlobalContext* globalCtx) { } } } + FrameInterpolation_RecordCloseChild(); } actor = targetCtx->unk_94; if ((actor != NULL) && !(actor->flags & ACTOR_FLAG_27)) { + FrameInterpolation_RecordOpenChild(actor, 1); NaviColor* naviColor = &sNaviColorList[actor->category]; POLY_XLU_DISP = Gfx_CallSetupDL(POLY_XLU_DISP, 0x7); @@ -503,6 +507,7 @@ void func_8002C124(TargetContext* targetCtx, GlobalContext* globalCtx) { gSPMatrix(POLY_XLU_DISP++, Matrix_NewMtx(globalCtx->state.gfxCtx, "../z_actor.c", 2153), G_MTX_MODELVIEW | G_MTX_LOAD); gSPDisplayList(POLY_XLU_DISP++, gZTargetArrowDL); + FrameInterpolation_RecordCloseChild(); } CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_actor.c", 2158); @@ -2490,6 +2495,7 @@ void Actor_Draw(GlobalContext* globalCtx, Actor* actor) { Fault_AddClient(&faultClient, Actor_FaultPrint, actor, "Actor_draw"); + FrameInterpolation_RecordOpenChild(actor, 0); OPEN_DISPS(globalCtx->state.gfxCtx, "../z_actor.c", 6035); lights = LightContext_NewLights(&globalCtx->lightCtx, globalCtx->state.gfxCtx); @@ -2497,6 +2503,7 @@ void Actor_Draw(GlobalContext* globalCtx, Actor* actor) { Lights_BindAll(lights, globalCtx->lightCtx.listHead, (actor->flags & ACTOR_FLAG_22) ? NULL : &actor->world.pos); Lights_Draw(lights, globalCtx->state.gfxCtx); + FrameInterpolation_RecordActorPosRotMatrix(); if (actor->flags & ACTOR_FLAG_12) { Matrix_SetTranslateRotateYXZ( actor->world.pos.x + globalCtx->mainCamera.skyboxOffset.x, @@ -2546,6 +2553,7 @@ void Actor_Draw(GlobalContext* globalCtx, Actor* actor) { } CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_actor.c", 6119); + FrameInterpolation_RecordCloseChild(); Fault_RemoveClient(&faultClient); } diff --git a/soh/src/code/z_camera.c b/soh/src/code/z_camera.c index 41c16b5cce..00f3f70f0b 100644 --- a/soh/src/code/z_camera.c +++ b/soh/src/code/z_camera.c @@ -6,6 +6,8 @@ #include "overlays/actors/ovl_En_Horse/z_en_horse.h" +#include "soh/frame_interpolation.h" + s16 Camera_ChangeSettingFlags(Camera* camera, s16 setting, s16 flags); s32 Camera_ChangeModeFlags(Camera* camera, s16 mode, u8 flags); s32 Camera_QRegInit(void); @@ -6675,6 +6677,7 @@ s32 Camera_Special9(Camera* camera) { case 1: spec9->doorParams.timer1--; if (spec9->doorParams.timer1 <= 0) { + FrameInterpolation_DontInterpolateCamera(); camera->animState++; if (params->interfaceFlags & 1) { camPosData = Camera_GetCamBGData(camera); @@ -7968,6 +7971,8 @@ s32 Camera_SetCSParams(Camera* camera, CutsceneCameraPoint* atPoints, CutsceneCa camera->speedRatio = 0.0f; } + FrameInterpolation_DontInterpolateCamera(); + return 1; } diff --git a/soh/src/code/z_eff_blure.c b/soh/src/code/z_eff_blure.c index e08f8f3379..f3a1e61223 100644 --- a/soh/src/code/z_eff_blure.c +++ b/soh/src/code/z_eff_blure.c @@ -1,6 +1,8 @@ #include "global.h" #include "objects/gameplay_keep/gameplay_keep.h" +#include "soh/frame_interpolation.h" + void EffectBlure_AddVertex(EffectBlure* this, Vec3f* p1, Vec3f* p2) { EffectBlureElement* elem; s32 numElements; @@ -946,6 +948,7 @@ void EffectBlure_Draw(void* thisx, GraphicsContext* gfxCtx) { s32 j; s32 phi_t2; + FrameInterpolation_RecordOpenChild(this, 0); OPEN_DISPS(gfxCtx, "../z_eff_blure.c", 1596); gSPMatrix(POLY_XLU_DISP++, &gMtxClear, G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); @@ -1059,4 +1062,5 @@ void EffectBlure_Draw(void* thisx, GraphicsContext* gfxCtx) { } CLOSE_DISPS(gfxCtx, "../z_eff_blure.c", 1823); + FrameInterpolation_RecordCloseChild(); } diff --git a/soh/src/code/z_eff_shield_particle.c b/soh/src/code/z_eff_shield_particle.c index fad499286c..73134a6540 100644 --- a/soh/src/code/z_eff_shield_particle.c +++ b/soh/src/code/z_eff_shield_particle.c @@ -2,6 +2,8 @@ #include "vt.h" #include "objects/gameplay_keep/gameplay_keep.h" +#include "soh/frame_interpolation.h" + static Vtx sVertices[5] = { VTX(-32, -32, 0, 0, 1024, 0xFF, 0xFF, 0xFF, 0xFF), VTX(32, 32, 0, 1024, 0, 0xFF, 0xFF, 0xFF, 0xFF), @@ -154,6 +156,7 @@ void EffectShieldParticle_Draw(void* thisx, GraphicsContext* gfxCtx) { Color_RGBA8 primColor; Color_RGBA8 envColor; + FrameInterpolation_RecordOpenChild(this, 0); OPEN_DISPS(gfxCtx, "../z_eff_shield_particle.c", 272); if (this != NULL) { @@ -213,4 +216,5 @@ void EffectShieldParticle_Draw(void* thisx, GraphicsContext* gfxCtx) { } CLOSE_DISPS(gfxCtx, "../z_eff_shield_particle.c", 359); + FrameInterpolation_RecordCloseChild(); } diff --git a/soh/src/code/z_eff_spark.c b/soh/src/code/z_eff_spark.c index cfe8f628f2..b76968371a 100644 --- a/soh/src/code/z_eff_spark.c +++ b/soh/src/code/z_eff_spark.c @@ -1,6 +1,8 @@ #include "global.h" #include "objects/gameplay_keep/gameplay_keep.h" +#include "soh/frame_interpolation.h" + // original name: "spark" void EffectSpark_Init(void* thisx, void* initParamsx) { EffectSpark* this = (EffectSpark*)thisx; @@ -152,6 +154,7 @@ void EffectSpark_Draw(void* thisx, GraphicsContext* gfxCtx) { u8 sp1C4; f32 ratio; + FrameInterpolation_RecordOpenChild(this, 0); OPEN_DISPS(gfxCtx, "../z_eff_spark.c", 293); if (this != NULL) { @@ -274,4 +277,5 @@ void EffectSpark_Draw(void* thisx, GraphicsContext* gfxCtx) { end: CLOSE_DISPS(gfxCtx, "../z_eff_spark.c", 498); + FrameInterpolation_RecordCloseChild(); } diff --git a/soh/src/code/z_effect_soft_sprite.c b/soh/src/code/z_effect_soft_sprite.c index aa0b922fe6..2548966cc1 100644 --- a/soh/src/code/z_effect_soft_sprite.c +++ b/soh/src/code/z_effect_soft_sprite.c @@ -1,6 +1,8 @@ #include "global.h" #include "vt.h" +#include "soh/frame_interpolation.h" + EffectSsInfo sEffectSsInfo = { 0 }; // "EffectSS2Info" void EffectSs_InitInfo(GlobalContext* globalCtx, s32 tableSize) { @@ -233,6 +235,7 @@ void EffectSs_Spawn(GlobalContext* globalCtx, s32 type, s32 priority, void* init sEffectSsInfo.table[index].type = type; sEffectSsInfo.table[index].priority = priority; + sEffectSsInfo.table[index].epoch++; if (initInfo->init(globalCtx, index, &sEffectSsInfo.table[index], initParams) == 0) { osSyncPrintf(VT_FGCOL(GREEN)); @@ -284,7 +287,9 @@ void EffectSs_Draw(GlobalContext* globalCtx, s32 index) { EffectSs* effectSs = &sEffectSsInfo.table[index]; if (effectSs->draw != NULL) { + FrameInterpolation_RecordOpenChild(effectSs, effectSs->epoch); effectSs->draw(globalCtx, index, effectSs); + FrameInterpolation_RecordCloseChild(); } } diff --git a/soh/src/code/z_lifemeter.c b/soh/src/code/z_lifemeter.c index 344022505a..5f1863440f 100644 --- a/soh/src/code/z_lifemeter.c +++ b/soh/src/code/z_lifemeter.c @@ -498,8 +498,8 @@ void HealthMeter_HandleCriticalAlarm(GlobalContext* globalCtx) { if (interfaceCtx->unk_22A <= 0) { interfaceCtx->unk_22A = 0; interfaceCtx->unk_22C = 0; - if (!Player_InCsMode(globalCtx) && (globalCtx->pauseCtx.state == 0) && - (globalCtx->pauseCtx.debugState == 0) && HealthMeter_IsCritical() && !Gameplay_InCsMode(globalCtx)) { + if (CVar_GetS32("gLowHpAlarm", 0) == 0 && !Player_InCsMode(globalCtx) && (globalCtx->pauseCtx.state == 0) && + (globalCtx->pauseCtx.debugState == 0) && HealthMeter_IsCritical() && !Gameplay_InCsMode(globalCtx)) { func_80078884(NA_SE_SY_HITPOINT_ALARM); } } diff --git a/soh/src/code/z_lights.c b/soh/src/code/z_lights.c index c9b60ceca1..bee6772765 100644 --- a/soh/src/code/z_lights.c +++ b/soh/src/code/z_lights.c @@ -4,6 +4,8 @@ #include "objects/gameplay_keep/gameplay_keep.h" +#include "soh/frame_interpolation.h" + #define LIGHTS_BUFFER_SIZE 32 //#define LIGHTS_BUFFER_SIZE 1024 // Kill me @@ -434,12 +436,14 @@ void Lights_DrawGlow(GlobalContext* globalCtx) { if ((info->type == LIGHT_POINT_GLOW) && (params->drawGlow)) { scale = SQ(params->radius) * 0.0000026f; + FrameInterpolation_RecordOpenChild(node, 0); gDPSetPrimColor(POLY_XLU_DISP++, 0, 0, params->color[0], params->color[1], params->color[2], 50); Matrix_Translate(params->x, params->y, params->z, MTXMODE_NEW); Matrix_Scale(scale, scale, scale, MTXMODE_APPLY); gSPMatrix(POLY_XLU_DISP++, Matrix_NewMtx(globalCtx->state.gfxCtx, "../z_lights.c", 918), G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); gSPDisplayList(POLY_XLU_DISP++, gGlowCircleDL); + FrameInterpolation_RecordCloseChild(); } node = node->next; diff --git a/soh/src/code/z_parameter.c b/soh/src/code/z_parameter.c index edc4348b95..8cfcccbf5d 100644 --- a/soh/src/code/z_parameter.c +++ b/soh/src/code/z_parameter.c @@ -753,7 +753,15 @@ void func_80083108(GlobalContext* globalCtx) { gSaveContext.buttonStatus[0] = BTN_DISABLED; for (i = 1; i < 4; i++) { - if (func_8008F2F8(globalCtx) == 2) { + if ((gSaveContext.equips.buttonItems[i] >= ITEM_SHIELD_DEKU) && + (gSaveContext.equips.buttonItems[i] <= ITEM_BOOTS_HOVER)) { + // Equipment on c-buttons is always enabled + if (gSaveContext.buttonStatus[i] == BTN_DISABLED) { + sp28 = 1; + } + + gSaveContext.buttonStatus[i] = BTN_ENABLED; + } else if (func_8008F2F8(globalCtx) == 2) { if ((gSaveContext.equips.buttonItems[i] != ITEM_HOOKSHOT) && (gSaveContext.equips.buttonItems[i] != ITEM_LONGSHOT)) { if (gSaveContext.buttonStatus[i] == BTN_ENABLED) { diff --git a/soh/src/code/z_play.c b/soh/src/code/z_play.c index 2934e5e1bf..632ed41f66 100644 --- a/soh/src/code/z_play.c +++ b/soh/src/code/z_play.c @@ -5,6 +5,8 @@ #include "soh/Enhancements/gameconsole.h" +#include "soh/frame_interpolation.h" + void* D_8012D1F0 = NULL; //UNK_TYPE D_8012D1F4 = 0; // unused Input* D_8012D1F8 = NULL; @@ -1379,7 +1381,9 @@ void Gameplay_Main(GameState* thisx) { LOG_NUM("1", 1, "../z_play.c", 4583); } + FrameInterpolation_StartRecord(); Gameplay_Draw(globalCtx); + FrameInterpolation_StopRecord(); if (1 && HREG(63)) { LOG_NUM("1", 1, "../z_play.c", 4587); diff --git a/soh/src/code/z_player_lib.c b/soh/src/code/z_player_lib.c index 355d323e06..4e4e1ff702 100644 --- a/soh/src/code/z_player_lib.c +++ b/soh/src/code/z_player_lib.c @@ -29,7 +29,8 @@ s16 sBootData[PLAYER_BOOTS_MAX][17] = { // Used to map action params to model groups u8 sActionModelGroups[] = { 3, 15, 10, 2, 2, 5, 10, 11, 6, 6, 6, 6, 6, 6, 6, 6, 9, 9, 7, 7, 8, 3, 3, 6, 3, 3, 3, 3, 12, 13, 14, 14, 14, 14, - 14, 14, 14, 14, 14, 14, 14, 14, 14, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 14, 14, 14, 14, 14, 14, 14, 14, 14, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3 }; TextTriggerEntry sTextTriggers[] = { diff --git a/soh/src/code/z_skin_matrix.c b/soh/src/code/z_skin_matrix.c index f7df8091fa..d822d6c134 100644 --- a/soh/src/code/z_skin_matrix.c +++ b/soh/src/code/z_skin_matrix.c @@ -1,6 +1,8 @@ #include "global.h" #include "vt.h" +#include "soh/frame_interpolation.h" + // clang-format off MtxF sMtxFClear = { 1.0f, 0.0f, 0.0f, 0.0f, @@ -523,6 +525,7 @@ void SkinMatrix_Vec3sToVec3f(Vec3s* src, Vec3f* dest) { } void SkinMatrix_MtxFToMtx(MtxF* src, Mtx* dest) { + FrameInterpolation_RecordSkinMatrixMtxFToMtx(src, dest); guMtxF2L(src, dest); } diff --git a/soh/src/code/z_view.c b/soh/src/code/z_view.c index 97f1ad26e4..e03daee832 100644 --- a/soh/src/code/z_view.c +++ b/soh/src/code/z_view.c @@ -2,6 +2,9 @@ #include "vt.h" #include +#include + +#include "soh/frame_interpolation.h" vu32 D_8012ABF0 = true; @@ -277,6 +280,10 @@ void func_800AAA50(View* view, s32 arg1) { } } +static float sqr(float a) { + return a * a; +} + s32 func_800AAA9C(View* view) { f32 aspect; s32 width; @@ -307,6 +314,85 @@ s32 func_800AAA9C(View* view) { height = view->viewport.bottomY - view->viewport.topY; aspect = (f32)width / (f32)height; + viewing = Graph_Alloc(gfxCtx, sizeof(Mtx)); + LogUtils_CheckNullPointer("viewing", viewing, "../z_view.c", 667); + view->viewingPtr = viewing; + + if (view->eye.x == view->lookAt.x && view->eye.y == view->lookAt.y && view->eye.z == view->lookAt.z) { + view->eye.x += 1.0f; + view->eye.y += 1.0f; + view->eye.z += 1.0f; + } + + func_800ABE74(view->eye.x, view->eye.y, view->eye.z); + MtxF viewingF; + guLookAtF(viewingF.mf, view->eye.x, view->eye.y, view->eye.z, view->lookAt.x, view->lookAt.y, view->lookAt.z, view->up.x, + view->up.y, view->up.z); + + // Some heuristics to identify instant camera movements and skip interpolation in that case + + static View old_view; + + float dirx = view->eye.x - view->lookAt.x; + float diry = view->eye.y - view->lookAt.y; + float dirz = view->eye.z - view->lookAt.z; + float dir_dist = sqrtf(sqr(dirx) + sqr(diry) + sqr(dirz)); + dirx /= dir_dist; + diry /= dir_dist; + dirz /= dir_dist; + + float odirx = old_view.eye.x - old_view.lookAt.x; + float odiry = old_view.eye.y - old_view.lookAt.y; + float odirz = old_view.eye.z - old_view.lookAt.z; + float odir_dist = sqrtf(sqr(odirx) + sqr(odiry) + sqr(odirz)); + odirx /= odir_dist; + odiry /= odir_dist; + odirz /= odir_dist; + + float eye_dist = sqrtf(sqr(view->eye.x - old_view.eye.x) + sqr(view->eye.y - old_view.eye.y) + sqr(view->eye.z - old_view.eye.z)); + float look_dist = sqrtf(sqr(view->lookAt.x - old_view.lookAt.x) + sqr(view->lookAt.y - old_view.lookAt.y) + sqr(view->lookAt.z - old_view.lookAt.z)); + float up_dist = sqrtf(sqr(view->up.x - old_view.up.x) + sqr(view->up.y - old_view.up.y) + sqr(view->up.z - old_view.up.z)); + float d_dist = sqrtf(sqr(dirx - odirx) + sqr(diry - odiry) + sqr(dirz - odirz)); + + bool dont_interpolate = false; + + if (up_dist < 0.01 && d_dist < 0.01) { + if (eye_dist + look_dist > 300) { + dont_interpolate = true; + } + } else { + if (eye_dist >= 400) { + dont_interpolate = true; + } + if (look_dist >= 100) { + dont_interpolate = true; + } + if (up_dist >= 1.50f) { + dont_interpolate = true; + } + if (d_dist >= 1.414f && look_dist >= 15) { + dont_interpolate = true; + } + if (d_dist >= 1.414f && up_dist >= 0.31f && look_dist >= 1 && eye_dist >= 300) { + dont_interpolate = true; + } + if (d_dist >= 0.5f && up_dist >= 0.31f && look_dist >= 3 && eye_dist >= 170) { + dont_interpolate = true; + } + if (look_dist >= 52 && eye_dist >= 52) { + dont_interpolate = true; + } + if (look_dist >= 30 && eye_dist >= 90) { + dont_interpolate = true; + } + } + + if (dont_interpolate) { + FrameInterpolation_DontInterpolateCamera(); + } + + FrameInterpolation_RecordOpenChild(NULL, FrameInterpolation_GetCameraEpoch()); + if (HREG(80) == 11) { if (HREG(94) != 11) { HREG(94) = 11; @@ -347,22 +433,17 @@ s32 func_800AAA9C(View* view) { gSPPerspNormalize(POLY_KAL_DISP++, view->normal); gSPMatrix(POLY_KAL_DISP++, projection, G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_PROJECTION); - viewing = Graph_Alloc(gfxCtx, sizeof(Mtx)); - LogUtils_CheckNullPointer("viewing", viewing, "../z_view.c", 667); - view->viewingPtr = viewing; - - if (view->eye.x == view->lookAt.x && view->eye.y == view->lookAt.y && view->eye.z == view->lookAt.z) { - view->eye.x += 1.0f; - view->eye.y += 1.0f; - view->eye.z += 1.0f; - } - - func_800ABE74(view->eye.x, view->eye.y, view->eye.z); - guLookAt(viewing, view->eye.x, view->eye.y, view->eye.z, view->lookAt.x, view->lookAt.y, view->lookAt.z, view->up.x, - view->up.y, view->up.z); + Matrix_MtxFToMtx(viewingF.mf, viewing); view->viewing = *viewing; + + /*if (eye_dist > 1 || look_dist > 1 || abs(up_dist) > 0.1 || abs(d_dist) > 0.1) + printf("%d %f %f %f, %f %f %f, %f %f %f, %f %f %f %f %d\n", (int)view->fovy, view->eye.x, view->eye.y, view->eye.z, view->lookAt.x, view->lookAt.y, view->lookAt.z, + view->up.x, view->up.y, view->up.z, eye_dist, look_dist, up_dist, d_dist, dont_interpolate);*/ + + old_view = *view; + if (QREG(88) & 2) { s32 i; MtxF mf; @@ -374,10 +455,10 @@ s32 func_800AAA9C(View* view) { } osSyncPrintf("\n"); } - gSPMatrix(POLY_OPA_DISP++, viewing, G_MTX_NOPUSH | G_MTX_MUL | G_MTX_PROJECTION); gSPMatrix(POLY_XLU_DISP++, viewing, G_MTX_NOPUSH | G_MTX_MUL | G_MTX_PROJECTION); gSPMatrix(POLY_KAL_DISP++, viewing, G_MTX_NOPUSH | G_MTX_MUL | G_MTX_PROJECTION); + FrameInterpolation_RecordCloseChild(); CLOSE_DISPS(gfxCtx, "../z_view.c", 711); diff --git a/soh/src/code/z_vr_box_draw.c b/soh/src/code/z_vr_box_draw.c index 2927893332..13eb93bd08 100644 --- a/soh/src/code/z_vr_box_draw.c +++ b/soh/src/code/z_vr_box_draw.c @@ -1,5 +1,7 @@ #include "global.h" +#include "soh/frame_interpolation.h" + Mtx* sSkyboxDrawMatrix; Mtx* SkyboxDraw_UpdateMatrix(SkyboxContext* skyboxCtx, f32 x, f32 y, f32 z) { @@ -13,6 +15,7 @@ Mtx* SkyboxDraw_UpdateMatrix(SkyboxContext* skyboxCtx, f32 x, f32 y, f32 z) { void SkyboxDraw_Draw(SkyboxContext* skyboxCtx, GraphicsContext* gfxCtx, s16 skyboxId, s16 blend, f32 x, f32 y, f32 z) { OPEN_DISPS(gfxCtx, "../z_vr_box_draw.c", 52); + FrameInterpolation_RecordOpenChild(NULL, FrameInterpolation_GetCameraEpoch()); func_800945A0(gfxCtx); @@ -85,7 +88,7 @@ void SkyboxDraw_Draw(SkyboxContext* skyboxCtx, GraphicsContext* gfxCtx, s16 skyb gDPPipeSync(POLY_OPA_DISP++); //gsSPShaderTest2(POLY_OPA_DISP++); - + FrameInterpolation_RecordCloseChild(); CLOSE_DISPS(gfxCtx, "../z_vr_box_draw.c", 125); } diff --git a/soh/src/libultra/gu/guLookAt.c b/soh/src/libultra/gu/guLookAt.c index 58946f4dc6..f94204bc4e 100644 --- a/soh/src/libultra/gu/guLookAt.c +++ b/soh/src/libultra/gu/guLookAt.c @@ -62,5 +62,6 @@ void guLookAt(Mtx* m, f32 xEye, f32 yEye, f32 zEye, f32 xAt, f32 yAt, f32 zAt, f guLookAtF(mf, xEye, yEye, zEye, xAt, yAt, zAt, xUp, yUp, zUp); - guMtxF2L((MtxF*)mf, m); + //guMtxF2L((MtxF*)mf, m); + Matrix_MtxFToMtx((MtxF*)mf, m); } diff --git a/soh/src/libultra/gu/guPerspectiveF.c b/soh/src/libultra/gu/guPerspectiveF.c index 731cec3e9e..dd020f5899 100644 --- a/soh/src/libultra/gu/guPerspectiveF.c +++ b/soh/src/libultra/gu/guPerspectiveF.c @@ -37,6 +37,6 @@ void guPerspective(Mtx* m, u16* perspNorm, f32 fovy, f32 aspect, f32 near, f32 f f32 mf[4][4]; guPerspectiveF(mf, perspNorm, fovy, aspect, near, far, scale); - guMtxF2L((MtxF*)mf, m); - + //guMtxF2L((MtxF*)mf, m); + Matrix_MtxFToMtx((MtxF*)mf, m); } diff --git a/soh/src/libultra/gu/ortho.c b/soh/src/libultra/gu/ortho.c index 517ba1dccc..77d43ce218 100644 --- a/soh/src/libultra/gu/ortho.c +++ b/soh/src/libultra/gu/ortho.c @@ -1,5 +1,7 @@ #include "global.h" +#include "soh/frame_interpolation.h" + void guOrthoF(f32 mf[4][4], f32 left, f32 right, f32 bottom, f32 top, f32 near, f32 far, f32 scale) { s32 i, j; @@ -25,5 +27,8 @@ void guOrtho(Mtx* mtx, f32 left, f32 right, f32 bottom, f32 top, f32 near, f32 f guOrthoF(mf, left, right, bottom, top, near, far, scale); - guMtxF2L((MtxF*)mf, mtx); + //guMtxF2L((MtxF*)mf, mtx); + FrameInterpolation_RecordOpenChild("ortho", 0); + Matrix_MtxFToMtx((MtxF*)mf, mtx); + FrameInterpolation_RecordCloseChild(); } diff --git a/soh/src/overlays/actors/ovl_Bg_Ganon_Otyuka/z_bg_ganon_otyuka.c b/soh/src/overlays/actors/ovl_Bg_Ganon_Otyuka/z_bg_ganon_otyuka.c index d1e940619e..9616204cd7 100644 --- a/soh/src/overlays/actors/ovl_Bg_Ganon_Otyuka/z_bg_ganon_otyuka.c +++ b/soh/src/overlays/actors/ovl_Bg_Ganon_Otyuka/z_bg_ganon_otyuka.c @@ -8,6 +8,8 @@ #include "overlays/actors/ovl_Boss_Ganon/z_boss_ganon.h" #include "vt.h" +#include "soh/frame_interpolation.h" + #define FLAGS (ACTOR_FLAG_4 | ACTOR_FLAG_5) typedef enum { @@ -283,6 +285,7 @@ void BgGanonOtyuka_Draw(Actor* thisx, GlobalContext* globalCtx) { platform = (BgGanonOtyuka*)actor; if (platform->dyna.actor.projectedPos.z > spBC) { + FrameInterpolation_RecordOpenChild(platform, 0); if (camera->eye.y > platform->dyna.actor.world.pos.y) { phi_s2 = sPlatformTopDL; } else { @@ -309,7 +312,7 @@ void BgGanonOtyuka_Draw(Actor* thisx, GlobalContext* globalCtx) { } for (i = 0; i < ARRAY_COUNT(sSides); i++) { - if (platform->visibleSides & sSides[i]) { + if ((platform->visibleSides & sSides[i]) || 1) { // || 1 for frame interpolation Matrix_Push(); Matrix_Translate(sSideCenters[i].x, 0.0f, sSideCenters[i].z, MTXMODE_APPLY); Matrix_RotateY(sSideAngles[i], MTXMODE_APPLY); @@ -320,6 +323,7 @@ void BgGanonOtyuka_Draw(Actor* thisx, GlobalContext* globalCtx) { Matrix_Pop(); } } + FrameInterpolation_RecordCloseChild(); } } @@ -333,6 +337,7 @@ void BgGanonOtyuka_Draw(Actor* thisx, GlobalContext* globalCtx) { platform = (BgGanonOtyuka*)actor; if ((platform->dyna.actor.projectedPos.z > -30.0f) && (platform->flashState != FLASH_NONE)) { + FrameInterpolation_RecordOpenChild(platform, 0); gSPSegment(POLY_XLU_DISP++, 0x08, Gfx_TwoTexScroll(globalCtx->state.gfxCtx, 0, platform->flashTimer * 4, 0, 32, 64, 1, platform->flashTimer * 4, 0, 32, 64)); @@ -344,7 +349,7 @@ void BgGanonOtyuka_Draw(Actor* thisx, GlobalContext* globalCtx) { Matrix_Translate(platform->dyna.actor.world.pos.x, 0.0f, platform->dyna.actor.world.pos.z, MTXMODE_NEW); for (i = 0; i < ARRAY_COUNT(sSides); i++) { - if (platform->unwalledSides & sSides[i]) { + if ((platform->unwalledSides & sSides[i]) || 1) { // || 1 for frame interpolation Matrix_Push(); Matrix_Translate(sSideCenters[i].x, 0.0f, sSideCenters[i].z, MTXMODE_APPLY); Matrix_RotateY(sSideAngles[i], MTXMODE_APPLY); @@ -356,6 +361,7 @@ void BgGanonOtyuka_Draw(Actor* thisx, GlobalContext* globalCtx) { Matrix_Pop(); } } + FrameInterpolation_RecordCloseChild(); } } diff --git a/soh/src/overlays/actors/ovl_Bg_Hidan_Rsekizou/z_bg_hidan_rsekizou.c b/soh/src/overlays/actors/ovl_Bg_Hidan_Rsekizou/z_bg_hidan_rsekizou.c index 73044e8b1d..c2599e1856 100644 --- a/soh/src/overlays/actors/ovl_Bg_Hidan_Rsekizou/z_bg_hidan_rsekizou.c +++ b/soh/src/overlays/actors/ovl_Bg_Hidan_Rsekizou/z_bg_hidan_rsekizou.c @@ -240,7 +240,8 @@ void BgHidanRsekizou_Draw(Actor* thisx, GlobalContext* globalCtx) { POLY_XLU_DISP = Gfx_CallSetupDL(POLY_XLU_DISP, 0x14); - if ((s16)((Camera_GetCamDirYaw(GET_ACTIVE_CAM(globalCtx)) - this->dyna.actor.shape.rot.y) - 0x2E6C) >= 0) { + // Strange original code. Add || 1 for frame interpolation to get correct. + if ((s16)((Camera_GetCamDirYaw(GET_ACTIVE_CAM(globalCtx)) - this->dyna.actor.shape.rot.y) - 0x2E6C) >= 0 || 1) { for (i = 3; i >= 0; i--) { POLY_XLU_DISP = BgHidanRsekizou_DrawFireball(globalCtx, this, i, &mf, 0, POLY_XLU_DISP); } diff --git a/soh/src/overlays/actors/ovl_Boss_Fd/z_boss_fd.c b/soh/src/overlays/actors/ovl_Boss_Fd/z_boss_fd.c index 6497743465..5b468c7d30 100644 --- a/soh/src/overlays/actors/ovl_Boss_Fd/z_boss_fd.c +++ b/soh/src/overlays/actors/ovl_Boss_Fd/z_boss_fd.c @@ -12,6 +12,8 @@ #include "overlays/actors/ovl_Door_Warp1/z_door_warp1.h" #include "objects/gameplay_keep/gameplay_keep.h" +#include "soh/frame_interpolation.h" + #define FLAGS (ACTOR_FLAG_0 | ACTOR_FLAG_2 | ACTOR_FLAG_4 | ACTOR_FLAG_5) typedef enum { @@ -78,6 +80,7 @@ void BossFd_SpawnEmber(BossFdEffect* effect, Vec3f* position, Vec3f* velocity, V effect->scale = scale / 1000.0f; effect->alpha = 255; effect->timer1 = (s16)Rand_ZeroFloat(10.0f); + effect->epoch++; break; } } @@ -95,6 +98,7 @@ void BossFd_SpawnDebris(BossFdEffect* effect, Vec3f* position, Vec3f* velocity, effect->scale = scale / 1000.0f; effect->vFdFxRotX = Rand_ZeroFloat(100.0f); effect->vFdFxRotY = Rand_ZeroFloat(100.0f); + effect->epoch++; break; } } @@ -111,6 +115,7 @@ void BossFd_SpawnDust(BossFdEffect* effect, Vec3f* position, Vec3f* velocity, Ve effect->accel = *acceleration; effect->timer2 = 0; effect->scale = scale / 400.0f; + effect->epoch++; break; } } @@ -136,6 +141,7 @@ void BossFd_SpawnFireBreath(BossFdEffect* effect, Vec3f* position, Vec3f* veloci effect->timer2 = 0; effect->scale = scale / 400.0f; effect->kbAngle = kbAngle; + effect->epoch++; break; } } @@ -1522,6 +1528,7 @@ void BossFd_DrawEffects(BossFdEffect* effect, GlobalContext* globalCtx) { for (i = 0; i < 180; i++, effect++) { if (effect->type == BFD_FX_EMBER) { + FrameInterpolation_RecordOpenChild(effect, effect->epoch); if (!flag) { func_80093D84(globalCtx->state.gfxCtx); gSPDisplayList(POLY_XLU_DISP++, gVolvagiaEmberMaterialDL); @@ -1536,6 +1543,7 @@ void BossFd_DrawEffects(BossFdEffect* effect, GlobalContext* globalCtx) { gSPMatrix(POLY_XLU_DISP++, Matrix_NewMtx(gfxCtx, "../z_boss_fd.c", 4046), G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); gSPDisplayList(POLY_XLU_DISP++, gVolvagiaEmberModelDL); + FrameInterpolation_RecordCloseChild(); } } @@ -1543,6 +1551,7 @@ void BossFd_DrawEffects(BossFdEffect* effect, GlobalContext* globalCtx) { flag = false; for (i = 0; i < 180; i++, effect++) { if (effect->type == BFD_FX_DEBRIS) { + FrameInterpolation_RecordOpenChild(effect, effect->epoch); if (!flag) { func_80093D18(globalCtx->state.gfxCtx); gSPDisplayList(POLY_OPA_DISP++, gVolvagiaDebrisMaterialDL); @@ -1557,6 +1566,7 @@ void BossFd_DrawEffects(BossFdEffect* effect, GlobalContext* globalCtx) { gSPMatrix(POLY_OPA_DISP++, Matrix_NewMtx(gfxCtx, "../z_boss_fd.c", 4068), G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); gSPDisplayList(POLY_OPA_DISP++, gVolvagiaDebrisModelDL); + FrameInterpolation_RecordCloseChild(); } } @@ -1564,6 +1574,7 @@ void BossFd_DrawEffects(BossFdEffect* effect, GlobalContext* globalCtx) { flag = false; for (i = 0; i < 180; i++, effect++) { if (effect->type == BFD_FX_DUST) { + FrameInterpolation_RecordOpenChild(effect, effect->epoch); if (!flag) { POLY_XLU_DISP = Gfx_CallSetupDL(POLY_XLU_DISP, 0); gSPDisplayList(POLY_XLU_DISP++, gVolvagiaDustMaterialDL); @@ -1580,6 +1591,7 @@ void BossFd_DrawEffects(BossFdEffect* effect, GlobalContext* globalCtx) { G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); gSPSegment(POLY_XLU_DISP++, 0x08, SEGMENTED_TO_VIRTUAL(dustTex[effect->timer2])); gSPDisplayList(POLY_XLU_DISP++, gVolvagiaDustModelDL); + FrameInterpolation_RecordCloseChild(); } } @@ -1587,6 +1599,7 @@ void BossFd_DrawEffects(BossFdEffect* effect, GlobalContext* globalCtx) { flag = false; for (i = 0; i < 180; i++, effect++) { if (effect->type == BFD_FX_FIRE_BREATH) { + FrameInterpolation_RecordOpenChild(effect, effect->epoch); if (!flag) { POLY_XLU_DISP = Gfx_CallSetupDL(POLY_XLU_DISP, 0); gSPDisplayList(POLY_XLU_DISP++, gVolvagiaDustMaterialDL); @@ -1603,6 +1616,7 @@ void BossFd_DrawEffects(BossFdEffect* effect, GlobalContext* globalCtx) { G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); gSPSegment(POLY_XLU_DISP++, 0x08, SEGMENTED_TO_VIRTUAL(dustTex[effect->timer2])); gSPDisplayList(POLY_XLU_DISP++, gVolvagiaDustModelDL); + FrameInterpolation_RecordCloseChild(); } } @@ -1610,6 +1624,7 @@ void BossFd_DrawEffects(BossFdEffect* effect, GlobalContext* globalCtx) { flag = false; for (i = 0; i < 180; i++, effect++) { if (effect->type == BFD_FX_SKULL_PIECE) { + FrameInterpolation_RecordOpenChild(effect, effect->epoch); if (!flag) { func_80093D84(globalCtx->state.gfxCtx); gSPDisplayList(POLY_XLU_DISP++, gVolvagiaSkullPieceMaterialDL); @@ -1624,6 +1639,7 @@ void BossFd_DrawEffects(BossFdEffect* effect, GlobalContext* globalCtx) { gSPMatrix(POLY_XLU_DISP++, Matrix_NewMtx(gfxCtx, "../z_boss_fd.c", 4192), G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); gSPDisplayList(POLY_XLU_DISP++, gVolvagiaSkullPieceModelDL); + FrameInterpolation_RecordCloseChild(); } } diff --git a/soh/src/overlays/actors/ovl_Boss_Fd/z_boss_fd.h b/soh/src/overlays/actors/ovl_Boss_Fd/z_boss_fd.h index ea24fd346e..d9580fc285 100644 --- a/soh/src/overlays/actors/ovl_Boss_Fd/z_boss_fd.h +++ b/soh/src/overlays/actors/ovl_Boss_Fd/z_boss_fd.h @@ -49,6 +49,7 @@ typedef struct { /* 0x30 */ f32 scale; /* 0x34 */ f32 bFdFxFloat1; /* 0x38 */ f32 bFdFxFloat2; + u32 epoch; } BossFdEffect; // size = 0x3C #define BOSSFD_EFFECT_COUNT 180 diff --git a/soh/src/overlays/actors/ovl_Boss_Fd2/z_boss_fd2.c b/soh/src/overlays/actors/ovl_Boss_Fd2/z_boss_fd2.c index 20db1fb559..779584e32a 100644 --- a/soh/src/overlays/actors/ovl_Boss_Fd2/z_boss_fd2.c +++ b/soh/src/overlays/actors/ovl_Boss_Fd2/z_boss_fd2.c @@ -87,6 +87,7 @@ void BossFd2_SpawnDebris(GlobalContext* globalCtx, BossFdEffect* effect, Vec3f* effect->scale = scale / 1000.0f; effect->vFdFxRotX = Rand_ZeroFloat(100.0f); effect->vFdFxRotY = Rand_ZeroFloat(100.0f); + effect->epoch++; break; } } @@ -112,6 +113,7 @@ void BossFd2_SpawnFireBreath(GlobalContext* globalCtx, BossFdEffect* effect, Vec effect->timer2 = 0; effect->scale = scale / 400.0f; effect->kbAngle = kbAngle; + effect->epoch++; break; } } @@ -130,6 +132,7 @@ void BossFd2_SpawnEmber(GlobalContext* globalCtx, BossFdEffect* effect, Vec3f* p effect->scale = scale / 1000.0f; effect->alpha = 255; effect->timer1 = (s16)Rand_ZeroFloat(10.0f); + effect->epoch++; break; } } @@ -148,6 +151,7 @@ void BossFd2_SpawnSkullPiece(GlobalContext* globalCtx, BossFdEffect* effect, Vec effect->scale = scale / 1000.0f; effect->vFdFxRotX = Rand_ZeroFloat(100.0f); effect->vFdFxRotY = Rand_ZeroFloat(100.0f); + effect->epoch++; break; } } @@ -164,6 +168,7 @@ void BossFd2_SpawnDust(BossFdEffect* effect, Vec3f* position, Vec3f* velocity, V effect->accel = *acceleration; effect->timer2 = 0; effect->scale = scale / 400.0f; + effect->epoch++; break; } } diff --git a/soh/src/overlays/actors/ovl_Boss_Ganon/z_boss_ganon.c b/soh/src/overlays/actors/ovl_Boss_Ganon/z_boss_ganon.c index f7132bcafe..21199d4202 100644 --- a/soh/src/overlays/actors/ovl_Boss_Ganon/z_boss_ganon.c +++ b/soh/src/overlays/actors/ovl_Boss_Ganon/z_boss_ganon.c @@ -9,6 +9,8 @@ #include "assets/objects/object_ganon_anime2/object_ganon_anime2.h" #include "assets/scenes/dungeons/ganon_boss/ganon_boss_scene.h" +#include "soh/frame_interpolation.h" + #include #define FLAGS (ACTOR_FLAG_0 | ACTOR_FLAG_2 | ACTOR_FLAG_4 | ACTOR_FLAG_5) @@ -138,6 +140,7 @@ void BossGanonEff_SpawnWindowShard(GlobalContext* globalCtx, Vec3f* pos, Vec3f* eff->color.g = color->g; eff->color.b = color->b; eff->timer = (s16)Rand_ZeroFloat(20.0f); + eff->epoch++; break; } } @@ -158,6 +161,7 @@ void BossGanonEff_SpawnSparkle(GlobalContext* globalCtx, Vec3f* pos, Vec3f* velo eff->unk_2E = (s16)Rand_ZeroFloat(100.0f) + 0xC8; eff->unk_30 = arg6; eff->timer = (s16)Rand_ZeroFloat(10.0f); + eff->epoch++; break; } } @@ -182,6 +186,7 @@ void BossGanonEff_SpawnLightRay(GlobalContext* globalCtx, Vec3f* pos, Vec3f* vel eff->timer = (s16)Rand_ZeroFloat(10.0f); eff->unk_48 = Math_Atan2F(eff->velocity.z, eff->velocity.x); eff->unk_44 = -Math_Atan2F(sqrtf(SQXZ(eff->velocity)), eff->velocity.y); + eff->epoch++; break; } } @@ -201,6 +206,7 @@ void BossGanonEff_SpawnShock(GlobalContext* globalCtx, f32 scale, s16 shockType) eff->scale = scale / 1000.0f; eff->unk_2E = shockType; eff->timer = 0; + eff->epoch++; break; } } @@ -220,6 +226,7 @@ void BossGanonEff_SpawnLightning(GlobalContext* globalCtx, f32 scale, f32 arg2, eff->unk_48 = arg2; eff->unk_3C = arg3; eff->timer = 0; + eff->epoch++; break; } } @@ -240,6 +247,7 @@ void BossGanonEff_SpawnDustDark(GlobalContext* globalCtx, Vec3f* pos, f32 scale, eff->unk_38 = arg3; eff->unk_30 = (s16)Rand_ZeroFloat(100.0f); eff->unk_2E = eff->timer = eff->alpha = 0; + eff->epoch++; break; } } @@ -257,6 +265,7 @@ void BossGanonEff_SpawnDustLight(GlobalContext* globalCtx, Vec3f* pos, f32 scale effArr[bufIndex].unk_38 = arg3; effArr[bufIndex].unk_30 = Rand_ZeroFloat(100.0f); effArr[bufIndex].unk_2E = effArr[bufIndex].timer = effArr[bufIndex].alpha = 0; + effArr[bufIndex].epoch++; } void BossGanonEff_SpawnShockwave(GlobalContext* globalCtx, Vec3f* pos, f32 scale, f32 arg3) { @@ -275,6 +284,7 @@ void BossGanonEff_SpawnShockwave(GlobalContext* globalCtx, Vec3f* pos, f32 scale eff->unk_38 = arg3; eff->unk_30 = (s16)Rand_ZeroFloat(100.0f); eff->unk_2E = eff->timer = 0; + eff->epoch++; break; } } @@ -295,6 +305,7 @@ void BossGanonEff_SpawnBlackDot(GlobalContext* globalCtx, Vec3f* pos, f32 scale) eff->timer = 0; eff->alpha = 0; eff->unk_2E = 0; + eff->epoch++; break; } } @@ -4820,6 +4831,7 @@ void BossGanon_DrawEffects(GlobalContext* globalCtx) { for (i = 0; i < 200; i++, eff++) { if (eff->type == GDF_EFF_WINDOW_SHARD) { + FrameInterpolation_RecordOpenChild(eff, eff->epoch); gDPPipeSync(POLY_OPA_DISP++); if (flag == 0) { gSPDisplayList(POLY_OPA_DISP++, gDorfWindowShardMaterialDL); @@ -4837,6 +4849,7 @@ void BossGanon_DrawEffects(GlobalContext* globalCtx) { gSPMatrix(POLY_OPA_DISP++, Matrix_NewMtx(gfxCtx, "../z_boss_ganon.c", 10898), G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); gSPDisplayList(POLY_OPA_DISP++, gDorfWindowShardModelDL); + FrameInterpolation_RecordCloseChild(); } } @@ -4845,6 +4858,7 @@ void BossGanon_DrawEffects(GlobalContext* globalCtx) { for (i = 0; i < 150; i++, eff++) { if (eff->type == GDF_EFF_SPARKLE) { + FrameInterpolation_RecordOpenChild(eff, eff->epoch); gDPPipeSync(POLY_XLU_DISP++); if (flag == 0) { gDPSetEnvColor(POLY_XLU_DISP++, 255, 255, 0, 0); @@ -4859,6 +4873,7 @@ void BossGanon_DrawEffects(GlobalContext* globalCtx) { gSPMatrix(POLY_XLU_DISP++, Matrix_NewMtx(gfxCtx, "../z_boss_ganon.c", 10932), G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); gSPDisplayList(POLY_XLU_DISP++, gDorfSquareDL); + FrameInterpolation_RecordCloseChild(); } } @@ -4867,6 +4882,7 @@ void BossGanon_DrawEffects(GlobalContext* globalCtx) { for (i = 0; i < 150; i++, eff++) { if (eff->type == GDF_EFF_LIGHT_RAY) { + FrameInterpolation_RecordOpenChild(eff, eff->epoch); gDPPipeSync(POLY_XLU_DISP++); if (flag == 0) { gDPSetEnvColor(POLY_XLU_DISP++, 255, 255, 0, 0); @@ -4883,6 +4899,7 @@ void BossGanon_DrawEffects(GlobalContext* globalCtx) { gSPMatrix(POLY_XLU_DISP++, Matrix_NewMtx(gfxCtx, "../z_boss_ganon.c", 10971), G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); gSPDisplayList(POLY_XLU_DISP++, gDorfSquareDL); + FrameInterpolation_RecordCloseChild(); } } @@ -4891,6 +4908,7 @@ void BossGanon_DrawEffects(GlobalContext* globalCtx) { for (i = 0; i < 150; i++, eff++) { if (eff->type == GDF_EFF_SHOCK) { + FrameInterpolation_RecordOpenChild(eff, eff->epoch); if (flag == 0) { gDPPipeSync(POLY_XLU_DISP++); if (eff->unk_2E == GDF_SHOCK_PLAYER_PURPLE) { @@ -4909,6 +4927,7 @@ void BossGanon_DrawEffects(GlobalContext* globalCtx) { gSPMatrix(POLY_XLU_DISP++, Matrix_NewMtx(gfxCtx, "../z_boss_ganon.c", 11023), G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); gSPDisplayList(POLY_XLU_DISP++, gDorfShockDL); + FrameInterpolation_RecordCloseChild(); } } @@ -4916,6 +4935,7 @@ void BossGanon_DrawEffects(GlobalContext* globalCtx) { for (i = 0; i < 150; i++, eff++) { if (eff->type == GDF_EFF_LIGHTNING) { + FrameInterpolation_RecordOpenChild(eff, eff->epoch); gDPPipeSync(POLY_XLU_DISP++); gDPSetPrimColor(POLY_XLU_DISP++, 0, 0, sLightningPrimColors[(eff->timer * 3) + 0], sLightningPrimColors[(eff->timer * 3) + 1], sLightningPrimColors[(eff->timer * 3) + 2], @@ -4931,6 +4951,7 @@ void BossGanon_DrawEffects(GlobalContext* globalCtx) { G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); gSPSegment(POLY_XLU_DISP++, 0x08, SEGMENTED_TO_VIRTUAL(sLightningTextures[eff->timer])); gSPDisplayList(POLY_XLU_DISP++, gDorfLightningDL); + FrameInterpolation_RecordCloseChild(); } } @@ -4938,6 +4959,7 @@ void BossGanon_DrawEffects(GlobalContext* globalCtx) { for (i = 0; i < 150; i++, eff++) { if (eff->type == GDF_EFF_IMPACT_DUST_DARK) { + FrameInterpolation_RecordOpenChild(eff, eff->epoch); gDPPipeSync(POLY_XLU_DISP++); gDPSetPrimColor(POLY_XLU_DISP++, 0, 0, 0, 0, 0, eff->alpha); gDPSetEnvColor(POLY_XLU_DISP++, 100, 70, 0, 128); @@ -4949,6 +4971,7 @@ void BossGanon_DrawEffects(GlobalContext* globalCtx) { gSPMatrix(POLY_XLU_DISP++, Matrix_NewMtx(gfxCtx, "../z_boss_ganon.c", 11121), G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); gSPDisplayList(POLY_XLU_DISP++, gDorfImpactDarkDL); + FrameInterpolation_RecordCloseChild(); } } @@ -4956,6 +4979,7 @@ void BossGanon_DrawEffects(GlobalContext* globalCtx) { for (i = 0; i < 150; i++, eff++) { if (eff->type == GDF_EFF_IMPACT_DUST_LIGHT) { + FrameInterpolation_RecordOpenChild(eff, eff->epoch); gDPPipeSync(POLY_XLU_DISP++); gDPSetPrimColor(POLY_XLU_DISP++, 0, 0, 255, 255, 255, eff->alpha); gDPSetEnvColor(POLY_XLU_DISP++, 200, 100, 0, 128); @@ -4967,6 +4991,7 @@ void BossGanon_DrawEffects(GlobalContext* globalCtx) { gSPMatrix(POLY_XLU_DISP++, Matrix_NewMtx(gfxCtx, "../z_boss_ganon.c", 11165), G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); gSPDisplayList(POLY_XLU_DISP++, gDorfImpactLightDL); + FrameInterpolation_RecordCloseChild(); } } @@ -4974,6 +4999,7 @@ void BossGanon_DrawEffects(GlobalContext* globalCtx) { for (i = 0; i < 150; i++, eff++) { if (eff->type == GDF_EFF_SHOCKWAVE) { + FrameInterpolation_RecordOpenChild(eff, eff->epoch); gDPPipeSync(POLY_XLU_DISP++); gDPSetPrimColor(POLY_XLU_DISP++, 0, 0, 255, 255, 170, eff->alpha); gDPSetEnvColor(POLY_XLU_DISP++, 150, 255, 0, 128); @@ -4986,6 +5012,7 @@ void BossGanon_DrawEffects(GlobalContext* globalCtx) { gSPMatrix(POLY_XLU_DISP++, Matrix_NewMtx(gfxCtx, "../z_boss_ganon.c", 11209), G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); gSPDisplayList(POLY_XLU_DISP++, gDorfShockwaveDL); + FrameInterpolation_RecordCloseChild(); } } @@ -4993,6 +5020,7 @@ void BossGanon_DrawEffects(GlobalContext* globalCtx) { for (i = 0; i < 150; i++, eff++) { if (eff->type == GDF_EFF_BLACK_DOT) { + FrameInterpolation_RecordOpenChild(eff, eff->epoch); gDPPipeSync(POLY_XLU_DISP++); gDPSetPrimColor(POLY_XLU_DISP++, 0, 0, 150, 170, 0, eff->alpha); gDPSetEnvColor(POLY_XLU_DISP++, 255, 255, 255, 128); @@ -5005,6 +5033,7 @@ void BossGanon_DrawEffects(GlobalContext* globalCtx) { gSPMatrix(POLY_XLU_DISP++, Matrix_NewMtx(gfxCtx, "../z_boss_ganon.c", 11250), G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); gSPDisplayList(POLY_XLU_DISP++, gDorfDotDL); + FrameInterpolation_RecordCloseChild(); } } diff --git a/soh/src/overlays/actors/ovl_Boss_Ganon/z_boss_ganon.h b/soh/src/overlays/actors/ovl_Boss_Ganon/z_boss_ganon.h index 69a9837177..7a797ad741 100644 --- a/soh/src/overlays/actors/ovl_Boss_Ganon/z_boss_ganon.h +++ b/soh/src/overlays/actors/ovl_Boss_Ganon/z_boss_ganon.h @@ -69,6 +69,7 @@ typedef struct { /* 0x40 */ f32 unk_40; /* 0x44 */ f32 unk_44; // mostly x rot /* 0x48 */ f32 unk_48; // mostly y rot + u32 epoch; } GanondorfEffect; // size = 0x4C typedef struct BossGanon { diff --git a/soh/src/overlays/actors/ovl_Boss_Mo/z_boss_mo.c b/soh/src/overlays/actors/ovl_Boss_Mo/z_boss_mo.c index ec4899a287..ae436ca2d8 100644 --- a/soh/src/overlays/actors/ovl_Boss_Mo/z_boss_mo.c +++ b/soh/src/overlays/actors/ovl_Boss_Mo/z_boss_mo.c @@ -10,6 +10,8 @@ #include "objects/gameplay_keep/gameplay_keep.h" #include "vt.h" +#include "soh/frame_interpolation.h" + #include #define FLAGS (ACTOR_FLAG_0 | ACTOR_FLAG_2 | ACTOR_FLAG_4 | ACTOR_FLAG_5) @@ -34,6 +36,7 @@ typedef struct { /* 0x30 */ f32 scale; /* 0x30 */ f32 fwork[2]; /* 0x3C */ Vec3f* targetPos; + u32 epoch; } BossMoEffect; // size = 0x40 #define MO_FX_MAX_SIZE 0 @@ -211,6 +214,7 @@ void BossMo_SpawnRipple(BossMoEffect* effect, Vec3f* pos, f32 scale, f32 maxScal effect->rippleMode = 1; effect->fwork[MO_FX_SPREAD_RATE] = (effect->fwork[MO_FX_MAX_SIZE] - effect->scale) * 0.1f; } + effect->epoch++; break; } } @@ -232,6 +236,7 @@ void BossMo_SpawnDroplet(s16 type, BossMoEffect* effect, Vec3f* pos, Vec3f* vel, effect->scale = scale; effect->fwork[MO_FX_SPREAD_RATE] = 1.0f; effect->stopTimer = 0; + effect->epoch++; break; } } @@ -250,6 +255,7 @@ void BossMo_SpawnStillDroplet(BossMoEffect* effect, Vec3f* pos, f32 scale) { effect->accel = zeroVec; effect->scale = scale; effect->fwork[MO_FX_SPREAD_RATE] = 1.0f; + effect->epoch++; break; } } @@ -274,6 +280,7 @@ void BossMo_SpawnBubble(BossMoEffect* effect, Vec3f* pos, Vec3f* vel, Vec3f* acc effect->alpha = 0; } effect->timer = 0; + effect->epoch++; break; } } @@ -2909,6 +2916,7 @@ void BossMo_DrawEffects(BossMoEffect* effect, GlobalContext* globalCtx) { for (i = 0; i < ARRAY_COUNT(sEffects); i++, effect++) { if (effect->type == MO_FX_BIG_RIPPLE) { + FrameInterpolation_RecordOpenChild(effect, effect->epoch); if (flag == 0) { func_80094BC4(gfxCtx); @@ -2925,6 +2933,7 @@ void BossMo_DrawEffects(BossMoEffect* effect, GlobalContext* globalCtx) { G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); gSPDisplayList(POLY_XLU_DISP++, gEffWaterRippleDL); + FrameInterpolation_RecordCloseChild(); } } @@ -2932,6 +2941,7 @@ void BossMo_DrawEffects(BossMoEffect* effect, GlobalContext* globalCtx) { flag = 0; for (i = 0; i < ARRAY_COUNT(sEffects); i++, effect++) { if (effect->type == MO_FX_SMALL_RIPPLE) { + FrameInterpolation_RecordOpenChild(effect, effect->epoch); if (flag == 0) { func_80093D84(globalCtx->state.gfxCtx); @@ -2948,6 +2958,7 @@ void BossMo_DrawEffects(BossMoEffect* effect, GlobalContext* globalCtx) { G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); gSPDisplayList(POLY_XLU_DISP++, gEffShockwaveDL); + FrameInterpolation_RecordCloseChild(); } } @@ -2956,6 +2967,7 @@ void BossMo_DrawEffects(BossMoEffect* effect, GlobalContext* globalCtx) { for (i = 0; i < ARRAY_COUNT(sEffects); i++, effect++) { if (((effect->type == MO_FX_DROPLET) || (effect->type == MO_FX_SPLASH)) || (effect->type == MO_FX_SPLASH_TRAIL)) { + FrameInterpolation_RecordOpenChild(effect, effect->epoch); if (flag == 0) { POLY_XLU_DISP = Gfx_CallSetupDL(POLY_XLU_DISP, 0); @@ -2977,6 +2989,7 @@ void BossMo_DrawEffects(BossMoEffect* effect, GlobalContext* globalCtx) { G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); gSPDisplayList(POLY_XLU_DISP++, gMorphaDropletModelDL); + FrameInterpolation_RecordCloseChild(); } } @@ -2984,6 +2997,7 @@ void BossMo_DrawEffects(BossMoEffect* effect, GlobalContext* globalCtx) { flag = 0; for (i = 0; i < ARRAY_COUNT(sEffects); i++, effect++) { if (effect->type == MO_FX_WET_SPOT) { + FrameInterpolation_RecordOpenChild(effect, effect->epoch); if (flag == 0) { func_80094044(gfxCtx); @@ -3003,6 +3017,7 @@ void BossMo_DrawEffects(BossMoEffect* effect, GlobalContext* globalCtx) { G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); gSPDisplayList(POLY_XLU_DISP++, gMorphaWetSpotModelDL); + FrameInterpolation_RecordCloseChild(); } } @@ -3010,6 +3025,7 @@ void BossMo_DrawEffects(BossMoEffect* effect, GlobalContext* globalCtx) { flag = 0; for (i = 0; i < ARRAY_COUNT(sEffects); i++, effect++) { if (effect->type == MO_FX_BUBBLE) { + FrameInterpolation_RecordOpenChild(effect, effect->epoch); if (flag == 0) { func_80093D18(globalCtx->state.gfxCtx); @@ -3027,6 +3043,7 @@ void BossMo_DrawEffects(BossMoEffect* effect, GlobalContext* globalCtx) { G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); gSPDisplayList(POLY_OPA_DISP++, gMorphaBubbleDL); + FrameInterpolation_RecordCloseChild(); } } diff --git a/soh/src/overlays/actors/ovl_Boss_Va/z_boss_va.c b/soh/src/overlays/actors/ovl_Boss_Va/z_boss_va.c index c5b60a327d..3d6865e244 100644 --- a/soh/src/overlays/actors/ovl_Boss_Va/z_boss_va.c +++ b/soh/src/overlays/actors/ovl_Boss_Va/z_boss_va.c @@ -12,6 +12,8 @@ #include "overlays/actors/ovl_En_Boom/z_en_boom.h" #include "objects/gameplay_keep/gameplay_keep.h" +#include "soh/frame_interpolation.h" + #define FLAGS (ACTOR_FLAG_0 | ACTOR_FLAG_2 | ACTOR_FLAG_4 | ACTOR_FLAG_5) #define GET_BODY(this) ((BossVa*)(this)->actor.parent) @@ -40,6 +42,7 @@ typedef struct BossVaEffect { /* 0x44 */ f32 scaleMod; /* 0x48 */ Vec3f offset; /* 0x54 */ struct BossVa* parent; + u32 epoch; } BossVaEffect; // size = 0x58 typedef enum { @@ -3519,6 +3522,7 @@ void BossVa_DrawEffects(BossVaEffect* effect, GlobalContext* globalCtx) { for (i = 0; i < ARRAY_COUNT(sVaEffects); i++, effect++) { if (effect->type == VA_LARGE_SPARK) { + FrameInterpolation_RecordOpenChild(effect, effect->epoch); if (!flag) { func_80093D84(globalCtx->state.gfxCtx); gDPSetEnvColor(POLY_XLU_DISP++, 130, 130, 30, 0); @@ -3534,12 +3538,14 @@ void BossVa_DrawEffects(BossVaEffect* effect, GlobalContext* globalCtx) { gSPMatrix(POLY_XLU_DISP++, Matrix_NewMtx(gfxCtx, "../z_boss_va.c", 4976), G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); gSPDisplayList(POLY_XLU_DISP++, gBarinadeDL_015710); + FrameInterpolation_RecordCloseChild(); } } effect = effectHead; for (i = 0, flag = 0; i < ARRAY_COUNT(sVaEffects); i++, effect++) { if (effect->type == VA_SPARK_BALL) { + FrameInterpolation_RecordOpenChild(effect, effect->epoch); if (!flag) { func_80093D84(globalCtx->state.gfxCtx); gSPDisplayList(POLY_XLU_DISP++, gBarinadeDL_011738); @@ -3560,12 +3566,14 @@ void BossVa_DrawEffects(BossVaEffect* effect, GlobalContext* globalCtx) { gDPSetEnvColor(POLY_XLU_DISP++, effect->envColor[0], effect->envColor[1], effect->envColor[2], effect->envColor[3]); gSPDisplayList(POLY_XLU_DISP++, gBarinadeDL_011768); + FrameInterpolation_RecordCloseChild(); } } effect = effectHead; for (i = 0, flag = 0; i < ARRAY_COUNT(sVaEffects); i++, effect++) { if (effect->type == VA_BLOOD) { + FrameInterpolation_RecordOpenChild(effect, effect->epoch); if (!flag) { func_80093D84(globalCtx->state.gfxCtx); gSPDisplayList(POLY_XLU_DISP++, gBarinadeDL_009430); @@ -3590,6 +3598,7 @@ void BossVa_DrawEffects(BossVaEffect* effect, GlobalContext* globalCtx) { gSPMatrix(POLY_XLU_DISP++, Matrix_NewMtx(gfxCtx, "../z_boss_va.c", 5052), G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); gSPDisplayList(POLY_XLU_DISP++, gBarinadeDL_009468); + FrameInterpolation_RecordCloseChild(); } } @@ -3598,6 +3607,7 @@ void BossVa_DrawEffects(BossVaEffect* effect, GlobalContext* globalCtx) { if (effect->type == VA_TUMOR) { BossVa* parent = effect->parent; + FrameInterpolation_RecordOpenChild(effect, effect->epoch); if (!flag) { func_80093D18(globalCtx->state.gfxCtx); gDPSetEnvColor(POLY_OPA_DISP++, 0, 0, 0, effect->envColor[3]); @@ -3614,12 +3624,14 @@ void BossVa_DrawEffects(BossVaEffect* effect, GlobalContext* globalCtx) { G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); gSPDisplayList(POLY_OPA_DISP++, gBarinadeDL_012948); } + FrameInterpolation_RecordCloseChild(); } } effect = effectHead; for (i = 0, flag = 0; i < ARRAY_COUNT(sVaEffects); i++, effect++) { if (effect->type == VA_GORE) { + FrameInterpolation_RecordOpenChild(effect, effect->epoch); if (!flag) { func_80093D18(globalCtx->state.gfxCtx); gSPDisplayList(POLY_OPA_DISP++, gBarinadeDL_012BA0); @@ -3645,12 +3657,14 @@ void BossVa_DrawEffects(BossVaEffect* effect, GlobalContext* globalCtx) { gSPMatrix(POLY_OPA_DISP++, Matrix_NewMtx(gfxCtx, "../z_boss_va.c", 5124), G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); gSPDisplayList(POLY_OPA_DISP++, gBarinadeDL_012C50); + FrameInterpolation_RecordCloseChild(); } } effect = effectHead; for (i = 0, flag = 0; i < ARRAY_COUNT(sVaEffects); i++, effect++) { if (effect->type == VA_ZAP_CHARGE) { + FrameInterpolation_RecordOpenChild(effect, effect->epoch); if (!flag) { func_80093D84(globalCtx->state.gfxCtx); gSPDisplayList(POLY_XLU_DISP++, gBarinadeDL_0135B0); @@ -3668,12 +3682,14 @@ void BossVa_DrawEffects(BossVaEffect* effect, GlobalContext* globalCtx) { gSPMatrix(POLY_XLU_DISP++, Matrix_NewMtx(gfxCtx, "../z_boss_va.c", 5152), G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); gSPDisplayList(POLY_XLU_DISP++, gBarinadeDL_013638); + FrameInterpolation_RecordCloseChild(); } } effect = effectHead; for (i = 0, flag = 0; i < ARRAY_COUNT(sVaEffects); i++, effect++) { if (effect->type == VA_BLAST_SPARK) { + FrameInterpolation_RecordOpenChild(effect, effect->epoch); if (!flag) { func_80093C14(globalCtx->state.gfxCtx); gDPSetEnvColor(POLY_XLU_DISP++, 130, 130, 30, 0); @@ -3690,12 +3706,14 @@ void BossVa_DrawEffects(BossVaEffect* effect, GlobalContext* globalCtx) { gSPMatrix(POLY_XLU_DISP++, Matrix_NewMtx(gfxCtx, "../z_boss_va.c", 5180), G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); gSPDisplayList(POLY_XLU_DISP++, gBarinadeDL_015710); + FrameInterpolation_RecordCloseChild(); } } effect = effectHead; for (i = 0, flag = 0; i < ARRAY_COUNT(sVaEffects); i++, effect++) { if (effect->type == VA_SMALL_SPARK) { + FrameInterpolation_RecordOpenChild(effect, effect->epoch); if (!flag) { func_80093D84(globalCtx->state.gfxCtx); gDPSetEnvColor(POLY_XLU_DISP++, 255, 255, 100, 0); @@ -3712,6 +3730,7 @@ void BossVa_DrawEffects(BossVaEffect* effect, GlobalContext* globalCtx) { gSPMatrix(POLY_XLU_DISP++, Matrix_NewMtx(gfxCtx, "../z_boss_va.c", 5208), G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); gSPDisplayList(POLY_XLU_DISP++, gBarinadeDL_008F70); + FrameInterpolation_RecordCloseChild(); } } @@ -3734,6 +3753,7 @@ void BossVa_SpawnSpark(GlobalContext* globalCtx, BossVaEffect* effect, BossVa* t effect->timer = (s16)(Rand_ZeroOne() * 10.0f) + 111; effect->velocity = effect->accel = sZeroVec; effect->mode = mode; + effect->epoch++; switch (mode) { case SPARK_UNUSED: 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 9e9e8f9888..bc059daadd 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 @@ -427,7 +427,8 @@ void EnFloormas_SetupFreeze(EnFloormas* this) { } void EnFloormas_Die(EnFloormas* this, GlobalContext* globalCtx) { - if (this->actor.scale.x > 0.004f) { + //Originally was doing > 0.004f, better fix thanks Gary :D + if (this->actor.scale.x > (f32)0.004f) { // split this->actor.shape.rot.y = this->actor.yawTowardsPlayer + 0x8000; EnFloormas_SetupSplit((EnFloormas*)this->actor.child); 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 b557774842..ac50f226a6 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 @@ -7,6 +7,8 @@ #include "z_en_zo.h" #include "objects/object_zo/object_zo.h" +#include "soh/frame_interpolation.h" + #define FLAGS (ACTOR_FLAG_0 | ACTOR_FLAG_3) typedef enum { @@ -41,6 +43,7 @@ void EnZo_Ripple(EnZo* this, Vec3f* pos, f32 scale, f32 targetScale, u8 alpha) { effect->scale = scale; effect->targetScale = targetScale; effect->color.a = alpha; + effect->epoch++; break; } effect++; @@ -65,6 +68,7 @@ void EnZo_Bubble(EnZo* this, Vec3f* pos) { effect->vec = *pos; effect->vel = vel; effect->scale = ((Rand_ZeroOne() - 0.5f) * 0.02f) + 0.12f; + effect->epoch++; break; } } @@ -87,6 +91,7 @@ void EnZo_Splash(EnZo* this, Vec3f* pos, Vec3f* vel, f32 scale) { effect->vel = *vel; effect->color.a = (Rand_ZeroOne() * 100.0f) + 100.0f; effect->scale = scale; + effect->epoch++; break; } effect++; @@ -180,6 +185,7 @@ void EnZo_DrawRipples(EnZo* this, GlobalContext* globalCtx) { func_80093D84(globalCtx->state.gfxCtx); for (i = 0; i < ARRAY_COUNT(this->effects); i++) { if (effect->type == ENZO_EFFECT_RIPPLE) { + FrameInterpolation_RecordOpenChild(effect, effect->epoch); if (!setup) { if (1) {} gDPPipeSync(POLY_XLU_DISP++); @@ -194,6 +200,7 @@ void EnZo_DrawRipples(EnZo* this, GlobalContext* globalCtx) { gSPMatrix(POLY_XLU_DISP++, Matrix_NewMtx(globalCtx->state.gfxCtx, "../z_en_zo_eff.c", 242), G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); gSPDisplayList(POLY_XLU_DISP++, gZoraRipplesModelDL); + FrameInterpolation_RecordCloseChild(); } effect++; } @@ -210,6 +217,7 @@ void EnZo_DrawBubbles(EnZo* this, GlobalContext* globalCtx) { func_80093D84(globalCtx->state.gfxCtx); for (i = 0; i < ARRAY_COUNT(this->effects); i++) { if (effect->type == ENZO_EFFECT_BUBBLE) { + FrameInterpolation_RecordOpenChild(effect, effect->epoch); if (!setup) { if (1) {} gSPDisplayList(POLY_XLU_DISP++, gZoraBubblesMaterialDL); @@ -227,6 +235,7 @@ void EnZo_DrawBubbles(EnZo* this, GlobalContext* globalCtx) { gSPMatrix(POLY_XLU_DISP++, Matrix_NewMtx(globalCtx->state.gfxCtx, "../z_en_zo_eff.c", 281), G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); gSPDisplayList(POLY_XLU_DISP++, gZoraBubblesModelDL); + FrameInterpolation_RecordCloseChild(); } effect++; } @@ -244,6 +253,7 @@ void EnZo_DrawSplashes(EnZo* this, GlobalContext* globalCtx) { func_80093D84(globalCtx->state.gfxCtx); for (i = 0; i < ARRAY_COUNT(this->effects); i++) { if (effect->type == ENZO_EFFECT_SPLASH) { + FrameInterpolation_RecordOpenChild(effect, effect->epoch); if (!setup) { if (1) {} gSPDisplayList(POLY_XLU_DISP++, gZoraSplashesMaterialDL); @@ -260,6 +270,7 @@ void EnZo_DrawSplashes(EnZo* this, GlobalContext* globalCtx) { G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); gSPDisplayList(POLY_XLU_DISP++, gZoraSplashesModelDL); + FrameInterpolation_RecordCloseChild(); } effect++; } diff --git a/soh/src/overlays/actors/ovl_En_Zo/z_en_zo.h b/soh/src/overlays/actors/ovl_En_Zo/z_en_zo.h index ba6eebb30c..8911ce3990 100644 --- a/soh/src/overlays/actors/ovl_En_Zo/z_en_zo.h +++ b/soh/src/overlays/actors/ovl_En_Zo/z_en_zo.h @@ -15,6 +15,7 @@ typedef struct { /* 0x14 */ Vec3f pos; /* 0x20 */ Vec3f vel; /* 0x2C */ Vec3f vec; // Usage specific + u32 epoch; } EnZoEffect; // size = 0x38 typedef void (*EnZoActionFunc)(struct EnZo*, GlobalContext*); diff --git a/soh/src/overlays/actors/ovl_Fishing/z_fishing.c b/soh/src/overlays/actors/ovl_Fishing/z_fishing.c index a103c48261..0798145db9 100644 --- a/soh/src/overlays/actors/ovl_Fishing/z_fishing.c +++ b/soh/src/overlays/actors/ovl_Fishing/z_fishing.c @@ -10,6 +10,8 @@ #include "objects/object_fish/object_fish.h" #include "vt.h" +#include "soh/frame_interpolation.h" + #define FLAGS ACTOR_FLAG_4 #define WATER_SURFACE_Y(globalCtx) globalCtx->colCtx.colHeader->waterBoxes->ySurface @@ -56,6 +58,7 @@ typedef struct { /* 0x34 */ f32 unk_34; /* 0x38 */ f32 unk_38; /* 0x3C */ f32 unk_3C; + u32 epoch; } FishingEffect; // size = 0x40 #define POND_PROP_COUNT 140 @@ -490,6 +493,8 @@ void Fishing_SpawnRipple(Vec3f* projectedPos, FishingEffect* effect, Vec3f* pos, effect->unk_2C = 1; effect->unk_38 = (effect->unk_34 - effect->unk_30) * 0.1f; } + + effect->epoch++; break; } @@ -514,6 +519,7 @@ void Fishing_SpawnDustSplash(Vec3f* projectedPos, FishingEffect* effect, Vec3f* effect->accel = accel; effect->alpha = 100 + (s16)Rand_ZeroFloat(100.0f); effect->unk_30 = scale; + effect->epoch++; break; } @@ -539,6 +545,7 @@ void Fishing_SpawnWaterDust(Vec3f* projectedPos, FishingEffect* effect, Vec3f* p effect->timer = (s16)Rand_ZeroFloat(100.0f); effect->unk_30 = scale; effect->unk_34 = 2.0f * scale; + effect->epoch++; break; } @@ -563,6 +570,7 @@ void Fishing_SpawnBubble(Vec3f* projectedPos, FishingEffect* effect, Vec3f* pos, effect->timer = (s16)Rand_ZeroFloat(100.0f); effect->unk_30 = scale; effect->unk_2C = arg4; + effect->epoch++; break; } @@ -591,6 +599,7 @@ void Fishing_SpawnRainDrop(FishingEffect* effect, Vec3f* pos, Vec3f* rot) { Matrix_RotateY(rot->y, MTXMODE_NEW); Matrix_RotateX(rot->x, MTXMODE_APPLY); Matrix_MultVec3f(&velSrc, &effect->vel); + effect->epoch++; break; } @@ -1182,6 +1191,7 @@ void Fishing_DrawEffects(FishingEffect* effect, GlobalContext* globalCtx) { for (i = 0; i < 100; i++) { if (effect->type == FS_EFF_RIPPLE) { + FrameInterpolation_RecordOpenChild(effect, effect->epoch); if (flag == 0) { gSPDisplayList(POLY_XLU_DISP++, gFishingRippleMaterialDL); gDPSetEnvColor(POLY_XLU_DISP++, 155, 155, 155, 0); @@ -1197,6 +1207,7 @@ void Fishing_DrawEffects(FishingEffect* effect, GlobalContext* globalCtx) { G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); gSPDisplayList(POLY_XLU_DISP++, gFishingRippleModelDL); + FrameInterpolation_RecordCloseChild(); } effect++; } @@ -1205,6 +1216,7 @@ void Fishing_DrawEffects(FishingEffect* effect, GlobalContext* globalCtx) { flag = 0; for (i = 0; i < 100; i++) { if (effect->type == FS_EFF_DUST_SPLASH) { + FrameInterpolation_RecordOpenChild(effect, effect->epoch); if (flag == 0) { gSPDisplayList(POLY_XLU_DISP++, gFishingDustSplashMaterialDL); gDPSetEnvColor(POLY_XLU_DISP++, 200, 200, 200, 0); @@ -1221,6 +1233,7 @@ void Fishing_DrawEffects(FishingEffect* effect, GlobalContext* globalCtx) { G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); gSPDisplayList(POLY_XLU_DISP++, gFishingDustSplashModelDL); + FrameInterpolation_RecordCloseChild(); } effect++; } @@ -1229,6 +1242,7 @@ void Fishing_DrawEffects(FishingEffect* effect, GlobalContext* globalCtx) { flag = 0; for (i = 0; i < 100; i++) { if (effect->type == FS_EFF_WATER_DUST) { + FrameInterpolation_RecordOpenChild(effect, effect->epoch); if (flag == 0) { gSPDisplayList(POLY_OPA_DISP++, gFishingWaterDustMaterialDL); gDPSetEnvColor(POLY_OPA_DISP++, 40, 90, 80, 128); @@ -1249,6 +1263,7 @@ void Fishing_DrawEffects(FishingEffect* effect, GlobalContext* globalCtx) { G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); gSPDisplayList(POLY_OPA_DISP++, gFishingWaterDustModelDL); + FrameInterpolation_RecordCloseChild(); } effect++; } @@ -1257,6 +1272,7 @@ void Fishing_DrawEffects(FishingEffect* effect, GlobalContext* globalCtx) { flag = 0; for (i = 0; i < 100; i++) { if (effect->type == FS_EFF_BUBBLE) { + FrameInterpolation_RecordOpenChild(effect, effect->epoch); if (flag == 0) { gSPDisplayList(POLY_XLU_DISP++, gFishingBubbleMaterialDL); gDPSetEnvColor(POLY_XLU_DISP++, 150, 150, 150, 0); @@ -1272,6 +1288,7 @@ void Fishing_DrawEffects(FishingEffect* effect, GlobalContext* globalCtx) { G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); gSPDisplayList(POLY_XLU_DISP++, gFishingBubbleModelDL); + FrameInterpolation_RecordCloseChild(); } effect++; } @@ -1280,6 +1297,7 @@ void Fishing_DrawEffects(FishingEffect* effect, GlobalContext* globalCtx) { flag = 0; for (i = 30; i < EFFECT_COUNT; i++) { if (effect->type == FS_EFF_RAIN_DROP) { + FrameInterpolation_RecordOpenChild(effect, effect->epoch); if (flag == 0) { POLY_XLU_DISP = Gfx_CallSetupDL(POLY_XLU_DISP, 0x14); gDPSetCombineMode(POLY_XLU_DISP++, G_CC_PRIMITIVE, G_CC_PRIMITIVE); @@ -1297,6 +1315,7 @@ void Fishing_DrawEffects(FishingEffect* effect, GlobalContext* globalCtx) { G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); gSPDisplayList(POLY_XLU_DISP++, gFishingRainDropModelDL); + FrameInterpolation_RecordCloseChild(); } effect++; } @@ -1307,6 +1326,7 @@ void Fishing_DrawEffects(FishingEffect* effect, GlobalContext* globalCtx) { flag = 0; for (i = 30; i < EFFECT_COUNT; i++) { if (effect->type == FS_EFF_RAIN_RIPPLE) { + FrameInterpolation_RecordOpenChild(effect, effect->epoch); if (flag == 0) { gSPDisplayList(POLY_XLU_DISP++, gFishingRippleMaterialDL); gDPSetEnvColor(POLY_XLU_DISP++, 155, 155, 155, 0); @@ -1321,6 +1341,7 @@ void Fishing_DrawEffects(FishingEffect* effect, GlobalContext* globalCtx) { G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); gSPDisplayList(POLY_XLU_DISP++, gFishingRippleModelDL); + FrameInterpolation_RecordCloseChild(); } effect++; } @@ -1329,6 +1350,7 @@ void Fishing_DrawEffects(FishingEffect* effect, GlobalContext* globalCtx) { flag = 0; for (i = 30; i < EFFECT_COUNT; i++) { if (effect->type == FS_EFF_RAIN_SPLASH) { + FrameInterpolation_RecordOpenChild(effect, effect->epoch); if (flag == 0) { gSPDisplayList(POLY_XLU_DISP++, gFishingRainSplashMaterialDL); gDPSetPrimColor(POLY_XLU_DISP++, 0, 0, 255, 255, 255, KREG(19) + 80); @@ -1350,12 +1372,14 @@ void Fishing_DrawEffects(FishingEffect* effect, GlobalContext* globalCtx) { G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); gSPDisplayList(POLY_XLU_DISP++, gFishingRainSplashModelDL); + FrameInterpolation_RecordCloseChild(); } effect++; } effect = firstEffect; if (effect->type == FS_EFF_OWNER_HAT) { + FrameInterpolation_RecordOpenChild(effect, effect->epoch); Matrix_Translate(effect->pos.x, effect->pos.y, effect->pos.z, MTXMODE_NEW); Matrix_RotateY((sEffOwnerHatRot.y * M_PI) / 32768, MTXMODE_APPLY); Matrix_RotateX((sEffOwnerHatRot.x * M_PI) / 32768, MTXMODE_APPLY); @@ -1367,6 +1391,7 @@ void Fishing_DrawEffects(FishingEffect* effect, GlobalContext* globalCtx) { G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); gSPDisplayList(POLY_OPA_DISP++, gFishingOwnerHatDL); + FrameInterpolation_RecordCloseChild(); } Matrix_Pop(); @@ -4398,6 +4423,7 @@ void Fishing_DrawPondProps(GlobalContext* globalCtx) { } if (prop->shouldDraw) { + FrameInterpolation_RecordOpenChild(prop, 0); Matrix_Translate(prop->pos.x, prop->pos.y, prop->pos.z, MTXMODE_NEW); Matrix_Scale(prop->scale, prop->scale, prop->scale, MTXMODE_APPLY); Matrix_RotateY(prop->rotY, MTXMODE_APPLY); @@ -4407,6 +4433,7 @@ void Fishing_DrawPondProps(GlobalContext* globalCtx) { gSPMatrix(POLY_XLU_DISP++, Matrix_NewMtx(globalCtx->state.gfxCtx, "../z_fishing.c", 7726), G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); gSPDisplayList(POLY_XLU_DISP++, gFishingReedModelDL); + FrameInterpolation_RecordCloseChild(); } } @@ -4423,12 +4450,14 @@ void Fishing_DrawPondProps(GlobalContext* globalCtx) { } if (prop->shouldDraw) { + FrameInterpolation_RecordOpenChild(prop, 0); Matrix_Translate(prop->pos.x, prop->pos.y, prop->pos.z, MTXMODE_NEW); Matrix_Scale(prop->scale, prop->scale, prop->scale, MTXMODE_APPLY); gSPMatrix(POLY_OPA_DISP++, Matrix_NewMtx(globalCtx->state.gfxCtx, "../z_fishing.c", 7748), G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); gSPDisplayList(POLY_OPA_DISP++, gFishingWoodPostModelDL); + FrameInterpolation_RecordCloseChild(); } } @@ -4445,6 +4474,7 @@ void Fishing_DrawPondProps(GlobalContext* globalCtx) { } if (prop->shouldDraw) { + FrameInterpolation_RecordOpenChild(prop, 0); Matrix_Translate(prop->pos.x, prop->pos.y, prop->pos.z, MTXMODE_NEW); Matrix_Scale(prop->scale, 1.0f, prop->scale, MTXMODE_APPLY); Matrix_RotateY(prop->lilyPadAngle * (M_PI / 32768), MTXMODE_APPLY); @@ -4454,6 +4484,7 @@ void Fishing_DrawPondProps(GlobalContext* globalCtx) { gSPMatrix(POLY_XLU_DISP++, Matrix_NewMtx(globalCtx->state.gfxCtx, "../z_fishing.c", 7774), G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); gSPDisplayList(POLY_XLU_DISP++, gFishingLilyPadModelDL); + FrameInterpolation_RecordCloseChild(); } } @@ -4470,6 +4501,7 @@ void Fishing_DrawPondProps(GlobalContext* globalCtx) { } if (prop->shouldDraw) { + FrameInterpolation_RecordOpenChild(prop, 0); Matrix_Translate(prop->pos.x, prop->pos.y, prop->pos.z, MTXMODE_NEW); Matrix_Scale(prop->scale, prop->scale, prop->scale, MTXMODE_APPLY); Matrix_RotateY(prop->rotY, MTXMODE_APPLY); @@ -4477,6 +4509,7 @@ void Fishing_DrawPondProps(GlobalContext* globalCtx) { gSPMatrix(POLY_OPA_DISP++, Matrix_NewMtx(globalCtx->state.gfxCtx, "../z_fishing.c", 7798), G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); gSPDisplayList(POLY_OPA_DISP++, gFishingRockModelDL); + FrameInterpolation_RecordCloseChild(); } } diff --git a/soh/src/overlays/actors/ovl_Object_Kankyo/z_object_kankyo.c b/soh/src/overlays/actors/ovl_Object_Kankyo/z_object_kankyo.c index b01bcf1d95..d4282b91b2 100644 --- a/soh/src/overlays/actors/ovl_Object_Kankyo/z_object_kankyo.c +++ b/soh/src/overlays/actors/ovl_Object_Kankyo/z_object_kankyo.c @@ -9,6 +9,8 @@ #include "objects/gameplay_keep/gameplay_keep.h" #include "objects/object_spot02_objects/object_spot02_objects.h" +#include "soh/frame_interpolation.h" + #define FLAGS (ACTOR_FLAG_4 | ACTOR_FLAG_5 | ACTOR_FLAG_25) void ObjectKankyo_Init(Actor* thisx, GlobalContext* globalCtx); @@ -260,6 +262,7 @@ void ObjectKankyo_Fairies(ObjectKankyo* this, GlobalContext* globalCtx) { this->effects[i].dirPhase.z = Rand_ZeroOne() * 360.0f; this->effects[i].state++; this->effects[i].timer = 0; + this->effects[i].epoch++; break; case 1: // blinking fairies / inactive fairy trails @@ -417,26 +420,32 @@ void ObjectKankyo_Fairies(ObjectKankyo* this, GlobalContext* globalCtx) { if (this->effects[i].base.x + this->effects[i].pos.x - baseX > maxDist) { this->effects[i].base.x = baseX - maxDist; this->effects[i].pos.x = 0.0f; + this->effects[i].epoch++; } if (this->effects[i].base.x + this->effects[i].pos.x - baseX < -maxDist) { this->effects[i].base.x = baseX + maxDist; this->effects[i].pos.x = 0.0f; + this->effects[i].epoch++; } if (this->effects[i].base.y + this->effects[i].pos.y - baseY > 50.0f) { this->effects[i].base.y = baseY - 50.0f; this->effects[i].pos.y = 0.0f; + this->effects[i].epoch++; } if (this->effects[i].base.y + this->effects[i].pos.y - baseY < -50.0f) { this->effects[i].base.y = baseY + 50.0f; this->effects[i].pos.y = 0.0f; + this->effects[i].epoch++; } if (this->effects[i].base.z + this->effects[i].pos.z - baseZ > maxDist) { this->effects[i].base.z = baseZ - maxDist; this->effects[i].pos.z = 0.0f; + this->effects[i].epoch++; } if (this->effects[i].base.z + this->effects[i].pos.z - baseZ < -maxDist) { this->effects[i].base.z = baseZ + maxDist; this->effects[i].pos.z = 0.0f; + this->effects[i].epoch++; } } } @@ -496,6 +505,7 @@ void ObjectKankyo_DrawFairies(ObjectKankyo* this2, GlobalContext* globalCtx2) { gSPDisplayList(POLY_XLU_DISP++, gKokiriDustMoteTextureLoadDL); for (i = 0; i < globalCtx->envCtx.unk_EE[3]; i++) { + FrameInterpolation_RecordOpenChild(&this->effects[i], this->effects[i].epoch); Matrix_Translate(this->effects[i].base.x + this->effects[i].pos.x, this->effects[i].base.y + this->effects[i].pos.y, this->effects[i].base.z + this->effects[i].pos.z, MTXMODE_NEW); @@ -561,6 +571,7 @@ void ObjectKankyo_DrawFairies(ObjectKankyo* this2, GlobalContext* globalCtx2) { Matrix_RotateZ(DEG_TO_RAD(globalCtx->state.frames * 20.0f), MTXMODE_APPLY); gSPMatrix(POLY_XLU_DISP++, Matrix_NewMtx(globalCtx->state.gfxCtx, "../z_object_kankyo.c", 913), G_MTX_LOAD); gSPDisplayList(POLY_XLU_DISP++, gKokiriDustMoteDL); + FrameInterpolation_RecordCloseChild(); } CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_object_kankyo.c", 922); } diff --git a/soh/src/overlays/actors/ovl_Object_Kankyo/z_object_kankyo.h b/soh/src/overlays/actors/ovl_Object_Kankyo/z_object_kankyo.h index 9b9a699753..fc45e156ed 100644 --- a/soh/src/overlays/actors/ovl_Object_Kankyo/z_object_kankyo.h +++ b/soh/src/overlays/actors/ovl_Object_Kankyo/z_object_kankyo.h @@ -24,6 +24,7 @@ typedef struct ObjectKankyoEffect { /* 0x4A */ u16 flightRadius; /* 0x4C */ f32 amplitude; /* 0x50 */ u16 timer; + u32 epoch; } ObjectKankyoEffect; // size = 0x54 typedef struct ObjectKankyo { 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 49ad4e6e48..733bdee2db 100644 --- a/soh/src/overlays/actors/ovl_player_actor/z_player.c +++ b/soh/src/overlays/actors/ovl_player_actor/z_player.c @@ -898,6 +898,15 @@ static s8 sItemActionParams[] = { PLAYER_AP_SWORD_KOKIRI, PLAYER_AP_SWORD_MASTER, PLAYER_AP_SWORD_BGS, + PLAYER_AP_SHIELD_DEKU, + PLAYER_AP_SHIELD_HYLIAN, + PLAYER_AP_SHIELD_MIRROR, + PLAYER_AP_TUNIC_KOKIRI, + PLAYER_AP_TUNIC_GORON, + PLAYER_AP_TUNIC_ZORA, + PLAYER_AP_BOOTS_KOKIRI, + PLAYER_AP_BOOTS_IRON, + PLAYER_AP_BOOTS_HOVER, }; static s32(*D_80853EDC[])(Player* this, GlobalContext* globalCtx) = { @@ -910,7 +919,8 @@ static s32(*D_80853EDC[])(Player* this, GlobalContext* globalCtx) = { func_8083485C, func_8083485C, func_8083485C, func_8083485C, func_8083485C, func_8083485C, func_8083485C, func_8083485C, func_8083485C, func_8083485C, func_8083485C, func_8083485C, func_8083485C, func_8083485C, func_8083485C, func_8083485C, func_8083485C, func_8083485C, func_8083485C, func_8083485C, func_8083485C, - func_8083485C, func_8083485C, func_8083485C, func_8083485C, + func_8083485C, func_8083485C, func_8083485C, func_8083485C, func_8083485C, func_8083485C, func_8083485C, + func_8083485C, func_8083485C, func_8083485C, func_8083485C, func_8083485C, func_8083485C, }; static void (*D_80853FE8[])(GlobalContext* globalCtx, Player* this) = { @@ -923,7 +933,8 @@ static void (*D_80853FE8[])(GlobalContext* globalCtx, Player* this) = { func_80833770, func_80833770, func_80833770, func_80833770, func_80833770, func_80833770, func_80833770, func_80833770, func_80833770, func_80833770, func_80833770, func_80833770, func_80833770, func_80833770, func_80833770, func_80833770, func_80833770, func_80833770, func_80833770, func_80833770, func_80833770, - func_80833770, func_80833770, func_80833770, func_80833770, + func_80833770, func_80833770, func_80833770, func_80833770, func_80833770, func_80833770, func_80833770, + func_80833770, func_80833770, func_80833770, func_80833770, func_80833770, func_80833770, }; typedef enum { @@ -2752,7 +2763,8 @@ void func_80835F44(GlobalContext* globalCtx, Player* this, s32 item) { if ((actionParam == PLAYER_AP_NONE) || !(this->stateFlags1 & PLAYER_STATE1_27) || ((this->actor.bgCheckFlags & 1) && - ((actionParam == PLAYER_AP_HOOKSHOT) || (actionParam == PLAYER_AP_LONGSHOT)))) { + ((actionParam == PLAYER_AP_HOOKSHOT) || (actionParam == PLAYER_AP_LONGSHOT))) || + ((actionParam >= PLAYER_AP_SHIELD_DEKU) && (actionParam <= PLAYER_AP_BOOTS_HOVER))) { if ((globalCtx->bombchuBowlingStatus == 0) && (((actionParam == PLAYER_AP_STICK) && (AMMO(ITEM_STICK) == 0)) || @@ -2764,6 +2776,33 @@ void func_80835F44(GlobalContext* globalCtx, Player* this, s32 item) { return; } + if (actionParam >= PLAYER_AP_BOOTS_KOKIRI) { + u16 bootsValue = actionParam - PLAYER_AP_BOOTS_KOKIRI + 1; + if (CUR_EQUIP_VALUE(EQUIP_BOOTS) == bootsValue) { + Inventory_ChangeEquipment(EQUIP_BOOTS, 1); + } else { + Inventory_ChangeEquipment(EQUIP_BOOTS, bootsValue); + } + Player_SetEquipmentData(globalCtx, this); + return; + } + + if (actionParam >= PLAYER_AP_TUNIC_KOKIRI) { + u16 tunicValue = actionParam - PLAYER_AP_TUNIC_KOKIRI + 1; + if (CUR_EQUIP_VALUE(EQUIP_TUNIC) == tunicValue) { + Inventory_ChangeEquipment(EQUIP_TUNIC, 1); + } else { + Inventory_ChangeEquipment(EQUIP_TUNIC, tunicValue); + } + Player_SetEquipmentData(globalCtx, this); + return; + } + + if (actionParam >= PLAYER_AP_SHIELD_DEKU) { + // Changing shields through action commands is unimplemented + return; + } + if (actionParam == PLAYER_AP_LENS) { if (func_80087708(globalCtx, 0, 3)) { if (globalCtx->actorCtx.unk_03 != 0) { @@ -6981,7 +7020,7 @@ void func_808409CC(GlobalContext* globalCtx, Player* this) { if (sp34 < 4) { if (((sp34 != 0) && (sp34 != 3)) || ((this->rightHandType == PLAYER_MODELTYPE_RH_SHIELD) && ((sp34 == 3) || Player_GetSwordHeld(this)))) { - if ((sp34 == 0) && Player_HoldsTwoHandedWeapon(this)) { + if ((sp34 == 1) && Player_HoldsTwoHandedWeapon(this) && CVar_GetS32("gTwoHandedIdle", 1) == 1) { sp34 = 4; } sp38 = sp34 + 9; diff --git a/soh/src/overlays/misc/ovl_kaleido_scope/z_kaleido_equipment.c b/soh/src/overlays/misc/ovl_kaleido_scope/z_kaleido_equipment.c index 27575c3b6b..719eaa3f33 100644 --- a/soh/src/overlays/misc/ovl_kaleido_scope/z_kaleido_equipment.c +++ b/soh/src/overlays/misc/ovl_kaleido_scope/z_kaleido_equipment.c @@ -471,39 +471,76 @@ void KaleidoScope_DrawEquipment(GlobalContext* globalCtx) { KaleidoScope_SetCursorVtx(pauseCtx, cursorSlot * 4, pauseCtx->equipVtx); if ((pauseCtx->cursorSpecialPos == 0) && (cursorItem != PAUSE_ITEM_NONE) && (pauseCtx->state == 6) && - (pauseCtx->unk_1E4 == 0) && CHECK_BTN_ALL(input->press.button, BTN_A) && + (pauseCtx->unk_1E4 == 0) && + CHECK_BTN_ANY(input->press.button, BTN_A | BTN_CLEFT | BTN_CDOWN | BTN_CRIGHT) && (pauseCtx->cursorX[PAUSE_EQUIP] != 0)) { if ((gEquipAgeReqs[pauseCtx->cursorY[PAUSE_EQUIP]][pauseCtx->cursorX[PAUSE_EQUIP]] == 9) || (gEquipAgeReqs[pauseCtx->cursorY[PAUSE_EQUIP]][pauseCtx->cursorX[PAUSE_EQUIP]] == ((void)0, gSaveContext.linkAge))) { - Inventory_ChangeEquipment(pauseCtx->cursorY[PAUSE_EQUIP], pauseCtx->cursorX[PAUSE_EQUIP]); + if (CHECK_BTN_ALL(input->press.button, BTN_A)) { + Inventory_ChangeEquipment(pauseCtx->cursorY[PAUSE_EQUIP], pauseCtx->cursorX[PAUSE_EQUIP]); - if (pauseCtx->cursorY[PAUSE_EQUIP] == 0) { - gSaveContext.infTable[29] = 0; - gSaveContext.equips.buttonItems[0] = cursorItem; + if (pauseCtx->cursorY[PAUSE_EQUIP] == 0) { + gSaveContext.infTable[29] = 0; + gSaveContext.equips.buttonItems[0] = cursorItem; - if ((pauseCtx->cursorX[PAUSE_EQUIP] == 3) && (gSaveContext.bgsFlag != 0)) { - gSaveContext.equips.buttonItems[0] = ITEM_SWORD_BGS; - gSaveContext.swordHealth = 8; - } else { - if (gSaveContext.equips.buttonItems[0] == ITEM_HEART_PIECE_2) { + if ((pauseCtx->cursorX[PAUSE_EQUIP] == 3) && (gSaveContext.bgsFlag != 0)) { gSaveContext.equips.buttonItems[0] = ITEM_SWORD_BGS; + gSaveContext.swordHealth = 8; + } else { + if (gSaveContext.equips.buttonItems[0] == ITEM_HEART_PIECE_2) { + gSaveContext.equips.buttonItems[0] = ITEM_SWORD_BGS; + } + if ((gSaveContext.equips.buttonItems[0] == ITEM_SWORD_BGS) && (gSaveContext.bgsFlag == 0) && + (gBitFlags[3] & gSaveContext.inventory.equipment)) { + gSaveContext.equips.buttonItems[0] = ITEM_SWORD_KNIFE; + } } - if ((gSaveContext.equips.buttonItems[0] == ITEM_SWORD_BGS) && (gSaveContext.bgsFlag == 0) && - (gBitFlags[3] & gSaveContext.inventory.equipment)) { - gSaveContext.equips.buttonItems[0] = ITEM_SWORD_KNIFE; - } + + Interface_LoadItemIcon1(globalCtx, 0); } - Interface_LoadItemIcon1(globalCtx, 0); + Audio_PlaySoundGeneral(NA_SE_SY_DECIDE, &D_801333D4, 4, &D_801333E0, &D_801333E0, &D_801333E8); + pauseCtx->unk_1E4 = 7; + sEquipTimer = 10; + } else if (CVar_GetS32("gAssignableTunicsAndBoots", 0) != 0) { + // Only allow assigning tunic and boots to c-buttons + if (pauseCtx->cursorY[PAUSE_EQUIP] > 1) { + u16 slot = 0; + switch (cursorItem) { + case ITEM_TUNIC_KOKIRI: + slot = SLOT_TUNIC_KOKIRI; + break; + case ITEM_TUNIC_GORON: + slot = SLOT_TUNIC_GORON; + break; + case ITEM_TUNIC_ZORA: + slot = SLOT_TUNIC_ZORA; + break; + case ITEM_BOOTS_KOKIRI: + slot = SLOT_BOOTS_KOKIRI; + break; + case ITEM_BOOTS_IRON: + slot = SLOT_BOOTS_IRON; + break; + case ITEM_BOOTS_HOVER: + slot = SLOT_BOOTS_HOVER; + break; + default: + break; + } + KaleidoScope_SetupItemEquip(globalCtx, cursorItem, slot, + pauseCtx->equipVtx[cursorSlot * 4].v.ob[0] * 10, + pauseCtx->equipVtx[cursorSlot * 4].v.ob[1] * 10); + } } - - Audio_PlaySoundGeneral(NA_SE_SY_DECIDE, &D_801333D4, 4, &D_801333E0, &D_801333E0, &D_801333E8); - pauseCtx->unk_1E4 = 7; - sEquipTimer = 10; } else { - Audio_PlaySoundGeneral(NA_SE_SY_ERROR, &D_801333D4, 4, &D_801333E0, &D_801333E0, &D_801333E8); + if (CHECK_BTN_ALL(input->press.button, BTN_A)) { + Audio_PlaySoundGeneral(NA_SE_SY_ERROR, &D_801333D4, 4, &D_801333E0, &D_801333E0, &D_801333E8); + } else if ((CVar_GetS32("gAssignableTunicsAndBoots", 0) != 0) && (pauseCtx->cursorY[PAUSE_EQUIP] > 1)) { + Audio_PlaySoundGeneral(NA_SE_SY_ERROR, &D_801333D4, 4, &D_801333E0, &D_801333E0, &D_801333E8); + } } } 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 52c04908fa..705d1461e2 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 @@ -351,43 +351,9 @@ void KaleidoScope_DrawItemSelect(GlobalContext* globalCtx) { if (((gSlotAgeReqs[cursorSlot] == 9) || (gSlotAgeReqs[cursorSlot] == ((void)0, gSaveContext.linkAge))) && (cursorItem != ITEM_SOLD_OUT)) { - if (CHECK_BTN_ALL(input->press.button, BTN_CLEFT)) { - pauseCtx->equipTargetCBtn = 0; - } else if (CHECK_BTN_ALL(input->press.button, BTN_CDOWN)) { - pauseCtx->equipTargetCBtn = 1; - } else if (CHECK_BTN_ALL(input->press.button, BTN_CRIGHT)) { - pauseCtx->equipTargetCBtn = 2; - } - - pauseCtx->equipTargetItem = cursorItem; - pauseCtx->equipTargetSlot = cursorSlot; - pauseCtx->unk_1E4 = 3; - pauseCtx->equipAnimX = pauseCtx->itemVtx[index].v.ob[0] * 10; - pauseCtx->equipAnimY = pauseCtx->itemVtx[index].v.ob[1] * 10; - pauseCtx->equipAnimAlpha = 255; - sEquipAnimTimer = 0; - sEquipState = 3; - sEquipMoveTimer = 10; - if ((pauseCtx->equipTargetItem == ITEM_ARROW_FIRE) || - (pauseCtx->equipTargetItem == ITEM_ARROW_ICE) || - (pauseCtx->equipTargetItem == ITEM_ARROW_LIGHT)) { - index = 0; - if (pauseCtx->equipTargetItem == ITEM_ARROW_ICE) { - index = 1; - } - if (pauseCtx->equipTargetItem == ITEM_ARROW_LIGHT) { - index = 2; - } - Audio_PlaySoundGeneral(NA_SE_SY_SET_FIRE_ARROW + index, &D_801333D4, 4, &D_801333E0, - &D_801333E0, &D_801333E8); - pauseCtx->equipTargetItem = 0xBF + index; - sEquipState = 0; - pauseCtx->equipAnimAlpha = 0; - sEquipMoveTimer = 6; - } else { - Audio_PlaySoundGeneral(NA_SE_SY_DECIDE, &D_801333D4, 4, &D_801333E0, &D_801333E0, - &D_801333E8); - } + KaleidoScope_SetupItemEquip(globalCtx, cursorItem, cursorSlot, + pauseCtx->itemVtx[index].v.ob[0] * 10, + pauseCtx->itemVtx[index].v.ob[1] * 10); } else { Audio_PlaySoundGeneral(NA_SE_SY_ERROR, &D_801333D4, 4, &D_801333E0, &D_801333E0, &D_801333E8); @@ -419,7 +385,9 @@ void KaleidoScope_DrawItemSelect(GlobalContext* globalCtx) { gDPSetEnvColor(POLY_KAL_DISP++, 0, 0, 0, 0); for (i = 0, j = 24 * 4; i < 3; i++, j += 4) { - if (gSaveContext.equips.buttonItems[i + 1] != ITEM_NONE) { + if ((gSaveContext.equips.buttonItems[i + 1] != ITEM_NONE) && + !((gSaveContext.equips.buttonItems[i + 1] >= ITEM_SHIELD_DEKU) && + (gSaveContext.equips.buttonItems[i + 1] <= ITEM_BOOTS_HOVER))) { gSPVertex(POLY_KAL_DISP++, &pauseCtx->itemVtx[j], 4, 0); POLY_KAL_DISP = KaleidoScope_QuadTextureIA8(POLY_KAL_DISP, gEquippedItemOutlineTex, 32, 32, 0); } @@ -496,6 +464,46 @@ void KaleidoScope_DrawItemSelect(GlobalContext* globalCtx) { CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_kaleido_item.c", 516); } +void KaleidoScope_SetupItemEquip(GlobalContext* globalCtx, u16 item, u16 slot, s16 animX, s16 animY) { + Input* input = &globalCtx->state.input[0]; + PauseContext* pauseCtx = &globalCtx->pauseCtx; + + if (CHECK_BTN_ALL(input->press.button, BTN_CLEFT)) { + pauseCtx->equipTargetCBtn = 0; + } else if (CHECK_BTN_ALL(input->press.button, BTN_CDOWN)) { + pauseCtx->equipTargetCBtn = 1; + } else if (CHECK_BTN_ALL(input->press.button, BTN_CRIGHT)) { + pauseCtx->equipTargetCBtn = 2; + } + + pauseCtx->equipTargetItem = item; + pauseCtx->equipTargetSlot = slot; + pauseCtx->unk_1E4 = 3; + pauseCtx->equipAnimX = animX; + pauseCtx->equipAnimY = animY; + pauseCtx->equipAnimAlpha = 255; + sEquipAnimTimer = 0; + sEquipState = 3; + sEquipMoveTimer = 10; + if ((pauseCtx->equipTargetItem == ITEM_ARROW_FIRE) || (pauseCtx->equipTargetItem == ITEM_ARROW_ICE) || + (pauseCtx->equipTargetItem == ITEM_ARROW_LIGHT)) { + u16 index = 0; + if (pauseCtx->equipTargetItem == ITEM_ARROW_ICE) { + index = 1; + } + if (pauseCtx->equipTargetItem == ITEM_ARROW_LIGHT) { + index = 2; + } + Audio_PlaySoundGeneral(NA_SE_SY_SET_FIRE_ARROW + index, &D_801333D4, 4, &D_801333E0, &D_801333E0, &D_801333E8); + pauseCtx->equipTargetItem = 0xBF + index; + sEquipState = 0; + pauseCtx->equipAnimAlpha = 0; + sEquipMoveTimer = 6; + } else { + Audio_PlaySoundGeneral(NA_SE_SY_DECIDE, &D_801333D4, 4, &D_801333E0, &D_801333E0, &D_801333E8); + } +} + static s16 sCButtonPosX[] = { 66, 90, 114 }; static s16 sCButtonPosY[] = { 110, 92, 110 }; 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 928ceadd7b..fa1d88fecf 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 @@ -18,6 +18,7 @@ void KaleidoScope_DrawPlayerWork(GlobalContext* globalCtx); void KaleidoScope_DrawEquipment(GlobalContext* globalCtx); void KaleidoScope_SetCursorVtx(PauseContext* pauseCtx, u16 index, Vtx* vtx); void KaleidoScope_DrawItemSelect(GlobalContext* globalCtx); +void KaleidoScope_SetupItemEquip(GlobalContext* globalCtx, u16 item, u16 slot, s16 animX, s16 animY); void KaleidoScope_UpdateItemEquip(GlobalContext* globalCtx); void KaleidoScope_DrawDungeonMap(GlobalContext* globalCtx, GraphicsContext* gfxCtx); void KaleidoScope_DrawWorldMap(GlobalContext* globalCtx, GraphicsContext* gfxCtx); diff --git a/soh/src/overlays/misc/ovl_kaleido_scope/z_kaleido_scope_PAL.c b/soh/src/overlays/misc/ovl_kaleido_scope/z_kaleido_scope_PAL.c index df6520ebfb..c057c98767 100644 --- a/soh/src/overlays/misc/ovl_kaleido_scope/z_kaleido_scope_PAL.c +++ b/soh/src/overlays/misc/ovl_kaleido_scope/z_kaleido_scope_PAL.c @@ -14,6 +14,8 @@ #include "vt.h" #include "SohHooks.h" +#include "soh/frame_interpolation.h" + static void* sEquipmentFRATexs[] = { gPauseEquipment00FRATex, gPauseEquipment01Tex, gPauseEquipment02Tex, gPauseEquipment03Tex, gPauseEquipment04Tex, gPauseEquipment10FRATex, gPauseEquipment11Tex, gPauseEquipment12Tex, gPauseEquipment13Tex, gPauseEquipment14Tex, @@ -914,6 +916,13 @@ void KaleidoScope_SwitchPage(PauseContext* pauseCtx, u8 pt) { gSaveContext.buttonStatus[3] = D_8082AB6C[pauseCtx->pageIndex + pt][3]; gSaveContext.buttonStatus[4] = D_8082AB6C[pauseCtx->pageIndex + pt][4]; + if ((CVar_GetS32("gAssignableTunicsAndBoots", 0) != 0) && (D_8082ABEC[pauseCtx->mode] == PAUSE_EQUIP)) { + gSaveContext.buttonStatus[1] = BTN_ENABLED; + gSaveContext.buttonStatus[2] = BTN_ENABLED; + gSaveContext.buttonStatus[3] = BTN_ENABLED; + gSaveContext.buttonStatus[4] = BTN_ENABLED; + } + osSyncPrintf("kscope->kscp_pos+pt = %d\n", pauseCtx->pageIndex + pt); gSaveContext.unk_13EA = 0; @@ -1047,6 +1056,7 @@ void KaleidoScope_DrawPages(GlobalContext* globalCtx, GraphicsContext* gfxCtx) { s16 stepG; s16 stepB; + FrameInterpolation_RecordOpenChild(NULL, pauseCtx->state + pauseCtx->pageIndex * 100); OPEN_DISPS(gfxCtx, "../z_kaleido_scope_PAL.c", 1100); if ((pauseCtx->state < 8) || (pauseCtx->state > 0x11)) { @@ -1418,6 +1428,7 @@ void KaleidoScope_DrawPages(GlobalContext* globalCtx, GraphicsContext* gfxCtx) { } CLOSE_DISPS(gfxCtx, "../z_kaleido_scope_PAL.c", 1577); + FrameInterpolation_RecordCloseChild(); } void KaleidoScope_DrawInfoPanel(GlobalContext* globalCtx) { @@ -2898,7 +2909,16 @@ void func_808265BC(GlobalContext* globalCtx) { gSaveContext.buttonStatus[2] = D_8082AB6C[pauseCtx->pageIndex][2]; gSaveContext.buttonStatus[3] = D_8082AB6C[pauseCtx->pageIndex][3]; gSaveContext.buttonStatus[4] = D_8082AB6C[pauseCtx->pageIndex][4]; + pauseCtx->pageIndex = D_8082ABEC[pauseCtx->mode]; + + if ((CVar_GetS32("gAssignableTunicsAndBoots", 0) != 0) && (pauseCtx->pageIndex == PAUSE_EQUIP)) { + gSaveContext.buttonStatus[1] = BTN_ENABLED; + gSaveContext.buttonStatus[2] = BTN_ENABLED; + gSaveContext.buttonStatus[3] = BTN_ENABLED; + gSaveContext.buttonStatus[4] = BTN_ENABLED; + } + pauseCtx->unk_1E4 = 0; pauseCtx->state++; pauseCtx->alpha = 255;