From 26cebd3fb827ed67ff1060c42c4a607c514f9732 Mon Sep 17 00:00:00 2001 From: coah <100182500+coah80@users.noreply.github.com> Date: Mon, 22 Dec 2025 14:47:15 -0600 Subject: [PATCH 1/3] Updated geode version and added ability to change songs in main menu levels Added a QOL change to be able to change the songs on the main levels. --- jukebox/jukebox/hooks/custom_song_widget.cpp | 201 +++++++++++++++++++ mod.json | 2 +- 2 files changed, 202 insertions(+), 1 deletion(-) diff --git a/jukebox/jukebox/hooks/custom_song_widget.cpp b/jukebox/jukebox/hooks/custom_song_widget.cpp index 0f4dd55..2884e9b 100644 --- a/jukebox/jukebox/hooks/custom_song_widget.cpp +++ b/jukebox/jukebox/hooks/custom_song_widget.cpp @@ -17,12 +17,17 @@ #include #include #include +#include +#include +#include #include +#include #include #include #include #include // IWYU pragma: keep #include // IWYU pragma: keep +#include // IWYU pragma: keep #include #include #include @@ -36,6 +41,52 @@ using namespace geode::prelude; using namespace jukebox; +static CCMenuItemSpriteExtra* jbFindTopRightMenuItem(CCMenu* menu) { + if (!menu) { + return nullptr; + } + auto children = menu->getChildren(); + if (!children) { + return nullptr; + } + + CCMenuItemSpriteExtra* best = nullptr; + float bestX = -1e9f; + float bestY = -1e9f; + + CCObject* obj = nullptr; + CCARRAY_FOREACH(children, obj) { + auto item = typeinfo_cast(obj); + if (!item) { + continue; + } + auto p = item->getPosition(); + if (p.x > bestX || (p.x == bestX && p.y > bestY)) { + best = item; + bestX = p.x; + bestY = p.y; + } + } + + return best; +} + +static void jbCollectMenuItems(CCNode* node, + std::vector& out) { + if (!node) { + return; + } + if (auto item = typeinfo_cast(node)) { + out.push_back(item); + } + if (auto children = node->getChildren()) { + CCObject* obj = nullptr; + CCARRAY_FOREACH(children, obj) { + jbCollectMenuItems(typeinfo_cast(obj), out); + } + } +} + class $modify(JBSongWidget, CustomSongWidget) { struct Fields { Nongs* nongs = nullptr; @@ -662,3 +713,153 @@ class $modify(JBLevelInfoLayer, LevelInfoLayer) { return true; } }; + +class $modify(JBLevelSelectLayer, LevelSelectLayer) { + struct Fields { + CCMenuItemSpriteExtra* m_jbBtn = nullptr; + }; + + void onJukebox(CCObject*) { + if (!m_scrollLayer) { + return; + } + + // Don't guess with page math; use the actual LevelPage -> GJGameLevel + // the user is currently viewing. + GJGameLevel* level = nullptr; + if (auto pageLayer = m_scrollLayer->getPage(m_scrollLayer->m_page)) { + if (auto page = typeinfo_cast(pageLayer)) { + level = page->m_level; + } + } + if (!level && m_scrollLayer->m_page > 0) { + if (auto pageLayer = m_scrollLayer->getPage(m_scrollLayer->m_page - 1)) { + if (auto page = typeinfo_cast(pageLayer)) { + level = page->m_level; + } + } + } + if (!level) { + return; + } + + const bool isRobtopSong = level->m_songID == 0; + const int rawSongID = isRobtopSong ? level->m_audioTrack : level->m_songID; + const int adjustedID = NongManager::get().adjustSongID(rawSongID, isRobtopSong); + if (adjustedID == 0) { + return; + } + + if (!NongManager::get().hasSongID(adjustedID)) { + auto title = LevelTools::getAudioTitle(rawSongID); + auto audioString = std::string(LevelTools::getAudioString(rawSongID)); + + std::string artist = "RobTop"; + if (auto dash = audioString.find(" - "); dash != std::string::npos) { + artist = audioString.substr(0, dash); + } + + auto obj = SongInfoObject::create(rawSongID, title, artist, 0, 0.0f, + "", "", "", 0, "", false, 0, 0); + if (!obj) { + return; + } + + auto res = NongManager::get().initSongID(obj, rawSongID, isRobtopSong); + if (res.isErr()) { + FLAlertLayer::create("Error", res.unwrapErr(), "Ok")->show(); + return; + } + } + + auto layer = NongDropdownLayer::create({adjustedID}, nullptr, adjustedID, std::nullopt); + if (!layer) { + FLAlertLayer::create("Error", "Failed to open Jukebox popup", "Ok") + ->show(); + return; + } + layer->setZOrder(106); + layer->show(); + } + + void ensureJukeboxButton() { + if (m_fields->m_jbBtn && m_fields->m_jbBtn->getParent()) { + return; + } + m_fields->m_jbBtn = nullptr; + + auto win = CCDirector::sharedDirector()->getWinSize(); + + std::vector items; + items.reserve(32); + jbCollectMenuItems(this, items); + + CCMenuItemSpriteExtra* infoBtn = nullptr; + CCMenu* infoMenu = nullptr; + CCPoint best = {-1e9f, -1e9f}; + + for (auto* item : items) { + if (!item || item->getID() == "jb-levelselect-button"_spr) { + continue; + } + auto parentMenu = typeinfo_cast(item->getParent()); + if (!parentMenu) { + continue; + } + + auto world = parentMenu->convertToWorldSpace(item->getPosition()); + if (world.x < win.width * 0.70f || world.y < win.height * 0.70f) { + continue; + } + + if (world.x > best.x || (world.x == best.x && world.y > best.y)) { + best = world; + infoBtn = item; + infoMenu = parentMenu; + } + } + + if (!infoBtn || !infoMenu) { + return; + } + + auto spr = CCSprite::createWithSpriteFrameName("JB_PinDisc.png"_spr); + if (!spr) { + spr = CCSprite::create("JB_PinDisc.png"_spr); + } + if (!spr) { + auto path = (Mod::get()->getResourcesDir() / "JB_PinDisc.png").string(); + spr = CCSprite::create(path.c_str()); + } + if (!spr) { + return; + } + spr->setScale(0.65f); + + auto btn = CCMenuItemSpriteExtra::create( + spr, this, menu_selector(JBLevelSelectLayer::onJukebox)); + btn->setID("jb-levelselect-button"_spr); + + constexpr float gap = 6.f; + auto pos = infoBtn->getPosition(); + float x = pos.x - infoBtn->getScaledContentSize().width / 2 - + btn->getScaledContentSize().width / 2 - gap; + btn->setPosition({x, pos.y}); + + infoMenu->addChild(btn, infoBtn->getZOrder()); + m_fields->m_jbBtn = btn; + } + + bool init(int page) { + if (!LevelSelectLayer::init(page)) { + return false; + } + this->ensureJukeboxButton(); + return true; + } + + void scrollLayerMoved(cocos2d::CCPoint position) { + LevelSelectLayer::scrollLayerMoved(position); + this->ensureJukeboxButton(); + } +}; diff --git a/mod.json b/mod.json index bfd1c38..ac89f5f 100644 --- a/mod.json +++ b/mod.json @@ -1,5 +1,5 @@ { - "geode": "4.6.2", + "geode": "4.10.0", "version": "3.4.0", "id": "fleym.nongd", "name": "Jukebox", From bad1a354b1196f0e0cb0e41b7af3816289c59e7a Mon Sep 17 00:00:00 2001 From: coah <100182500+coah80@users.noreply.github.com> Date: Fri, 9 Jan 2026 11:13:36 -0600 Subject: [PATCH 2/3] okay --- jukebox/jukebox/hooks/custom_song_widget.cpp | 198 ------------------- jukebox/jukebox/hooks/level_select_layer.cpp | 139 +++++++++++++ 2 files changed, 139 insertions(+), 198 deletions(-) create mode 100644 jukebox/jukebox/hooks/level_select_layer.cpp diff --git a/jukebox/jukebox/hooks/custom_song_widget.cpp b/jukebox/jukebox/hooks/custom_song_widget.cpp index 2884e9b..4e67911 100644 --- a/jukebox/jukebox/hooks/custom_song_widget.cpp +++ b/jukebox/jukebox/hooks/custom_song_widget.cpp @@ -18,16 +18,13 @@ #include #include #include -#include #include #include -#include #include #include #include #include // IWYU pragma: keep #include // IWYU pragma: keep -#include // IWYU pragma: keep #include #include #include @@ -41,52 +38,6 @@ using namespace geode::prelude; using namespace jukebox; -static CCMenuItemSpriteExtra* jbFindTopRightMenuItem(CCMenu* menu) { - if (!menu) { - return nullptr; - } - auto children = menu->getChildren(); - if (!children) { - return nullptr; - } - - CCMenuItemSpriteExtra* best = nullptr; - float bestX = -1e9f; - float bestY = -1e9f; - - CCObject* obj = nullptr; - CCARRAY_FOREACH(children, obj) { - auto item = typeinfo_cast(obj); - if (!item) { - continue; - } - auto p = item->getPosition(); - if (p.x > bestX || (p.x == bestX && p.y > bestY)) { - best = item; - bestX = p.x; - bestY = p.y; - } - } - - return best; -} - -static void jbCollectMenuItems(CCNode* node, - std::vector& out) { - if (!node) { - return; - } - if (auto item = typeinfo_cast(node)) { - out.push_back(item); - } - if (auto children = node->getChildren()) { - CCObject* obj = nullptr; - CCARRAY_FOREACH(children, obj) { - jbCollectMenuItems(typeinfo_cast(obj), out); - } - } -} - class $modify(JBSongWidget, CustomSongWidget) { struct Fields { Nongs* nongs = nullptr; @@ -714,152 +665,3 @@ class $modify(JBLevelInfoLayer, LevelInfoLayer) { } }; -class $modify(JBLevelSelectLayer, LevelSelectLayer) { - struct Fields { - CCMenuItemSpriteExtra* m_jbBtn = nullptr; - }; - - void onJukebox(CCObject*) { - if (!m_scrollLayer) { - return; - } - - // Don't guess with page math; use the actual LevelPage -> GJGameLevel - // the user is currently viewing. - GJGameLevel* level = nullptr; - if (auto pageLayer = m_scrollLayer->getPage(m_scrollLayer->m_page)) { - if (auto page = typeinfo_cast(pageLayer)) { - level = page->m_level; - } - } - if (!level && m_scrollLayer->m_page > 0) { - if (auto pageLayer = m_scrollLayer->getPage(m_scrollLayer->m_page - 1)) { - if (auto page = typeinfo_cast(pageLayer)) { - level = page->m_level; - } - } - } - if (!level) { - return; - } - - const bool isRobtopSong = level->m_songID == 0; - const int rawSongID = isRobtopSong ? level->m_audioTrack : level->m_songID; - const int adjustedID = NongManager::get().adjustSongID(rawSongID, isRobtopSong); - if (adjustedID == 0) { - return; - } - - if (!NongManager::get().hasSongID(adjustedID)) { - auto title = LevelTools::getAudioTitle(rawSongID); - auto audioString = std::string(LevelTools::getAudioString(rawSongID)); - - std::string artist = "RobTop"; - if (auto dash = audioString.find(" - "); dash != std::string::npos) { - artist = audioString.substr(0, dash); - } - - auto obj = SongInfoObject::create(rawSongID, title, artist, 0, 0.0f, - "", "", "", 0, "", false, 0, 0); - if (!obj) { - return; - } - - auto res = NongManager::get().initSongID(obj, rawSongID, isRobtopSong); - if (res.isErr()) { - FLAlertLayer::create("Error", res.unwrapErr(), "Ok")->show(); - return; - } - } - - auto layer = NongDropdownLayer::create({adjustedID}, nullptr, adjustedID, std::nullopt); - if (!layer) { - FLAlertLayer::create("Error", "Failed to open Jukebox popup", "Ok") - ->show(); - return; - } - layer->setZOrder(106); - layer->show(); - } - - void ensureJukeboxButton() { - if (m_fields->m_jbBtn && m_fields->m_jbBtn->getParent()) { - return; - } - m_fields->m_jbBtn = nullptr; - - auto win = CCDirector::sharedDirector()->getWinSize(); - - std::vector items; - items.reserve(32); - jbCollectMenuItems(this, items); - - CCMenuItemSpriteExtra* infoBtn = nullptr; - CCMenu* infoMenu = nullptr; - CCPoint best = {-1e9f, -1e9f}; - - for (auto* item : items) { - if (!item || item->getID() == "jb-levelselect-button"_spr) { - continue; - } - auto parentMenu = typeinfo_cast(item->getParent()); - if (!parentMenu) { - continue; - } - - auto world = parentMenu->convertToWorldSpace(item->getPosition()); - if (world.x < win.width * 0.70f || world.y < win.height * 0.70f) { - continue; - } - - if (world.x > best.x || (world.x == best.x && world.y > best.y)) { - best = world; - infoBtn = item; - infoMenu = parentMenu; - } - } - - if (!infoBtn || !infoMenu) { - return; - } - - auto spr = CCSprite::createWithSpriteFrameName("JB_PinDisc.png"_spr); - if (!spr) { - spr = CCSprite::create("JB_PinDisc.png"_spr); - } - if (!spr) { - auto path = (Mod::get()->getResourcesDir() / "JB_PinDisc.png").string(); - spr = CCSprite::create(path.c_str()); - } - if (!spr) { - return; - } - spr->setScale(0.65f); - - auto btn = CCMenuItemSpriteExtra::create( - spr, this, menu_selector(JBLevelSelectLayer::onJukebox)); - btn->setID("jb-levelselect-button"_spr); - - constexpr float gap = 6.f; - auto pos = infoBtn->getPosition(); - float x = pos.x - infoBtn->getScaledContentSize().width / 2 - - btn->getScaledContentSize().width / 2 - gap; - btn->setPosition({x, pos.y}); - - infoMenu->addChild(btn, infoBtn->getZOrder()); - m_fields->m_jbBtn = btn; - } - - bool init(int page) { - if (!LevelSelectLayer::init(page)) { - return false; - } - this->ensureJukeboxButton(); - return true; - } - - void scrollLayerMoved(cocos2d::CCPoint position) { - LevelSelectLayer::scrollLayerMoved(position); - this->ensureJukeboxButton(); - } -}; diff --git a/jukebox/jukebox/hooks/level_select_layer.cpp b/jukebox/jukebox/hooks/level_select_layer.cpp new file mode 100644 index 0000000..bef9813 --- /dev/null +++ b/jukebox/jukebox/hooks/level_select_layer.cpp @@ -0,0 +1,139 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +using namespace geode::prelude; +using namespace jukebox; + +class $modify(JBLevelSelectLayer, LevelSelectLayer) { + struct Fields { + CCMenuItemSpriteExtra* m_jbBtn = nullptr; + }; + + void onJukebox(CCObject*) { + if (!m_scrollLayer) { + return; + } + + GJGameLevel* level = nullptr; + if (auto pageLayer = m_scrollLayer->getPage(m_scrollLayer->m_page)) { + if (auto page = typeinfo_cast(pageLayer)) { + level = page->m_level; + } + } + if (!level && m_scrollLayer->m_page > 0) { + if (auto pageLayer = m_scrollLayer->getPage(m_scrollLayer->m_page - 1)) { + if (auto page = typeinfo_cast(pageLayer)) { + level = page->m_level; + } + } + } + if (!level) { + return; + } + + const bool isRobtopSong = level->m_songID == 0; + const int rawSongID = isRobtopSong ? level->m_audioTrack : level->m_songID; + const int adjustedID = NongManager::get().adjustSongID(rawSongID, isRobtopSong); + if (adjustedID == 0) { + return; + } + + if (!NongManager::get().hasSongID(adjustedID)) { + auto title = LevelTools::getAudioTitle(rawSongID); + int artistID = LevelTools::artistForAudio(rawSongID); + std::string artist = LevelTools::nameForArtist(artistID); + + if (rawSongID == 21) { + artist = "MDK"; + } + + auto obj = SongInfoObject::create(rawSongID, title, artist, artistID, 0.0f, + "", "", "", 0, "", false, 0, 0); + if (!obj) { + return; + } + + auto res = NongManager::get().initSongID(obj, rawSongID, isRobtopSong); + if (res.isErr()) { + FLAlertLayer::create("Error", res.unwrapErr(), "Ok")->show(); + return; + } + } + + auto layer = NongDropdownLayer::create({adjustedID}, nullptr, adjustedID, std::nullopt); + if (!layer) { + FLAlertLayer::create("Error", "Failed to open Jukebox popup", "Ok") + ->show(); + return; + } + layer->setZOrder(106); + layer->show(); + } + + void ensureJukeboxButton() { + if (m_fields->m_jbBtn && m_fields->m_jbBtn->getParent()) { + return; + } + m_fields->m_jbBtn = nullptr; + + auto infoMenu = this->getChildByIDRecursive("info-menu"); + if (!infoMenu) { + return; + } + + auto menu = typeinfo_cast(infoMenu); + if (!menu) { + return; + } + + auto infoBtn = menu->getChildByID("info-button"); + if (!infoBtn) { + return; + } + + auto spr = CCSprite::createWithSpriteFrameName("JB_PinDisc.png"_spr); + if (!spr) { + return; + } + spr->setScale(0.65f); + spr->setID("nong-pin"_spr); + + auto btn = CCMenuItemSpriteExtra::create( + spr, this, menu_selector(JBLevelSelectLayer::onJukebox)); + btn->setID("nong-button"_spr); + + constexpr float gap = 6.f; + auto pos = infoBtn->getPosition(); + float infoBtnWidth = infoBtn->getScaledContentSize().width; + float x = pos.x - infoBtnWidth / 2.f - btn->getScaledContentSize().width / 2.f - gap; + btn->setPosition({x, pos.y}); + + menu->addChild(btn, infoBtn->getZOrder()); + m_fields->m_jbBtn = btn; + } + + bool init(int page) { + if (!LevelSelectLayer::init(page)) { + return false; + } + this->ensureJukeboxButton(); + return true; + } + + void scrollLayerMoved(cocos2d::CCPoint position) { + LevelSelectLayer::scrollLayerMoved(position); + this->ensureJukeboxButton(); + } +}; + From a1b8c7e6b713538b7a5fe1071e6bffb87b6716ea Mon Sep 17 00:00:00 2001 From: coah <100182500+coah80@users.noreply.github.com> Date: Fri, 9 Jan 2026 11:18:18 -0600 Subject: [PATCH 3/3] Update level_select_layer.cpp --- jukebox/jukebox/hooks/level_select_layer.cpp | 4 ---- 1 file changed, 4 deletions(-) diff --git a/jukebox/jukebox/hooks/level_select_layer.cpp b/jukebox/jukebox/hooks/level_select_layer.cpp index bef9813..33ea277 100644 --- a/jukebox/jukebox/hooks/level_select_layer.cpp +++ b/jukebox/jukebox/hooks/level_select_layer.cpp @@ -53,10 +53,6 @@ class $modify(JBLevelSelectLayer, LevelSelectLayer) { auto title = LevelTools::getAudioTitle(rawSongID); int artistID = LevelTools::artistForAudio(rawSongID); std::string artist = LevelTools::nameForArtist(artistID); - - if (rawSongID == 21) { - artist = "MDK"; - } auto obj = SongInfoObject::create(rawSongID, title, artist, artistID, 0.0f, "", "", "", 0, "", false, 0, 0);