From 6481d12a3daa339c8c2eb10ade9b8922c161e2b8 Mon Sep 17 00:00:00 2001 From: gymnast86 Date: Thu, 6 Aug 2026 09:38:00 -0700 Subject: [PATCH] add lantern, midna, and heart colors --- include/d/actor/d_a_midna.h | 2 +- include/d/d_kantera_icon_meter.h | 2 +- include/d/d_meter2_draw.h | 2 +- mods/basic_cosmetics_mod/src/color_utils.cpp | 15 +- mods/basic_cosmetics_mod/src/color_utils.hpp | 2 + mods/basic_cosmetics_mod/src/hooks.cpp | 344 +++++++++++++++++- .../src/midna_hair_color.cpp | 125 ++++--- mods/basic_cosmetics_mod/src/mod.cpp | 192 ++++++++-- mods/basic_cosmetics_mod/src/mod.hpp | 5 +- .../basic_cosmetics_mod/src/texture_utils.cpp | 1 - 10 files changed, 587 insertions(+), 103 deletions(-) diff --git a/include/d/actor/d_a_midna.h b/include/d/actor/d_a_midna.h index 7793179066..2b07386ca7 100644 --- a/include/d/actor/d_a_midna.h +++ b/include/d/actor/d_a_midna.h @@ -423,7 +423,7 @@ public: static DUSK_GAME_DATA daMidna_texData_s const m_texDataTable[21]; static DUSK_GAME_DATA daMidna_anmData_s const m_anmDataTable[53]; -private: +// private: /* 0x568 */ request_of_phase_process_class mPhase; /* 0x570 */ J3DModel* mpModel; /* 0x574 */ J3DModel* mpShadowModel; diff --git a/include/d/d_kantera_icon_meter.h b/include/d/d_kantera_icon_meter.h index 04466df8be..67e2dff278 100644 --- a/include/d/d_kantera_icon_meter.h +++ b/include/d/d_kantera_icon_meter.h @@ -30,7 +30,7 @@ public: void drawSelf() { mpKanteraIcon->draw(); } -private: +// private: /* 0x04 */ dDlst_KanteraIcon_c* mpKanteraIcon; /* 0x08 */ CPaneMgr* mpParent; /* 0x0C */ CPaneMgr* mpGauge; diff --git a/include/d/d_meter2_draw.h b/include/d/d_meter2_draw.h index 10cded803d..aaad9dec70 100644 --- a/include/d/d_meter2_draw.h +++ b/include/d/d_meter2_draw.h @@ -154,7 +154,7 @@ public: } #endif -private: +// private: /* 0x004 */ item_params mItemParams[4]; /* 0x074 */ JKRExpHeap* heap; /* 0x078 */ J2DScreen* mpScreen; diff --git a/mods/basic_cosmetics_mod/src/color_utils.cpp b/mods/basic_cosmetics_mod/src/color_utils.cpp index 6b2718d37f..d555186907 100644 --- a/mods/basic_cosmetics_mod/src/color_utils.cpp +++ b/mods/basic_cosmetics_mod/src/color_utils.cpp @@ -60,13 +60,9 @@ GXColor hex_color_str_to_gx_color(const std::string& hexColorStr) { return GXColor{r, g, b}; } +static f32 rainbowPhaseAngle = 0.f; + GXColor get_rainbow_rgb(f32 amplitude) { - static f32 rainbowPhaseAngle = 0.f; - f32 angleIncrement = 1.0f; // Degrees per frame (Adjust for speed) - rainbowPhaseAngle += angleIncrement; - if (rainbowPhaseAngle >= 360.0f) { - rainbowPhaseAngle -= 360.0f; - } f32 phase_rad = rainbowPhaseAngle * M_PI / 180.0f; u8 r_val = (u8)(amplitude * (sinf(phase_rad) + 1.0f) + 0.5f); @@ -78,4 +74,11 @@ GXColor get_rainbow_rgb(f32 amplitude) { rgbColor.b = b_val; rgbColor.a = 0xff; return rgbColor; +} + +void update_rainbow_rgb(f32 increment) { + rainbowPhaseAngle += increment; + if (rainbowPhaseAngle >= 360.0f) { + rainbowPhaseAngle -= 360.0f; + } } \ No newline at end of file diff --git a/mods/basic_cosmetics_mod/src/color_utils.hpp b/mods/basic_cosmetics_mod/src/color_utils.hpp index 328597e157..eef8b438d7 100644 --- a/mods/basic_cosmetics_mod/src/color_utils.hpp +++ b/mods/basic_cosmetics_mod/src/color_utils.hpp @@ -23,3 +23,5 @@ bool is_valid_hex_color_str(std::string_view hexStr); GXColor hex_color_str_to_gx_color(const std::string& hexColorStr); GXColor get_rainbow_rgb(f32 amplitude); + +void update_rainbow_rgb(f32 increment); diff --git a/mods/basic_cosmetics_mod/src/hooks.cpp b/mods/basic_cosmetics_mod/src/hooks.cpp index cf83759a95..75f1cc93a5 100644 --- a/mods/basic_cosmetics_mod/src/hooks.cpp +++ b/mods/basic_cosmetics_mod/src/hooks.cpp @@ -1,18 +1,300 @@ #include "hooks.hpp" + +#include "mod.hpp" +#include "color_utils.hpp" +#include "midna_hair_color.hpp" #include "texture_utils.hpp" #include "types.h" #include "mods/svc/hook.hpp" #include "mods/svc/log.hpp" +#include "d/actor/d_a_alink.h" +#include "d/actor/d_a_midna.h" +#include "d/d_kankyo.h" +#include "d/d_kantera_icon_meter.h" +#include "d/d_meter2.h" +#include "d/d_meter2_draw.h" +#include "d/d_meter2_info.h" +#include "d/d_pane_class.h" +#include "m_Do/m_Do_dvd_thread.h" + +#include + DEFINE_HOOK_SYMBOL( "mDoDvdThd_mountArchive_c::execute", s32(mDoDvdThd_mountArchive_c*), MountArchiveExecute); - void mount_archive_execute_post(ModContext*, void* args, void* retval, void* userdata) { auto archive = mods::arg(args, 0); handle_texture_overrides_on_load(archive); } +// Helper for getting lantern color +std::optional get_lantern_color() { + auto lanternColorStr = get_str_option(get_cvars().lanternGlowColor, ""); + if (is_valid_hex_color_str(lanternColorStr) || lanternColorStr == "rainbow") { + if (is_valid_hex_color_str(lanternColorStr)) { + return hex_color_str_to_gx_color(lanternColorStr); + } + + // Assume rainbow if not a valid hex str + auto lanternColor = get_rainbow_rgb(127.5f); + lanternColor.r /= 2; + lanternColor.g /= 2; + lanternColor.b /= 2; + return lanternColor; + } + + return std::nullopt; +} + +// Override Lantern ambience color +DEFINE_HOOK(&dKy_WolfEyeLight_set, WolfEyeLightSet); +void wolf_eye_light_set_post(ModContext*, void* args, void* retval, void* userdata) { + auto maybeLanternColor = get_lantern_color(); + if (maybeLanternColor.has_value()) { + auto lanternColor = maybeLanternColor.value(); + + dScnKy_env_light_c* kankyo = dKy_getEnvlight(); + kankyo->field_0x0c18[0].mColor.r = lanternColor.r; + kankyo->field_0x0c18[0].mColor.g = lanternColor.g; + kankyo->field_0x0c18[0].mColor.b = lanternColor.b; + } +} + +// Override Lantern Sphere color +DEFINE_HOOK(&daAlink_c::preKandelaarDraw, PreKandelaarDraw); +void pre_kandelaar_draw_post(ModContext*, void* args, void* retval, void* userdata) { + auto maybeLanternColor = get_lantern_color(); + if (maybeLanternColor.has_value()) { + auto lanternColor = maybeLanternColor.value(); + + J3DMaterial* mat_p = daAlink_getAlinkActorClass()->mpKanteraGlowModel->getModelData()->getMaterialNodePointer(0); + + J3DGXColorS10 color; + color.r = lanternColor.r; + color.g = lanternColor.g; + color.b = lanternColor.b; + color.a = 255; + mat_p->setTevColor(1, &color); + + color.r = lanternColor.r; + color.g = lanternColor.g; + color.b = lanternColor.b; + mat_p->setTevColor(2, &color); + } +} + +// Override Main Lantern Meter Color +DEFINE_HOOK(&CPaneMgr::setBlackWhite, CPaneMgrSetBlackWhite); +HookAction cpane_mgr_set_black_white_pre(ModContext*, void* args, void* retval, void* userdata) { + // Check for magic meter + auto pane = mods::arg(args, 0); + if (dMeter2Info_getMeterClass() == NULL || pane != dMeter2Info_getMeterClass()->getMeterDrawPtr()->mpMagicMeter) { + return HOOK_CONTINUE; + } + + // If magic meter, check to see we're setting lantern colors + auto& black = mods::arg_ref(args, 1); + auto& white = mods::arg_ref(args, 2); + if (black != JUtility::TColor(255, 255, 140, 255) && + white != JUtility::TColor(230, 170, 0, 255)) { + return HOOK_CONTINUE; + } + + auto maybeLanternColor = get_lantern_color(); + if (maybeLanternColor.has_value()) { + auto lanternColor = maybeLanternColor.value(); + black = JUtility::TColor(lanternColor.r, lanternColor.g, lanternColor.b, 255); + white = JUtility::TColor(lanternColor.r, lanternColor.g, lanternColor.b, 255); + } + + return HOOK_CONTINUE; +} + +// Override Lantern Icon Meter Color +DEFINE_HOOK(&dKantera_icon_c::setNowGauge, KanteraIconSetNowGauge); +void kantera_icon_set_now_gauge_post(ModContext*, void* args, void* retval, void* userdata) { + auto maybeLanternColor = get_lantern_color(); + if (maybeLanternColor.has_value()) { + auto lanternColor = maybeLanternColor.value(); + + auto kanteraIcon = mods::arg(args, 0); + kanteraIcon->mpGauge->setBlackWhite( + JUtility::TColor(lanternColor.r, lanternColor.g, lanternColor.b, 255), + JUtility::TColor(lanternColor.r, lanternColor.g, lanternColor.b, 255)); + } +} + +// Override Light Sword Effect Color +DEFINE_HOOK(&daAlink_c::setLightningSwordEffect, SetLightningSwordEffect); +void set_lightning_sword_effect_post(ModContext*, void* args, void* retval, void* userdata) { + auto glowColorStr = get_str_option(get_cvars().lightSwordGlowColor, ""); + if (is_valid_hex_color_str(glowColorStr) || glowColorStr == "rainbow") { + GXColor glowColor{}; + if (glowColorStr == "rainbow") { + glowColor = get_rainbow_rgb(127.5f); + } else { + glowColor = hex_color_str_to_gx_color(glowColorStr); + } + + auto link = mods::arg(args, 0); + if (link->mEquipItem == 0x103 && link->checkNoResetFlg3(daPy_py_c::FLG3_UNK_100000)) { + for (size_t i = 0; i < 3; i++) { + auto emitter = dComIfGp_particle_getEmitter(link->field_0x327c[i]); + if (emitter != NULL) { + emitter->setGlobalEnvColor(glowColor.r, glowColor.g, glowColor.b); + emitter->setGlobalPrmColor(glowColor.r, glowColor.g, glowColor.b); + } + } + } + } +} + +// Heart Icon tags +static constexpr std::array heart_tags = { + MULTI_CHAR('hear_00'), MULTI_CHAR('hear_01'), MULTI_CHAR('hear_02'), MULTI_CHAR('hear_03'), MULTI_CHAR('hear_04'), MULTI_CHAR('hear_05'), MULTI_CHAR('hear_06'), + MULTI_CHAR('hear_07'), MULTI_CHAR('hear_08'), MULTI_CHAR('hear_09'), MULTI_CHAR('hear_10'), MULTI_CHAR('hear_11'), MULTI_CHAR('hear_12'), MULTI_CHAR('hear_13'), + MULTI_CHAR('hear_14'), MULTI_CHAR('hear_15'), MULTI_CHAR('hear_16'), MULTI_CHAR('hear_17'), MULTI_CHAR('hear_18'), MULTI_CHAR('hear_19'), MULTI_CHAR('bigh_00'), + MULTI_CHAR('bigh_01'), MULTI_CHAR('bigh_02'), MULTI_CHAR('bigh_03'), /*MULTI_CHAR('bigh_n')*/ +}; + +DEFINE_HOOK(&dMeter2Draw_c::init, dMeter2Init); +void d_meter_2_init_post(ModContext*, void* args, void* retval, void* userdata) { + auto dMeter2Draw = mods::arg(args, 0); + auto heartColorStr = get_str_option(get_cvars().heartColor, ""); + if (is_valid_hex_color_str(heartColorStr)) { + auto heartColor = hex_color_str_to_gx_color(heartColorStr); + auto screen = dMeter2Draw->getMainScreenPtr(); + for (auto tag : heart_tags) { + auto element = static_cast(screen->search(tag)); + if (element != nullptr) { + element->setBlackWhite(heartColor, JUtility::TColor(200, 200, 200, 255)); + } + } + } +} + +// Override Midna Hair Color +DEFINE_HOOK(&daMidna_c::create, MidnaCreate); +void midna_create_post(ModContext*, void* args, void* retval, void* userdata) { + auto step = reinterpret_cast(retval); + // check for cPhs_Compleate + if (*step != 4) { + return; + } + + auto midna = mods::arg(args, 0); + midna->field_0x6e0 = *get_midna_hair_normalColor(); + if (dKy_darkworld_check()) { + midna->field_0x6e8 = *get_midna_hair_normalKColor(); + midna->field_0x6ec = *get_midna_hair_normalKColor2(); + } else { + midna->field_0x6e8 = *get_midna_hair_lNormalKColor(); + midna->field_0x6ec = *get_midna_hair_lNormalKColor2(); + } +} + +// Override Midna Hair Color Part 2 +DEFINE_HOOK(&daMidna_c::setBodyPartMatrix, MidnaSetBodyPartMatrix); + +static GXColorS10 midnaField0x6e0{}; +static GXColor midnaField0x6e8{}; +static GXColor midnaField0x6ec{}; + +// Copy the original values from before the function +HookAction midna_set_body_part_matrix_pre(ModContext*, void* args, void* retval, void* userdata) { + auto midna = mods::arg(args, 0); + + midnaField0x6e0 = midna->field_0x6e0; + midnaField0x6e8 = midna->field_0x6e8; + midnaField0x6ec = midna->field_0x6ec; + + return HOOK_CONTINUE; +} + +void midna_set_body_part_matrix_post(ModContext*, void* args, void* retval, void* userdata) { + auto midna = mods::arg(args, 0); + if (midna->mpHairhandBmd != NULL) { + // Restore the original values from before the function ran (this undoes the chase) + midna->field_0x6e0 = midnaField0x6e0; + midna->field_0x6e8 = midnaField0x6e8; + midna->field_0x6ec = midnaField0x6ec; + + // statement copied from inside function to determine colors + bool bigColors = midna->checkStateFlg0(daMidna_c::FLG0_UNK_10000000) || + midna->mBckHeap[2].getIdx() == daMidna_c::m_anmDataTable[daMidna_c::ANM_HAIR].mResID || + midna->mBckHeap[2].getIdx() == daMidna_c::m_anmDataTable[daMidna_c::ANM_S_TAKES].mResID || + midna->mBckHeap[2].getIdx() == daMidna_c::m_anmDataTable[daMidna_c::ANM_S_WAITS].mResID || + midna->mBckHeap[2].getIdx() == daMidna_c::m_anmDataTable[daMidna_c::ANM_S_PACKAWAY].mResID || + midna->mBckHeap[2].getIdx() == daMidna_c::m_anmDataTable[daMidna_c::ANM_GRABST].mResID || + midna->checkEndResetStateFlg0(daMidna_c::ERFLG0_UNK_40) || + dComIfGp_checkPlayerStatus1(0, 0x800000); + + GXColorS10 color{}; + GXColor kcolor1{}; + GXColor kcolor2{}; + + // Then set our own colors + if (bigColors) { + kcolor1 = *get_midna_hair_bigKColor(); + if (dKy_darkworld_check()) { + color = *get_midna_hair_bigColor(); + kcolor2 = *get_midna_hair_normalKColor2(); + } else { + color = *get_midna_hair_lBigColor(); + kcolor2 = *get_midna_hair_lBigKColor2(); + } + } else { + color = *get_midna_hair_normalColor(); + if (dKy_darkworld_check()) { + kcolor1 = *get_midna_hair_normalKColor(); + kcolor2 = *get_midna_hair_normalKColor2(); + } else { + kcolor1 = *get_midna_hair_lNormalKColor(); + kcolor2 = *get_midna_hair_lNormalKColor2(); + } + } + + // And reapply the chase + cLib_chaseS(&midna->field_0x6e0.r, color.r, 10); + cLib_chaseS(&midna->field_0x6e0.g, color.g, 10); + cLib_chaseS(&midna->field_0x6e0.b, color.b, 10); + cLib_chaseUC(&midna->field_0x6e8.r, kcolor1.r, 10); + cLib_chaseUC(&midna->field_0x6e8.g, kcolor1.g, 10); + cLib_chaseUC(&midna->field_0x6e8.b, kcolor1.b, 10); + cLib_chaseUC(&midna->field_0x6ec.r, kcolor2.r, 10); + cLib_chaseUC(&midna->field_0x6ec.g, kcolor2.g, 10); + cLib_chaseUC(&midna->field_0x6ec.b, kcolor2.b, 10); + } +} + +// Override Midna Charge Ring Color +DEFINE_HOOK(&daAlink_c::setWolfLockDomeModel, SetWolfLockDomeModel); +void wolf_lock_dome_model_post(ModContext*, void* args, void* retval, void* userdata) { + auto domeRingColorStr = get_str_option(get_cvars().midnaChargeRingColor, ""); + if (is_valid_hex_color_str(domeRingColorStr)) { + auto domeRingColor = hex_color_str_to_gx_color(domeRingColorStr); + const u8 domeWave1RGBA[3] = {domeRingColor.r, domeRingColor.g, domeRingColor.b}; + const u8 domeWave2RGBA[3] = {domeRingColor.r, domeRingColor.g, domeRingColor.b}; + u8** chromaRegisterTable = reinterpret_cast(&daAlink_getAlinkActorClass()->field_0x0724->mAnmCRegDataR); + + for (int i = 0; i < 3; i++) + { + u8* currentTable = chromaRegisterTable[i]; + const u8 currentWave1Color = domeWave1RGBA[i]; + const u8 currentWave2Color = domeWave2RGBA[i]; + const u8 currentBaseColor = (currentWave1Color + currentWave2Color) / 2; + + currentTable[0x3] = currentBaseColor; // Set Alpha for the ring base + currentTable[0x13] = currentWave1Color; // Set Alpha for ring wave 1 + currentTable[0x23] = currentWave2Color; // Set Alpha for ring wave 2 + currentTable[0xB] = currentBaseColor; // Set Alpha for darkworld ring base + currentTable[0x1B] = currentWave1Color; // Set Alpha for darkworld ring wave 1 + currentTable[0x2B] = currentWave2Color; // Set Alpha for darkworld ring wave 2 + } + } +} + ModResult add_all_hooks() { auto result = mods::hook::add_post(mount_archive_execute_post); if (result != MOD_OK) { @@ -20,5 +302,65 @@ ModResult add_all_hooks() { return result; } + result = mods::hook::add_post(wolf_eye_light_set_post); + if (result != MOD_OK) { + mods::log::debug("failed to add post hook to dKy_WolfEyeLight_set, Result {}", static_cast(result)); + return result; + } + + result = mods::hook::add_post(pre_kandelaar_draw_post); + if (result != MOD_OK) { + mods::log::debug("failed to add post hook to daAlink_c::preKandelaarDraw, Result {}", static_cast(result)); + return result; + } + + result = mods::hook::add_post(wolf_lock_dome_model_post); + if (result != MOD_OK) { + mods::log::debug("failed to add post hook to daAlink_c::setWolfLockDomeModel, Result {}", static_cast(result)); + return result; + } + + result = mods::hook::add_pre(cpane_mgr_set_black_white_pre); + if (result != MOD_OK) { + mods::log::debug("failed to add pre hook to CPaneMgr::setBlackWhite, Result {}", static_cast(result)); + return result; + } + + result = mods::hook::add_post(kantera_icon_set_now_gauge_post); + if (result != MOD_OK) { + mods::log::debug("failed to add post hook to dKantera_icon_c::setNowGauge, Result {}", static_cast(result)); + return result; + } + + result = mods::hook::add_post(set_lightning_sword_effect_post); + if (result != MOD_OK) { + mods::log::debug("failed to add post hook to daAlink_c::setLightningSwordEffect, Result {}", static_cast(result)); + return result; + } + + result = mods::hook::add_post(midna_create_post); + if (result != MOD_OK) { + mods::log::debug("failed to add post hook to daMidna_c::create, Result {}", static_cast(result)); + return result; + } + + result = mods::hook::add_pre(midna_set_body_part_matrix_pre); + if (result != MOD_OK) { + mods::log::debug("failed to add pre hook to daMidna_c::setBodyPartMatrix, Result {}", static_cast(result)); + return result; + } + + result = mods::hook::add_post(midna_set_body_part_matrix_post); + if (result != MOD_OK) { + mods::log::debug("failed to add post hook to daMidna_c::setBodyPartMatrix, Result {}", static_cast(result)); + return result; + } + + result = mods::hook::add_post(d_meter_2_init_post); + if (result != MOD_OK) { + mods::log::debug("failed to add post hook to dMeter2Draw_c::init, Result {}", static_cast(result)); + return result; + } + return MOD_OK; } \ No newline at end of file diff --git a/mods/basic_cosmetics_mod/src/midna_hair_color.cpp b/mods/basic_cosmetics_mod/src/midna_hair_color.cpp index 48c331706c..c06629871c 100644 --- a/mods/basic_cosmetics_mod/src/midna_hair_color.cpp +++ b/mods/basic_cosmetics_mod/src/midna_hair_color.cpp @@ -1,55 +1,75 @@ #include "midna_hair_color.hpp" #include "mod.hpp" -#include -#include -#include +#include "mods/svc/log.hpp" -static std::unordered_map> const midnaHairColors = { - {"Default", {{ - /*l_lNormalKColor*/ /*l_normalKColor*/ /*l_normalColor*/ - {0xFF, 0xDC, 0x00}, {0xB4, 0x87, 0x00}, {0x50, 0x00, 0x00}, - /*l_bigKColor*/ /*l_lBigColor*/ /*l_bigColor*/ - {0x50, 0x00, 0x00}, {0xFF, 0x78, 0x00}, {0xFF, 0x64, 0x78}, - /*l_lNormalKColor2*//*l_normalKColor2*/ /*l_lBigKColor2*/ - {0x00, 0xC3, 0xEB}, {0xC3, 0xC3, 0x00}, {0xAA, 0xFF, 0xC3}}}}, - {"Pink", {{ - {0xF5, 0xCF, 0xF3}, {0xAD, 0x7F, 0x7F}, {0x1B, 0x00, 0x20}, - {0x3C, 0x02, 0x58}, {0xE3, 0x72, 0xF2}, {0xE3, 0x5F, 0xF8}, - {0xDD, 0x00, 0xEB}, {0xDD, 0x00, 0xC3}, {0xF6, 0x4C, 0xFF}}}}, - {"Red", {{ - {0xE4, 0x65, 0x41}, {0xA1, 0x3E, 0x22}, {0x21, 0x00, 0x00}, - {0x4F, 0x02, 0x01}, {0xF0, 0x3A, 0x25}, {0xF0, 0x30, 0x8C}, - {0xEB, 0x00, 0x00}, {0xEB, 0x00, 0x00}, {0xFF, 0x4F, 0x3A}}}}, - {"Yellow", {{ - {0x91, 0x83, 0x0E}, {0x66, 0x50, 0x07}, {0x0E, 0x0B, 0x00}, - {0x24, 0x25, 0x02}, {0xCB, 0xB7, 0x00}, {0xCB, 0x99, 0x78}, - {0xEB, 0xDE, 0x00}, {0xEB, 0xDE, 0x00}, {0xFF, 0xF8, 0xBF}}}}, - {"Green", {{ - {0x35, 0x79, 0x53}, {0x25, 0x4A, 0x2B}, {0x00, 0x0E, 0x05}, - {0x0A, 0x29, 0x10}, {0x00, 0xB6, 0x6F}, {0x00, 0x98, 0xB3}, - {0x1F, 0xEB, 0x00}, {0x1F, 0xEB, 0x00}, {0x9A, 0xFF, 0x81}}}}, - {"Blue", {{ - {0x00, 0x72, 0xFF}, {0x00, 0x46, 0x85}, {0x00, 0x08, 0x28}, - {0x16, 0x1B, 0x5D}, {0x00, 0x60, 0xFF}, {0x00, 0x50, 0xFF}, - {0x00, 0x48, 0xEB}, {0x00, 0x48, 0xC3}, {0x3A, 0x66, 0xFF}}}}, - {"Purple", {{ - {0x6F, 0x34, 0xFF}, {0x4E, 0x20, 0x85}, {0x0D, 0x00, 0x34}, - {0x15, 0x08, 0x79}, {0x62, 0x00, 0xFF}, {0x62, 0x00, 0xFF}, - {0x7B, 0x00, 0xEB}, {0x7B, 0x00, 0xC3}, {0x94, 0x3E, 0xFF}}}}, - {"Brown", {{ - {0x00, 0x00, 0x00}, {0x00, 0x00, 0x00}, {0x1A, 0x05, 0x00}, - {0x3C, 0x19, 0x0E}, {0x3F, 0x1D, 0x0B}, {0x3F, 0x18, 0x7E}, - {0x3F, 0x1D, 0x0B}, {0x3F, 0x1D, 0x09}, {0x59, 0x32, 0x1E}}}}, - {"White", {{ - {0xF0, 0xF1, 0xF1}, {0xA9, 0x94, 0x7E}, {0x09, 0x0B, 0x0C}, - {0x22, 0x24, 0x24}, {0xFF, 0xFF, 0xFF}, {0xFF, 0xD5, 0xFF}, - {0xEA, 0xEA, 0xEA}, {0xEA, 0xEA, 0xC2}, {0xF3, 0xF3, 0xF3}}}}, - {"Black", {{ - {0x00, 0x00, 0x00}, {0x00, 0x00, 0x00}, {0x0B, 0x0B, 0x0B}, - {0x23, 0x23, 0x23}, {0x00, 0x00, 0x00}, {0x00, 0x00, 0x78}, - {0x00, 0x00, 0x00}, {0x00, 0x00, 0x00}, {0x00, 0x00, 0x00}}}}, -}; +#include + +static constexpr std::array, 10> midnaHairColors = {{ + // Default + {{ + /*l_lNormalKColor*/ /*l_normalKColor*/ /*l_normalColor*/ + {0xFF, 0xDC, 0x00}, {0xB4, 0x87, 0x00}, {0x50, 0x00, 0x00}, + /*l_bigKColor*/ /*l_lBigColor*/ /*l_bigColor*/ + {0x50, 0x00, 0x00}, {0xFF, 0x78, 0x00}, {0xFF, 0x64, 0x78}, + /*l_lNormalKColor2*//*l_normalKColor2*/ /*l_lBigKColor2*/ + {0x00, 0xC3, 0xEB}, {0xC3, 0xC3, 0x00}, {0xAA, 0xFF, 0xC3} + }}, + // Pink + {{ + {0xF5, 0xCF, 0xF3}, {0xAD, 0x7F, 0x7F}, {0x1B, 0x00, 0x20}, + {0x3C, 0x02, 0x58}, {0xE3, 0x72, 0xF2}, {0xE3, 0x5F, 0xF8}, + {0xDD, 0x00, 0xEB}, {0xDD, 0x00, 0xC3}, {0xF6, 0x4C, 0xFF} + }}, + // Red + {{ + {0xE4, 0x65, 0x41}, {0xA1, 0x3E, 0x22}, {0x21, 0x00, 0x00}, + {0x4F, 0x02, 0x01}, {0xF0, 0x3A, 0x25}, {0xF0, 0x30, 0x8C}, + {0xEB, 0x00, 0x00}, {0xEB, 0x00, 0x00}, {0xFF, 0x4F, 0x3A} + }}, + // Yellow + {{ + {0x91, 0x83, 0x0E}, {0x66, 0x50, 0x07}, {0x0E, 0x0B, 0x00}, + {0x24, 0x25, 0x02}, {0xCB, 0xB7, 0x00}, {0xCB, 0x99, 0x78}, + {0xEB, 0xDE, 0x00}, {0xEB, 0xDE, 0x00}, {0xFF, 0xF8, 0xBF} + }}, + // Green + {{ + {0x35, 0x79, 0x53}, {0x25, 0x4A, 0x2B}, {0x00, 0x0E, 0x05}, + {0x0A, 0x29, 0x10}, {0x00, 0xB6, 0x6F}, {0x00, 0x98, 0xB3}, + {0x1F, 0xEB, 0x00}, {0x1F, 0xEB, 0x00}, {0x9A, 0xFF, 0x81} + }}, + // Blue + {{ + {0x00, 0x72, 0xFF}, {0x00, 0x46, 0x85}, {0x00, 0x08, 0x28}, + {0x16, 0x1B, 0x5D}, {0x00, 0x60, 0xFF}, {0x00, 0x50, 0xFF}, + {0x00, 0x48, 0xEB}, {0x00, 0x48, 0xC3}, {0x3A, 0x66, 0xFF} + }}, + // Purple + {{ + {0x6F, 0x34, 0xFF}, {0x4E, 0x20, 0x85}, {0x0D, 0x00, 0x34}, + {0x15, 0x08, 0x79}, {0x62, 0x00, 0xFF}, {0x62, 0x00, 0xFF}, + {0x7B, 0x00, 0xEB}, {0x7B, 0x00, 0xC3}, {0x94, 0x3E, 0xFF} + }}, + // Brown + {{ + {0x00, 0x00, 0x00}, {0x00, 0x00, 0x00}, {0x1A, 0x05, 0x00}, + {0x3C, 0x19, 0x0E}, {0x3F, 0x1D, 0x0B}, {0x3F, 0x18, 0x7E}, + {0x3F, 0x1D, 0x0B}, {0x3F, 0x1D, 0x09}, {0x59, 0x32, 0x1E} + }}, + // White + {{ + {0xF0, 0xF1, 0xF1}, {0xA9, 0x94, 0x7E}, {0x09, 0x0B, 0x0C}, + {0x22, 0x24, 0x24}, {0xFF, 0xFF, 0xFF}, {0xFF, 0xD5, 0xFF}, + {0xEA, 0xEA, 0xEA}, {0xEA, 0xEA, 0xC2}, {0xF3, 0xF3, 0xF3} + }}, + // Black + {{ + {0x00, 0x00, 0x00}, {0x00, 0x00, 0x00}, {0x0B, 0x0B, 0x0B}, + {0x23, 0x23, 0x23}, {0x00, 0x00, 0x00}, {0x00, 0x00, 0x78}, + {0x00, 0x00, 0x00}, {0x00, 0x00, 0x00}, {0x00, 0x00, 0x00} + }} +}}; static struct { GXColorS10 l_normalColor = { 0x50, 0x00, 0x00, 0x00 }; @@ -65,13 +85,8 @@ static struct { void set_all_midna_hair_colors() { auto& g_cvars = get_cvars(); - const auto& hairBaseColor = get_str_option(g_cvars.midnaHairBaseColor, ""); - const auto& hairTipsColor = get_str_option(g_cvars.midnaHairTipsColor, ""); - - // Don't set colors if either is invalid - if (!midnaHairColors.contains(hairBaseColor) || !midnaHairColors.contains(hairTipsColor)) { - return; - } + auto hairBaseColor = get_int_option(g_cvars.midnaHairBaseColor, 0); + auto hairTipsColor = get_int_option(g_cvars.midnaHairTipsColor, 0); // Colors we have to convert to GXColorS10 auto& normalColor = midnaHairColors.at(hairBaseColor)[2]; diff --git a/mods/basic_cosmetics_mod/src/mod.cpp b/mods/basic_cosmetics_mod/src/mod.cpp index 2723e73de6..9cf961279c 100644 --- a/mods/basic_cosmetics_mod/src/mod.cpp +++ b/mods/basic_cosmetics_mod/src/mod.cpp @@ -1,5 +1,7 @@ #include "mod.hpp" #include "hooks.hpp" +#include "color_utils.hpp" +#include "midna_hair_color.hpp" #include "mods/service.hpp" #include "mods/svc/config.h" @@ -8,8 +10,6 @@ #include "mods/svc/log.hpp" #include "mods/svc/ui.h" -#include "m_Do/m_Do_dvd_thread.h" - #include DEFINE_MOD(); @@ -35,6 +35,14 @@ std::string get_str_option(ConfigVarHandle handle, const std::string& fallback) return value; } +int64_t get_int_option(ConfigVarHandle handle, int64_t fallback) { + int64_t value = fallback; + if (handle == 0 || svc_config->get_int(mod_ctx, handle, &value) != MOD_OK) { + return fallback; + } + return value; +} + namespace { UiWindowHandle g_cosmeticsWindow = 0; @@ -50,6 +58,18 @@ ModResult register_str_option( return MOD_OK; } +ModResult register_int_option( + const char* name, int64_t defaultValue, ConfigVarHandle& outHandle, ModError* error) { + ConfigVarDesc cvarDesc = CONFIG_VAR_DESC_INIT; + cvarDesc.name = name; + cvarDesc.type = CONFIG_VAR_INT; + cvarDesc.default_int = defaultValue; + if (svc_config->register_var(mod_ctx, &cvarDesc, &outHandle) != MOD_OK) { + return mods::set_error(error, MOD_ERROR, "failed to register cosmetics option"); + } + return MOD_OK; +} + void add_control(UiElementHandle pane, const UiControlDesc& desc) { auto result = svc_ui->pane_add_control(mod_ctx, pane, &desc, nullptr); if (result != MOD_OK) { @@ -57,37 +77,121 @@ void add_control(UiElementHandle pane, const UiControlDesc& desc) { } } -void add_cosmetic_option(UiElementHandle left, ConfigVarHandle cvar, const std::string& name) { +const char* kDefaultHexColorsExplanation = + "Set the color with any 6 digit hex code. A reload will be required to see changes.
" + "Common colors:
" + "- Red: ab706e
" + "- Blue: 6382a0
" + "- Purple: 94749a
" + "- Orange: ec8644
" + "- Yellow: b9ab00
" + "- Pink: ec9fc8
" + "- Black: 505154
" + "- White: f8f7f4
" + "- Brown: 91723e
"; + +const char* kGlowColorsExplanation = + "Set the color with any 6 digit hex code, or with \"rainbow\" for a rainbow effect.
" + "Common colors:
" + "- Red: ff0000
" + "- Orange: f68821
" + "- Yellow: f6f321
" + "- Green: 00ff00
" + "- Blue: 0000ff
" + "- Purple: 8000ff
" + "- White: a0a0a0
"; + +const char* kMasterSwordColorsExplanation = + "Set the color with any 6 digit hex code. Closing and re-opening Dusklight will be required to see changes.
" + "Common colors:
" + "- Red: ff0000
" + "- Orange: f68821
" + "- Yellow: f6f321
" + "- Green: 00ff00
" + "- Blue: 0000ff
" + "- Purple: 8000ff
" + "- White: a0a0a0
" + "- Cyan: 30d0d0
"; + +const char* kChargeRingColorsExplanation = + "Set the color with any 6 digit hex code.
" + "Common colors:
" + "- Pink: ff9f9f
" + "- Red: ff0000
" + "- Yellow: ffff00
" + "- Green: 00ff00
" + "- Blue: 0000ff
" + "- Purple: ff00ff
" + "- Brown: 331900
" + "- White: feffff
" + "- Black: 000000
"; + +void add_cosmetic_option(UiElementHandle left, ConfigVarHandle cvar, const char* name, const char* helpRml) { UiControlDesc control = UI_CONTROL_DESC_INIT; control.kind = UI_CONTROL_STRING; - control.label = name.c_str(); - control.help_rml = "Set the color with a 6 digit hex code. A reload may be required to see changes."; + control.label = name; + control.help_rml = helpRml; control.binding = UI_BINDING_CONFIG_VAR; control.config_var = cvar; control.max_length = 6; add_control(left, control); } +void add_cosmetic_option_with_rainbow(UiElementHandle left, ConfigVarHandle cvar, const char* name, const char* helpRml) { + UiControlDesc control = UI_CONTROL_DESC_INIT; + control.kind = UI_CONTROL_STRING; + control.label = name; + control.help_rml = helpRml; + control.binding = UI_BINDING_CONFIG_VAR; + control.config_var = cvar; + control.max_length = 7; + add_control(left, control); +} + +void add_midna_hair_option(UiElementHandle left, ConfigVarHandle cvar, const std::string& name) { + static const char* kMidnaHairOptions[] = { + "Default", "Pink", "Red", "Yellow", "Green", "Blue", "Purple", "Brown", "White", "Black" + }; + UiControlDesc control = UI_CONTROL_DESC_INIT; + control.kind = UI_CONTROL_SELECT; + control.label = name.c_str(); + control.help_rml = "Choose Midna's hair color."; + control.binding = UI_BINDING_CONFIG_VAR; + control.config_var = cvar; + control.options = kMidnaHairOptions; + control.option_count = 10; + add_control(left, control); +} + ModResult build_equipment_colors_tab( ModContext*, UiWindowHandle, UiElementHandle left, UiElementHandle right, void*, ModError*) { (void)right; - add_cosmetic_option(left, g_cvars.herosTunicCapColor, "Hero's Tunic Cap Color"); - add_cosmetic_option(left, g_cvars.herosTunicTorsoColor, "Hero's Tunic Body Color"); - add_cosmetic_option(left, g_cvars.herosTunicSkirtColor, "Hero's Tunic Skirt Color"); - add_cosmetic_option(left, g_cvars.zoraArmorCapColor, "Zora Armor Cap Color"); - add_cosmetic_option(left, g_cvars.zoraArmorHelmetColor, "Zora Armor Helmet Color"); - add_cosmetic_option(left, g_cvars.zoraArmorTorsoColor, "Zora Armor Torso Color"); - add_cosmetic_option(left, g_cvars.zoraArmorScalesColor, "Zora Armor Scales Color"); - add_cosmetic_option(left, g_cvars.zoraArmorFlippersColor, "Zora Armor Flippers Color"); - add_cosmetic_option(left, g_cvars.lanternGlowColor, "Lantern Glow Color"); - add_cosmetic_option(left, g_cvars.woodenSwordColor, "Wooden Sword Color"); - add_cosmetic_option(left, g_cvars.msBladeColor, "Master Sword Blade Color"); - add_cosmetic_option(left, g_cvars.msHandleColor, "Master Sword Handle Color"); - add_cosmetic_option(left, g_cvars.lightSwordGlowColor, "Light Sword Glow Color"); - add_cosmetic_option(left, g_cvars.boomerangColor, "Boomerang Color"); - add_cosmetic_option(left, g_cvars.ironBootsColor, "Iron Boots Color"); - add_cosmetic_option(left, g_cvars.spinnerColor, "Spinner Color"); + add_cosmetic_option(left, g_cvars.herosTunicCapColor, "Hero's Tunic Cap Color", kDefaultHexColorsExplanation); + add_cosmetic_option(left, g_cvars.herosTunicTorsoColor, "Hero's Tunic Body Color", kDefaultHexColorsExplanation); + add_cosmetic_option(left, g_cvars.herosTunicSkirtColor, "Hero's Tunic Skirt Color", kDefaultHexColorsExplanation); + add_cosmetic_option(left, g_cvars.zoraArmorCapColor, "Zora Armor Cap Color", kDefaultHexColorsExplanation); + add_cosmetic_option(left, g_cvars.zoraArmorHelmetColor, "Zora Armor Helmet Color", kDefaultHexColorsExplanation); + add_cosmetic_option(left, g_cvars.zoraArmorTorsoColor, "Zora Armor Torso Color", kDefaultHexColorsExplanation); + add_cosmetic_option(left, g_cvars.zoraArmorScalesColor, "Zora Armor Scales Color", kDefaultHexColorsExplanation); + add_cosmetic_option(left, g_cvars.zoraArmorFlippersColor, "Zora Armor Flippers Color", kDefaultHexColorsExplanation); + add_cosmetic_option_with_rainbow(left, g_cvars.lanternGlowColor, "Lantern Glow Color", kGlowColorsExplanation); + add_cosmetic_option(left, g_cvars.woodenSwordColor, "Wooden Sword Color", kDefaultHexColorsExplanation); + add_cosmetic_option(left, g_cvars.msBladeColor, "Master Sword Blade Color", kMasterSwordColorsExplanation); + add_cosmetic_option(left, g_cvars.msHandleColor, "Master Sword Handle Color", kMasterSwordColorsExplanation); + add_cosmetic_option_with_rainbow(left, g_cvars.lightSwordGlowColor, "Light Sword Glow Color", kGlowColorsExplanation); + add_cosmetic_option(left, g_cvars.boomerangColor, "Boomerang Color", kDefaultHexColorsExplanation); + add_cosmetic_option(left, g_cvars.ironBootsColor, "Iron Boots Color", kDefaultHexColorsExplanation); + add_cosmetic_option(left, g_cvars.spinnerColor, "Spinner Color", kDefaultHexColorsExplanation); + + return MOD_OK; +} + +ModResult build_ui_colors_tab( + ModContext*, UiWindowHandle, UiElementHandle left, UiElementHandle right, void*, ModError*) { + (void)right; + + add_cosmetic_option(left, g_cvars.heartColor, "Heart Color", kDefaultHexColorsExplanation); return MOD_OK; } @@ -96,12 +200,12 @@ ModResult build_misc_colors_tab( ModContext*, UiWindowHandle, UiElementHandle left, UiElementHandle right, void*, ModError*) { (void)right; - add_cosmetic_option(left, g_cvars.midnaHairBaseColor, "Midna's Hair Base Color"); - add_cosmetic_option(left, g_cvars.midnaHairTipsColor, "Midna's Hair Tips Color"); - add_cosmetic_option(left, g_cvars.midnaChargeRingColor, "Midna Charge Ring Color"); - add_cosmetic_option(left, g_cvars.linkHairColor, "Link's Hair Color"); - add_cosmetic_option(left, g_cvars.wolfLinkColor, "Wolf Link Color"); - add_cosmetic_option(left, g_cvars.eponaColor, "Epona Color"); + add_midna_hair_option(left, g_cvars.midnaHairBaseColor, "Midna's Hair Base Color"); + add_midna_hair_option(left, g_cvars.midnaHairTipsColor, "Midna's Hair Tips Color"); + add_cosmetic_option(left, g_cvars.midnaChargeRingColor, "Midna Charge Ring Color", kChargeRingColorsExplanation); + add_cosmetic_option(left, g_cvars.linkHairColor, "Link's Hair Color", kDefaultHexColorsExplanation); + add_cosmetic_option(left, g_cvars.wolfLinkColor, "Wolf Link Color", kDefaultHexColorsExplanation); + add_cosmetic_option(left, g_cvars.eponaColor, "Epona Color", kDefaultHexColorsExplanation); return MOD_OK; } @@ -114,11 +218,13 @@ void on_open_cosmetics_menu(ModContext*, void*) { if (g_cosmeticsWindow != 0) { return; } - UiTabDesc tabs[] = {UI_TAB_DESC_INIT, UI_TAB_DESC_INIT}; + UiTabDesc tabs[] = {UI_TAB_DESC_INIT, UI_TAB_DESC_INIT, UI_TAB_DESC_INIT}; tabs[0].title = "Equipment Colors"; tabs[0].build = build_equipment_colors_tab; - tabs[1].title = "Misc Colors"; - tabs[1].build = build_misc_colors_tab; + tabs[1].title = "UI Colors"; + tabs[1].build = build_ui_colors_tab; + tabs[2].title = "Misc Colors"; + tabs[2].build = build_misc_colors_tab; UiWindowDesc desc = UI_WINDOW_DESC_INIT; desc.tabs = tabs; desc.tab_count = ARRAY_SIZE(tabs); @@ -139,15 +245,17 @@ ModResult build_panel(ModContext*, UiElementHandle panel, void*, ModError*) { } #define REGISTER_COSMETIC_OPTION(option) \ - auto option##Result = register_str_option(#option, NULL, g_cvars.option, error); \ - if (option##Result != MOD_OK) { \ - return option##Result; \ + result = register_str_option(#option, NULL, g_cvars.option, error); \ + if (result != MOD_OK) { \ + return result; \ } \ extern "C" { MOD_EXPORT ModResult mod_initialize(ModError* error) { svc_log->info(mod_ctx, "basic_cosmetics_mod initialized"); + ModResult result{}; + REGISTER_COSMETIC_OPTION(herosTunicCapColor) REGISTER_COSMETIC_OPTION(herosTunicTorsoColor) REGISTER_COSMETIC_OPTION(herosTunicSkirtColor) @@ -164,8 +272,18 @@ MOD_EXPORT ModResult mod_initialize(ModError* error) { REGISTER_COSMETIC_OPTION(boomerangColor) REGISTER_COSMETIC_OPTION(ironBootsColor) REGISTER_COSMETIC_OPTION(spinnerColor) - REGISTER_COSMETIC_OPTION(midnaHairBaseColor) - REGISTER_COSMETIC_OPTION(midnaHairTipsColor) + REGISTER_COSMETIC_OPTION(heartColor) + + result = register_int_option("midnaHairBaseColor", 0, g_cvars.midnaHairBaseColor, error); + if (result != MOD_OK) { + return result; + } + + result = register_int_option("midnaHairTipsColor", 0, g_cvars.midnaHairTipsColor, error); + if (result != MOD_OK) { + return result; + } + REGISTER_COSMETIC_OPTION(midnaChargeRingColor) REGISTER_COSMETIC_OPTION(linkHairColor) REGISTER_COSMETIC_OPTION(wolfLinkColor) @@ -176,7 +294,7 @@ MOD_EXPORT ModResult mod_initialize(ModError* error) { svc_ui->register_mods_panel(mod_ctx, &panelDesc); // Add all our hooks - auto result = add_all_hooks(); + result = add_all_hooks(); if (result != MOD_OK) { return result; } @@ -185,6 +303,8 @@ MOD_EXPORT ModResult mod_initialize(ModError* error) { } MOD_EXPORT ModResult mod_update(ModError*) { + update_rainbow_rgb(1.0f); + set_all_midna_hair_colors(); return MOD_OK; } diff --git a/mods/basic_cosmetics_mod/src/mod.hpp b/mods/basic_cosmetics_mod/src/mod.hpp index 5ec8366b72..aa184d7a1f 100644 --- a/mods/basic_cosmetics_mod/src/mod.hpp +++ b/mods/basic_cosmetics_mod/src/mod.hpp @@ -21,6 +21,7 @@ struct cvars { ConfigVarHandle boomerangColor = 0; ConfigVarHandle ironBootsColor = 0; ConfigVarHandle spinnerColor = 0; + ConfigVarHandle heartColor = 0; ConfigVarHandle midnaHairBaseColor = 0; ConfigVarHandle midnaHairTipsColor = 0; ConfigVarHandle midnaChargeRingColor = 0; @@ -31,4 +32,6 @@ struct cvars { cvars& get_cvars(); -std::string get_str_option(ConfigVarHandle handle, const std::string& fallback); \ No newline at end of file +std::string get_str_option(ConfigVarHandle handle, const std::string& fallback); + +int64_t get_int_option(ConfigVarHandle handle, int64_t fallback); \ No newline at end of file diff --git a/mods/basic_cosmetics_mod/src/texture_utils.cpp b/mods/basic_cosmetics_mod/src/texture_utils.cpp index 540058f014..9dd8221129 100644 --- a/mods/basic_cosmetics_mod/src/texture_utils.cpp +++ b/mods/basic_cosmetics_mod/src/texture_utils.cpp @@ -13,7 +13,6 @@ class J3DVertexData; #include "JSystem/JUtility/JUTNameTab.h" #include "JSystem/JUtility/JUTTexture.h" #include "d/actor/d_a_alink.h" -#include "d/actor/d_a_player.h" #include "global.h" #include "gx/GXEnum.h" #include "m_Do/m_Do_dvd_thread.h"